# Comprehensive Guide for LLMs: Writing Deployer Configurations
This guide provides structured instructions for Large Language Models to generate accurate and effective Deployer configurations. It covers common patterns, best practices, and complete examples.
## Configuration File Structure
Every Deployer project configuration (`.depl/config.yaml`) follows this structure:
```yaml
project_name: string # Required: Project identifier
version: 8 # Required: Config version (currently 8)
ignore_files: [] # Optional: Files to exclude from sync
cache_files: [] # Optional: Files to preserve between builds
variables: {} # Optional: Project variables
actions: [] # Optional: Action definitions
pipelines: [] # Required: At least one pipeline
```
## Key Principles
1. **Start Simple**: Begin with minimal configuration and add complexity as needed
2. **Reuse Actions**: Define actions once, use them in multiple pipelines
3. **Choose the Right Driver**: Use `shell` for CI/CD and containers, `deployer` for full features
4. **Cache Strategically**: Define cache files to speed up subsequent builds
5. **Use Variables**: Parameterize repeated values and sensitive data
## Step-by-Step Configuration Creation
### Step 1: Define Project Metadata
```yaml
project_name: my-app # Short, lowercase, hyphen-separated
version: 8 # Always use version 8
ignore_files: # Common ignore patterns
- .git
- target # Rust build output
- node_modules # Node.js dependencies
- __pycache__ # Python cache
cache_files: # Preserve between builds
- target # Rust: cargo build output
- node_modules # Node.js: npm packages
- go.sum # Go: dependency checksums
- Cargo.lock # Rust: dependency lock
```
### Step 2: Define Variables
Variables store values that change between environments or are reused across actions.
**Plain values** (constants):
```yaml
variables:
artifact-name:
value:
type: plain
value: my-app-binary
```
**Environment variables** (read from shell):
```yaml
variables:
api-token:
is_secret: true
value:
type: from_env_var
var_name: MY_API_TOKEN
```
**From environment files** (.env):
```yaml
variables:
database-url:
is_secret: true
value:
type: from_env_file
env_file_path: .env
key: DATABASE_URL
```
**From commands** (dynamic):
```yaml
variables:
git-commit:
value:
type: from_cmd
cmd: git rev-parse --short HEAD
```
**HashiCorp Vault KV2**:
```yaml
variables:
api-secret:
is_secret: true
value:
type: from_hc_vault_kv2
mount_path: secret
secret_path: api/token
```
### Step 3: Define Actions
Actions are reusable building blocks. Each action should do one thing well.
**Basic staged action** (build stage):
```yaml
actions:
- info: cargo-build@0.1.0
requirements:
- type: in_path
executable: cargo
desc: "Install Rust: curl https://sh.rustup.rs -sSf | sh"
action:
type: staged
stage: build # build or deploy
cmd: cargo build --release
show_success_output: true # Show output even on success
```
**Action with placeholders**:
```yaml
actions:
- info: compress@0.1.0
requirements:
- type: in_path
executable: upx
desc: "Install upx from package manager"
action:
type: staged
stage: build
cmd: upx {binary}
placeholders:
- "{binary}" # Will be replaced by variable
```
**Custom action** (no stage):
```yaml
actions:
- info: deploy-to-server@0.1.0
action:
type: custom
cmd: scp target/release/app server:/opt/app/
show_success_output: true
```
**Observe action** (interactive, no I/O redirection):
```yaml
actions:
- info: run-server@0.1.0
action:
type: observe
cmd: ./target/release/server
ignore_fails: true # Don't fail pipeline on Ctrl+C
```
**Test action** (validation):
```yaml
actions:
- info: verify-version@0.1.0
action:
type: test
cmd: ./target/release/app --version
success_when_found: "v1.0.0" # Regex to find
# success_when_not_found: "error" # Optional: regex to avoid
```
### Step 4: Create Pipelines
Pipelines orchestrate actions in a specific order.
**Simple pipeline**:
```yaml
pipelines:
- title: build # Simple name for CLI use
info: my-app-build@0.1.0 # Unique identifier
default: true # Run when no pipeline specified
driver: deployer # or 'shell' for portability
actions:
- title: Build app # Human-readable description
used: cargo-build@0.1.0
artifacts:
- from: target/release/app # Source in build folder
to: app # Destination in artifacts/
```
**Pipeline with variable substitution**:
```yaml
pipelines:
- title: install
info: my-app-install@0.1.0
driver: deployer
actions:
- title: Build
used: cargo-build@0.1.0
- title: Compress
used: compress@0.1.0
with:
"{binary}": artifact-name # Maps placeholder to variable
- title: Install
used: install@0.1.0
with:
"{binary}": artifact-name
artifacts:
- from: target/release/app
to: app
```
**Pipeline with exclusive execution tag** (for parallel incompatible builds):
```yaml
pipelines:
- title: build-x86
info: my-app-build-x86@0.1.0
exclusive_exec_tag: x86 # Separate cache folder
driver: shell
actions:
- title: Build for x86_64
used: cargo-build-x86@0.1.0
artifacts:
- from: target/x86_64-unknown-linux-gnu/release/app
to: app-x86
- title: build-arm
info: my-app-build-arm@0.1.0
exclusive_exec_tag: arm # Different cache folder
driver: shell
actions:
- title: Build for ARM
used: cargo-build-arm@0.1.0
artifacts:
- from: target/aarch64-unknown-linux-gnu/release/app
to: app-arm
```
### Step 5: Add Container Support (Optional)
For building in Docker/Podman containers:
**Basic containerized pipeline**:
```yaml
pipelines:
- title: ci-docker
info: my-app-ci-docker@0.1.0
driver: shell # Avoid building Deployer in container
exclusive_exec_tag: docker
actions:
- title: Build
used: cargo-build@0.1.0
artifacts:
- from: target/release/app
to: app
containered_opts:
base_image: rust:1.85
preflight_cmds:
- RUN apt-get update && apt-get install -y build-essential
```
**With cache strategies** (multi-stage build):
```yaml
pipelines:
- title: ci-docker-cached
info: my-app-ci-docker-cached@0.1.0
driver: shell
exclusive_exec_tag: docker-cached
actions:
- title: Build
used: cargo-build@0.1.0
artifacts:
- from: target/release/app
to: app
containered_opts:
base_image: rust:1.85
preflight_cmds:
- RUN apt-get update && apt-get install -y build-essential
cache_strategies:
# Stage 1: Cache dependencies
- copy_cmds:
- COPY Cargo.toml Cargo.lock ./
pre_cache_cmds:
- RUN cargo fetch
# Stage 2: Build with cached dependencies
- copy_cmds:
- COPY src/ src/
pre_cache_cmds:
- DEPL # Run the pipeline
use_containerd_local_storage_cache: true
prevent_metadata_loading: false
```
**With port bindings** (for services):
```yaml
containered_opts:
base_image: rust:1.85
port_bindings:
- from: 3000 # Container port
to: 3000 # Host port
cache_strategies:
# ... (as above)
```
### Step 6: Add CI/CD Integration (Optional)
**GitHub Actions**:
```yaml
pipelines:
- title: ci
info: my-app-ci@0.1.0
driver: shell
actions:
- title: Build
used: cargo-build@0.1.0
gh_opts:
on_push_branches:
- main
- develop
on_pull_requests_branches:
- main
preflight_steps:
- name: Install Rust
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
```
**GitLab CI**:
```yaml
pipelines:
- title: ci
info: my-app-ci@0.1.0
driver: shell
actions:
- title: Build
used: cargo-build@0.1.0
gl_opts:
rules:
- $CI_COMMIT_BRANCH == "main"
- $CI_PIPELINE_SOURCE == "merge_request_event"
base_image: rust:1.85
```
**Systemd service**:
```yaml
pipelines:
- title: run-server
info: my-app-run@0.1.0
driver: deployer
actions:
- title: Start server
used: run-server@0.1.0
systemd_opts:
user:
type: current_user
after:
- network.target
use_defined_shell: true
```
## Complete Examples
### Example 1: Rust CLI Application
```yaml
project_name: rust-cli-tool
version: 8
ignore_files:
- .git
- target
cache_files:
- target
- Cargo.lock
variables:
binary-path:
value:
type: plain
value: target/release/my-tool
actions:
- info: cargo-fmt@0.1.0
requirements:
- type: in_path
executable: cargo
desc: "Install Rust: curl https://sh.rustup.rs -sSf | sh"
action:
type: staged
stage: build
cmd: cargo fmt --check
- info: cargo-clippy@0.1.0
requirements:
- type: in_path
executable: cargo
action:
type: staged
stage: build
cmd: cargo clippy -- -D warnings
show_success_output: true
- info: cargo-test@0.1.0
requirements:
- type: in_path
executable: cargo
action:
type: staged
stage: build
cmd: cargo test
show_success_output: true
- info: cargo-build@0.1.0
requirements:
- type: in_path
executable: cargo
action:
type: staged
stage: build
cmd: cargo build --release
- info: upx-compress@0.1.0
requirements:
- type: in_path
executable: upx
desc: "Install upx from your package manager"
action:
type: staged
stage: build
cmd: upx {binary}
placeholders:
- "{binary}"
- info: install-to-cargo@0.1.0
action:
type: staged
stage: build
cmd: cp -f {binary} ~/.cargo/bin/
placeholders:
- "{binary}"
pipelines:
- title: install
info: rust-cli-install@0.1.0
default: true
driver: deployer
actions:
- title: Check formatting
used: cargo-fmt@0.1.0
- title: Lint
used: cargo-clippy@0.1.0
- title: Run tests
used: cargo-test@0.1.0
- title: Build release
used: cargo-build@0.1.0
- title: Compress binary
used: upx-compress@0.1.0
with:
"{binary}": binary-path
- title: Install to ~/.cargo/bin
used: install-to-cargo@0.1.0
with:
"{binary}": binary-path
artifacts:
- from: target/release/my-tool
to: my-tool
- title: ci
info: rust-cli-ci@0.1.0
driver: shell
exclusive_exec_tag: ci
tags:
- ci
- github
actions:
- title: Check formatting
used: cargo-fmt@0.1.0
- title: Lint
used: cargo-clippy@0.1.0
- title: Run tests
used: cargo-test@0.1.0
- title: Build release
used: cargo-build@0.1.0
artifacts:
- from: target/release/my-tool
to: my-tool
gh_opts:
on_push_branches:
- main
on_pull_requests_branches:
- main
preflight_steps:
- name: Install Rust Toolchain
uses: actions-rust-lang/setup-rust-toolchain@v1
with:
toolchain: stable
```
### Example 2: Go Web Service
```yaml
project_name: go-web-service
version: 8
ignore_files:
- .git
cache_files:
- go.sum
- go.work
variables:
service-port:
value:
type: plain
value: "8080"
actions:
- info: go-fmt@0.1.0
requirements:
- type: in_path
executable: go
desc: "Install Go from https://go.dev/doc/install"
action:
type: staged
stage: build
cmd: go fmt ./...
- info: go-vet@0.1.0
requirements:
- type: in_path
executable: go
action:
type: staged
stage: build
cmd: go vet ./...
show_success_output: true
- info: go-test@0.1.0
requirements:
- type: in_path
executable: go
action:
type: staged
stage: build
cmd: go test ./...
show_success_output: true
- info: go-build@0.1.0
requirements:
- type: in_path
executable: go
action:
type: staged
stage: build
cmd: go build -o app ./cmd/server
show_success_output: true
- info: run-service@0.1.0
action:
type: observe
cmd: ./app
ignore_fails: true
pipelines:
- title: dev
info: go-service-dev@0.1.0
default: true
driver: deployer
actions:
- title: Format code
used: go-fmt@0.1.0
- title: Vet code
used: go-vet@0.1.0
- title: Run tests
used: go-test@0.1.0
- title: Build service
used: go-build@0.1.0
- title: Run service
used: run-service@0.1.0
artifacts:
- from: app
to: app
- title: docker
info: go-service-docker@0.1.0
driver: shell
exclusive_exec_tag: docker
tags:
- docker
actions:
- title: Format code
used: go-fmt@0.1.0
- title: Vet code
used: go-vet@0.1.0
- title: Run tests
used: go-test@0.1.0
- title: Build service
used: go-build@0.1.0
artifacts:
- from: app
to: app
containered_opts:
base_image: golang:1.23
preflight_cmds:
- RUN apt-get update && apt-get install -y git
port_bindings:
- from: 8080
to: 8080
cache_strategies:
- copy_cmds:
- COPY go.mod go.sum ./
pre_cache_cmds:
- RUN go mod download
- copy_cmds:
- COPY cmd/ cmd/
- COPY internal/ internal/
pre_cache_cmds:
- DEPL
use_containerd_local_storage_cache: true
```
### Example 3: Python Application
```yaml
project_name: python-api
version: 8
ignore_files:
- .git
- __pycache__
- .venv
- venv
cache_files:
- .venv
variables:
python-cmd:
value:
type: plain
value: python3
actions:
- info: create-venv@0.1.0
requirements:
- type: in_path
executable: python3
desc: "Install Python 3.9+"
action:
type: custom
cmd: "{python} -m venv .venv"
placeholders:
- "{python}"
only_when_fresh: true
- info: pip-install@0.1.0
action:
type: custom
cmd: .venv/bin/pip install -r requirements.txt
- info: pytest@0.1.0
requirements:
- type: exists
path: .venv/bin/pytest
desc: "pytest should be in requirements.txt"
action:
type: staged
stage: build
cmd: .venv/bin/pytest
show_success_output: true
- info: run-api@0.1.0
action:
type: observe
cmd: .venv/bin/python -m api.main
ignore_fails: true
pipelines:
- title: dev
info: python-api-dev@0.1.0
default: true
driver: deployer
actions:
- title: Create virtual environment
used: create-venv@0.1.0
with:
"{python}": python-cmd
- title: Install dependencies
used: pip-install@0.1.0
- title: Run tests
used: pytest@0.1.0
- title: Run API server
used: run-api@0.1.0
```
## Common Patterns and Best Practices
### Pattern 1: Multi-Environment Configuration
Use variables to differentiate between environments:
```yaml
variables:
env:
value:
type: from_env_var
var_name: APP_ENV
api-url:
value:
type: from_cmd
cmd: |
if [ "$APP_ENV" = "production" ]; then
echo "https://api.prod.example.com"
else
echo "https://api.dev.example.com"
fi
```
### Pattern 2: Conditional Execution
Use `only_when_fresh` for one-time setup:
```yaml
actions:
- info: setup-database@0.1.0
action:
type: custom
cmd: ./scripts/db-setup.sh
only_when_fresh: true
```
### Pattern 3: Parallel-Safe Builds
Use `exclusive_exec_tag` for incompatible builds:
```yaml
pipelines:
- title: build-linux
exclusive_exec_tag: linux
# ...
- title: build-windows
exclusive_exec_tag: windows
# ...
```
### Pattern 4: Requirement Chains
Combine multiple requirement types:
```yaml
actions:
- info: advanced-action@0.1.0
requirements:
- type: in_path
executable: docker
desc: "Install Docker"
- type: exists
path: ~/.docker/config.json
desc: "Run 'docker login' first"
- type: check_success
command:
cmd: docker ps
success_when_found: "CONTAINER"
desc: "Docker daemon must be running"
action:
# ...
```
## Troubleshooting Configuration Issues
**Issue**: Pipeline fails with "action not found"
- **Solution**: Ensure action `info` matches exactly in both definition and usage
**Issue**: Variables not substituting
- **Solution**: Verify placeholder format `{name}` matches in action and usage
**Issue**: Containerized build is slow
- **Solution**: Add cache strategies to avoid rebuilding dependencies
**Issue**: Pipeline works locally but fails in CI
- **Solution**: Use `driver: shell` and ensure all tools are in PATH
**Issue**: Artifacts not copying
- **Solution**: Check that `from` path is relative to run directory, not project directory
## Tips for LLMs
1. **Always specify version 8** in configuration files
2. **Use `driver: shell`** for CI/CD and containerized builds
3. **Use `driver: deployer`** for local development and full features
4. **Define cache_files** matching ignore_files for build outputs
5. **Use descriptive action info** with semantic versioning (e.g., `@0.1.0`)
6. **Add requirements** to actions that depend on external tools
7. **Use `show_success_output: true`** for validation and test actions
8. **Set `is_secret: true`** for sensitive variables
9. **Use `exclusive_exec_tag`** when pipelines modify same cache incompatibly
10. **Add `tags`** to pipelines for better organization and search
## Validation Checklist
Before finalizing a configuration, verify:
- [ ] `project_name` is set
- [ ] `version: 8` is specified
- [ ] At least one pipeline exists
- [ ] All action `used` fields match defined action `info` fields
- [ ] All placeholders in actions have corresponding variables
- [ ] All variables referenced in `with` clauses are defined
- [ ] `driver` is appropriate for the use case
- [ ] `exclusive_exec_tag` is used for conflicting builds
- [ ] Cache strategies are defined for containerized builds
- [ ] Requirements have helpful `desc` fields
- [ ] Artifacts paths are relative to run directory
This guide provides a comprehensive foundation for generating Deployer configurations that are correct, maintainable, and follow best practices.