Core Concepts
Essential concepts for working with Dagu.
DAG (Directed Acyclic Graph)
A DAG defines your workflow as a graph of dependencies:
- Directed: Steps execute in a specific order
- Acyclic: No circular dependencies allowed
- Graph: Steps connected by dependency relationships
Workflow Components
Steps
The basic unit of execution. Each step runs a command or action:
tools:
- astral-sh/uv@0.11.14
steps:
- id: download
run: curl -O https://example.com/data.csv
- id: analyze
run: uv run --python 3.13.9 python analyze.py data.csv
depends: downloadSteps can execute multiple commands that share the same configuration:
tools:
- nodejs/node@v22.21.1
steps:
- id: build_and_test
run: |
npm install
npm run build
npm test
env:
- NODE_ENV: productionDependencies
Declare step relationships with depends:
steps:
- id: A
run: echo "First"
- id: B
run: echo "Second (after A)"
depends: A
- id: C
run: echo "Parallel with B"
depends: A # Only depends on A, runs parallel to B
- id: D
run: echo "After both B and C"
depends: [B, C]Parameters
params:
- name: env
type: string
default: dev
enum: [dev, staging, prod]
description: Target environment
- name: region
default: us-east-1
description: Deployment region
steps:
- run: echo "Deploying to ${env} in ${region}"Override at runtime:
dagu start workflow.yaml -- env=prod region=eu-west-1Parameter defaults are literal by default. If you need $VAR or backtick evaluation for a DAG param, use eval: on an inline rich parameter definition. Runtime overrides from the CLI, API, and sub-DAG calls remain literal.
Variables
Pass data between steps using output:
steps:
- id: date_stamp
run: date +%Y%m%d
output: TODAY
- id: backup
run: tar -czf backup_${TODAY}.tar.gz /data
depends: date_stampPersistent State
Use persistent state when one DAG run needs to remember a small JSON value for the next run:
steps:
- id: load_cursor
action: state.get
output: CURSOR
with:
key: cursors/feed
default:
last_id: 0
- id: save_cursor
action: state.set
with:
key: cursors/feed
value:
last_id: 123
depends: load_cursorState is versioned, stored locally by default, and can be scoped to the current DAG, the root DAG, a global namespace, or a custom shared namespace. See Persistent State.
Tools
Declare portable CLI dependencies at the DAG level when the workflow needs a reproducible binary version:
tools:
- jqlang/jq@jq-1.7.1
steps:
- run: jq --versionDagu installs these tools before the run and exposes them to host command steps through PATH. See Tools for syntax and limitations.
Status Management
Execution States
not_started: DAG has been defined but execution has not begunqueued: DAG is waiting to be executedrunning: DAG is currently executingsucceeded: All steps completed successfullypartially_succeeded: Some steps failed but execution continued (viacontinue_on)failed: DAG execution failedaborted: DAG was manually aborted
Status Transitions
Step Status
not_started: Step is waiting for dependenciesrunning: Step is executingsucceeded: Step completed successfullypartially_succeeded: Step completed with warnings or continue-on logicfailed: Step execution failedaborted: Step was abortedskipped: Step was skipped (precondition not met)
Status Hooks
handler_on:
success:
run: notify-team.sh "Workflow succeeded"
failure:
run: alert-oncall.sh "Workflow failed"
partial_success:
run: log-partial.sh "Some steps partially succeeded"Actions
Shell (Default)
Runs commands in the system shell. Set it once per DAG or override per step:
type: chain
shell: [bash, -e] # Default shell + args for all steps
steps:
- run: echo "Hello" # Uses DAG shell
- run: echo "Using zsh"
with:
shell: /usr/bin/zsh # Per-step overrideSee Shell for more details.
Docker
Execute in containers:
container:
image: python:3.11
working_dir: /app
volumes:
- /app/data:/data
steps:
- run: python script.pySee Docker for more details.
SSH
Run on remote machines:
ssh:
user: ubuntu
host: server.example.com
key: ~/.ssh/id_rsa
steps:
- run: echo "Running remote script"See SSH for more details.
HTTP
Make API calls:
steps:
- action: http.request
with:
method: POST
url: https://api.example.com/trigger
headers:
Authorization: Bearer ${API_TOKEN}See HTTP for more details.
Git
Clone or update a repository during a workflow run:
steps:
- id: checkout_source
action: git.checkout
with:
repository: https://github.com/example/app.git
ref: main
path: ./workspace/appSee Git for more details.
Wait
Wait for time, file state, or HTTP readiness:
steps:
- id: wait_for_api
action: wait.http
with:
url: https://api.example.com/health
status: 200
poll_interval: 5s
timeout_sec: 300See Wait for more details.
Custom Actions
Define reusable actions in actions when you want a typed wrapper around a built-in step type:
actions:
greet:
input_schema:
type: object
additionalProperties: false
required: [message]
properties:
message:
type: string
template:
run: |
#!/bin/bash
printf '%s\n' {{ json .input.message }}
steps:
- action: greet
with:
message: helloThe common case is a custom action with a templated run step. Schema defaults can be applied to the with object, the result is validated against input_schema, and then the template expands to a built-in step before execution. See Custom Actions for the full rules.
Scheduling
Cron-based scheduling:
schedule: "0 2 * * *" # Daily at 2 AMMultiple schedules:
schedule:
- "0 9 * * MON-FRI" # Weekdays at 9 AM
- "0 14 * * SAT,SUN" # Weekends at 2 PMStart/stop schedules:
schedule:
start: "0 8 * * *" # Start at 8 AM
stop: "0 18 * * *" # Stop at 6 PMSee Scheduling for more details.
Lifecycle Handlers
Execute commands on workflow events:
handler_on:
init:
run: echo "Setting up environment" # Runs before any steps
success:
run: echo "Workflow succeeded"
failure:
run: |
echo "Workflow failed" | mail -s "Alert" admin@example.com
abort:
run: echo "Cleaning up resources"
exit:
run: echo "Always runs"See Also
- Writing Workflows - Create your own workflows
- Examples - Ready-to-use patterns
- CLI Reference - Command-line usage
