Dagu is a local-first workflow engine for ops automation and AI-assisted operations. It is open source and self-hostable: a single binary with a built-in Web UI, no external database or message broker, running on Linux / Mac / Windows. Define DAGs in a declarative YAML format. It natively supports shell commands, Docker containers, Kubernetes Jobs, remote commands via SSH, external coding-agent CLIs through harness.run, and more through Dagu Actions.
Dagu turns existing scripts, runbooks, and agent-driven jobs into production workflows with scheduling, retries, human tasks, and run history. It runs where your data and credentials live: on-prem, air-gapped, edge, or cloud, and scales from a single node to a distributed worker fleet.
Highlights:
- Single binary file installation.
- Self-contained, with no need for a DBMS or message broker.
- Runs on Linux, macOS, and Windows.
- Declarative YAML format for defining DAGs.
- Run existing shell commands, Docker containers, Kubernetes Jobs, and remote commands over SSH without modifications.
- Compose reusable Sub-DAGs and run work in parallel with concurrency controls.
- Schedule workflows with cron syntax, timezones, overlap policies, and catch-up windows.
- Keep logs, run history, retries, notifications, and webhook triggers in one place.
- Built-in MCP support for AI agents to manage workflows.
- Run external coding-agent CLIs through
harness.runwhen workflows need AI assistance.
For a quick look at how workflows are defined, see the examples.
| Run Details | Step Logs | Wiki |
|---|---|---|
![]() |
![]() |
![]() |
Try it live: Live Demo (credentials: demouser / demouser)
Orchestration is not your main work. You have scripts and containers that already work. You want a schedule, retries, dependencies, and a place to see logs. The usual options each have a cost:
- cron runs commands, but gives you no dependencies, no retries, no history.
- Airflow orchestrates, but you operate a platform for it (scheduler, metadata database, workers, a Python environment), and your jobs get rewritten as
@dag/@taskframework code. - Temporal gives durable execution, but your business logic moves into its SDK and programming model.
You wanted to schedule some jobs. Now you operate a second system, and the orchestrator lives inside the code it was supposed to serve.
Dagu treats workflow structure as configuration, not code. Order, dependencies, retries, schedules, and human tasks go in one YAML file next to your scripts; the engine that runs them is a single process:
Traditional Orchestrator Dagu
┌────────────────────────┐ ┌──────────────────┐
│ Web Server │ │ │
│ Scheduler │ │ dagu start-all │
│ Worker(s) │ │ │
│ PostgreSQL │ └──────────────────┘
│ Redis / RabbitMQ │ Single binary.
│ Python Runtime │ Self-hosted.
└────────────────────────┘ Adds scheduling, retries, and human tasks around existing automation.
6+ services to manageYour scripts never import the orchestrator. Delete the YAML and they run exactly as before. Keep it, and every run gets a dependency graph, retries, per-step logs, history, and a Web UI.
Dagu stores state in local files and reaches production throughput without external services.
- Throughput: A single machine can run thousands of workflow runs per day. Actual capacity depends on CPU, memory, disk, and workflow shape.
- Load control: Queues, concurrency limits, and resource limits control how many runs execute at once and where they run.
- Scale out: Distributed workers spread execution across machines when one node is not enough.
| Use Case | How Dagu Helps |
|---|---|
| ETL and data operations | Turn data extraction scripts, SQL queries, dbt commands, and data-processing runbooks into observable pipelines with durable execution. |
| Legacy scripts and scheduled jobs | Turn complex jobs with interdependencies into maintainable DAGs with a UI, automatic logging, retries, and notifications instead of opaque cron jobs and bash scripts. |
| Media conversion | Run ffmpeg for video transcoding and format conversion. Thanks to Dagu's file-backed nature, workers can run heavy conversions in parallel without single machine bottlenecks or external databases. |
| Infrastructure and server automation | Run any command or script over SSH on remote servers, keeping logs, results, and notifications in one place. |
| GitHub-driven workflows | Trigger workflows from GitHub events. This is useful for running automation on private infrastructure without exposing your servers to the public internet. |
| Container and Kubernetes workflows | Run Docker containers and Kubernetes Jobs as steps in your workflows without building a custom control plane around containers. |
| Customer support automation | Run self-service support tools that non-engineering teams can use to run approved workflows for running diagnostics, querying databases, and performing common support tasks without escalating to engineering. |
| IoT and edge workflows | Run sensor polling, local ML inference, data preprocessing, backups, offline sync, health checks, etc. Dagu keeps these jobs close to the data source while still providing Web UI visibility. |
macOS/Linux:
curl -fsSL https://raw.githubusercontent.com/dagucloud/dagu/main/scripts/installer.sh | bashHomebrew:
brew install dagunpm:
npm install -g --ignore-scripts=false @dagucloud/daguWindows (PowerShell):
irm https://raw.githubusercontent.com/dagucloud/dagu/main/scripts/installer.ps1 | iexDocker:
docker run --rm -v ~/.dagu:/var/lib/dagu -p 8080:8080 ghcr.io/dagucloud/dagu:latest dagu start-allKubernetes (Helm):
helm repo add dagu https://dagucloud.github.io/dagu
helm repo update
helm install dagu dagu/dagu --set persistence.storageClass=<your-rwx-storage-class>Replace
<your-rwx-storage-class>with a StorageClass that supportsReadWriteMany. See charts/dagu/README.md for chart configuration.
The script installers run a guided wizard that can add Dagu to your PATH, set it up as a background service, and create the initial admin account. Homebrew, npm, Docker, and Helm install without the wizard. See the Installation documentation for all options.
Create hello.yaml:
steps:
- id: hello
run: echo "hello from Dagu"Run the workflow with:
dagu start hello.yamldagu start-all --dags .Visit http://localhost:8080
Run Dagu on one machine, or scale out with distributed workers. See the Deployment Models guide.
Single Server
|
Distributed Workers
|
| Model | Server | Execution | Best for |
|---|---|---|---|
| Single server | dagu start-all on one machine. |
Same machine. | Development, single-machine scheduled workloads, edge jobs, and internal automation. |
| Distributed workers | Dagu server and coordinator on your infrastructure. | Workers on separate machines, routed by labels. | Heavier workloads, private networks, and multiple execution hosts. |
- Community self-host: No license key required. You operate the server, storage, upgrades, networking, and workers. Start with the installation guide.
- Self-host license: Adds SSO, RBAC, audit logging, and incident SaaS integration to Dagu. See self-host licensing.
- Observability: Shared workflows and scheduling with clear visualizations, status tracking, and logs in the Web UI.
- Language-agnostic: No framework required. Define workflow steps using shell commands, Docker containers, Kubernetes Jobs, SQL queries, HTTP requests, and any other tool via official and third-party Dagu Actions.
- Build workflows: Reuse a step's result when its command and files have not changed. Dagu can also infer dependencies from matching file paths.
- Reproducibility: Reproducible runs with pinned tools, plus automatic installation and caching on workers—eliminating the need to manually install dependencies on the server or workers.
- Human Tasks: Pause a workflow for acknowledgement or typed operator input, then expose the response to downstream steps.
- Secret management: Built-in secret management with secure log masking, preventing credentials from leaking into logs or the Web UI.
- Self-hosted: A single binary that runs on Linux, macOS, and Windows. Includes an optional distributed worker mode for scaling out execution across machines.
- Permission Control: RBAC and SSO support for team environments, controlling who can view, run, and edit workflows through granular permissions and audit logging.
- MCP Server: Built-in MCP server for authoring and running workflows via AI agents like Claude Code, Codex, Gemini CLI, Pi, OpenCode, and more.
- External CLI Harness: You can run coding-agent CLIs (Claude Code, Codex, Gemini CLI, Pi, OpenCode, etc.) with a built-in harness action or custom harness definition.
Dagu can run in three configurations:
Standalone: A single dagu start-all process runs the HTTP server, scheduler, and executor. Suitable for single-machine deployments.
Coordinator/Worker: The scheduler enqueues jobs to a local file-based queue, then dispatches them to a coordinator over gRPC. Workers long-poll the coordinator for tasks, execute DAGs locally, and report status back. Workers can run on separate machines and are routed tasks based on labels.
Headless: Run without the web UI (DAGU_HEADLESS=true). Useful for CI/CD environments or when Dagu is managed through the CLI or API only.
Standalone:
┌─────────────────────────────────────────┐
│ dagu start-all │
│ ┌───────────┐ ┌───────────┐ ┌────────┐ │
│ │ HTTP / UI │ │ Scheduler │ │Executor│ │
│ └───────────┘ └───────────┘ └────────┘ │
│ File-based storage (logs, state, queue)│
└─────────────────────────────────────────┘
Distributed:
┌────────────┐ ┌────────────┐
│ Scheduler │ │ HTTP / UI │
│ │ │ │
│ ┌────────┐ │ └─────┬──────┘
│ │ Queue │ │ Dispatch (gRPC) │ Dispatch / GetWorkers
│ │(file) │ │─────────┐ │ (gRPC)
│ └────────┘ │ │ │
└────────────┘ ▼ ▼
┌─────────────────────────┐
│ Coordinator │
│ ┌───────────────────┐ │
│ │ Dispatch Task │ │
│ │ Store (pending/ │ │
│ │ claimed) │ │
│ └───────────────────┘ │
└────────▲────────────────┘
│
Worker poll / task response
Heartbeat / ReportStatus /
StreamLogs (gRPC)
│
┌─────────────┴─────────────┐
│ │ │
┌────┴───┐ ┌────┴───┐ ┌────┴───┐
│Worker 1│ │Worker 2│ │Worker N│ Sandbox execution of DAGs
│ │ │ │ │ │
└────────┘ └────────┘ └────────┘Workflows can define parameters that render as typed input forms in the Web UI and can be referenced by steps.
params:
- name: customer_id
type: string
description: Customer or account identifier
- name: change_scope
type: string
description: What the repair is allowed to change
enum:
- metadata_only
- permissions
- full_account
default: metadata_only
- name: dry_run
type: boolean
default: true
steps:
- id: extract
run: >-
./scripts/extract.sh
--customer "${params.customer_id}"
--scope "${params.change_scope}"
--dry-run="${params.dry_run}"
retry_policy:
limit: 3
interval_sec: 30steps:
- name: build
container:
image: node:20-alpine
run: npm run buildThe parent invokes the same child DAG for multiple targets and limits concurrent child runs:
steps:
- id: patch
action: dag.run
with:
dag: patch-host
params:
host: ${ITEM}
parallel:
items:
- web-1.internal
- web-2.internal
- db-1.internal
max_concurrent: 2
---
name: patch-host
params:
- name: host
type: string
ssh:
user: deploy
host: ${params.host}
steps:
- id: apply
run: apt-get update -q && apt-get upgrade -yssh:
user: deploy
host: web-1.internal
key: ~/.ssh/deploy_key
steps:
- id: health
run: curl -f http://localhost:8080/health
retry_policy:
limit: 3
interval_sec: 10
- id: restart
run: systemctl restart myapp
depends: healthschedule:
- "0 */6 * * *" # Every 6 hours
overlap_policy: skip # Skip if previous run is still active
catchup_window: "5h" # Catch up missed runs when scheduler is down for up to 5 hours
timeout_sec: 3600
handler_on:
failure:
run: notify-team.sh
exit:
run: cleanup.shsteps:
- name: flaky-api-call
run: curl -f https://api.example.com/data
retry_policy:
limit: 3
interval_sec: 10
continue_on:
failure: trueSee the Sub-DAG, SSH, scheduling, and notification documentation for complete configuration details.
steps:
- id: extract
run: ./extract.sh
- id: transform_a
run: ./transform_a.sh
depends: extract
- id: transform_b
run: ./transform_b.sh
depends: extract
- id: load
run: ./load.sh
depends: [transform_a, transform_b]%%{init: {'theme': 'base', 'themeVariables': {'background': '#18181B', 'primaryTextColor': '#fff', 'lineColor': '#888'}}}%%
graph LR
A[extract] --> B[transform_a]
A --> C[transform_b]
B --> D[load]
C --> D
style A fill:#18181B,stroke:#22C55E,stroke-width:1.6px,color:#fff
style B fill:#18181B,stroke:#22C55E,stroke-width:1.6px,color:#fff
style C fill:#18181B,stroke:#22C55E,stroke-width:1.6px,color:#fff
style D fill:#18181B,stroke:#3B82F6,stroke-width:1.6px,color:#fff
Save this as workflow.yaml:
type: build
working_dir: .
steps:
- id: uppercase
inputs:
- name: source
path: source.txt
outputs:
- name: result
path: uppercase.txt
run: |
#!/bin/sh
tr '[:lower:]' '[:upper:]' < "${inputs.source}" > "${outputs.result}"Run it:
printf 'alpha\n' > source.txt
dagu start workflow.yamlRun dagu start workflow.yaml again and Dagu reuses uppercase.txt. Change source.txt and the step runs again. ${outputs.result} is a temporary path that Dagu publishes as uppercase.txt after the command succeeds.
Build workflows currently run locally. See Build Workflows for dependency inference and reuse rules.
tools:
- jqlang/jq@jq-1.7.1
steps:
- id: inspect
run: jq --version
- id: summarize
action: python-script@v1
with:
input:
rows: [42, 8]
script: |
return {"total": sum(input["rows"])}Dagu installs declared portable CLIs before the DAG run, exposes them on PATH for host command steps, and caches them on each worker. Tool provisioning uses aqua as the default provider; the standard registry resolves to the latest aqua-registry release automatically. Pin a specific artifact with package@version#sha256:<hex> when the release tag alone is not a strong enough guarantee. See the Tools documentation and Dagu Actions for more details.
params:
- BUILD_ID
steps:
- id: notify
action: acme/dagu-action-notify@v1.2.0
with:
text: "Build ${params.BUILD_ID} finished"
- id: audit
depends: notify
run: 'echo "Notification result: ${steps.notify.outputs.messageId}"'A third-party Dagu Action package contains a DAG, manifest, schemas, and helper files behind an action: reference. See the Dagu Actions and Third-Party Actions documentation for details.
steps:
- name: batch-job
action: kubernetes.run
with:
namespace: production
image: my-registry/batch-processor:latest
resources:
requests:
cpu: "2"
memory: "4Gi"
command: ./process.shFor more examples, see the Examples documentation.
Dagu exposes a built-in MCP server at http://localhost:8080/mcp for reading Dagu state, changing workflows, and controlling runs. See the MCP setup guide.
External coding-agent CLIs can run as workflow steps through harness.run, and Agent DAGs can let an LLM choose the next step. The complete examples live in the Harness examples, AI examples, and Agent DAG documentation.
For authoring-only help in Claude Code, Codex, Gemini CLI, and other AI coding tools, install the Dagu workflow authoring skill:
gh skill install dagucloud/dagu daguDagu includes built-in actions that run within the Dagu process or on the selected worker. Local shell commands use the run: field; structured work uses action:.
| Action | Purpose |
|---|---|
run: field |
Local shell commands and scripts (bash, sh, PowerShell, custom shells) |
exec |
Direct process execution without shell parsing |
noop |
Output-only or approval-only placeholder step |
log.write |
Write structured log messages |
docker.run / container.run |
Run containers with registry auth, volume mounts, and resource limits |
kubernetes.run / k8s.run |
Execute Kubernetes Jobs with namespace, image, and resource settings |
ssh.run |
Remote command execution over SSH |
sftp.upload / sftp.download |
File transfer over SFTP |
http.request |
HTTP requests with headers, auth, and request bodies |
chat.completion |
Run an LLM chat completion step |
harness.run |
Run external coding-agent CLIs such as Claude Code, Codex, Copilot, OpenCode, and Pi |
postgres.query / postgres.import |
PostgreSQL queries and imports |
sqlite.query / sqlite.import |
SQLite queries and imports |
redis.<operation> |
Redis commands, pipelines, and Lua scripts |
s3.upload / s3.download / s3.list / s3.delete |
Upload, download, list, and delete S3 objects |
file.stat / file.read / file.write / file.copy / file.move / file.delete / file.mkdir / file.list |
Local file operations without shell commands |
artifact.write / artifact.read / artifact.list |
Write, read, and list DAG-run artifacts |
state.get / state.set / state.delete / state.list / state.diff |
Persistent JSON state across DAG runs |
data.convert / data.pick |
Convert and select structured data |
jq.filter |
JSON transformation using jq expressions |
archive.create / archive.extract / archive.list |
Create, extract, and list zip/tar archives |
wait.duration / wait.until / wait.file / wait.http |
Wait for time, file state, or HTTP readiness |
human.task |
Wait for acknowledgement or typed operator input before downstream steps continue |
mail.send |
Send email via SMTP |
template.render |
Text generation with template rendering |
router.route |
Conditional step routing based on values and patterns |
dag.run |
Invoke another DAG as a sub-workflow with params and dependencies |
dag.enqueue |
Queue another DAG asynchronously and continue after enqueue |
git.checkout |
Clone or update Git repositories |
outputs.write |
Publish DAG or Dagu Action outputs for callers |
Custom Actions are inline reusable wrappers defined with the top-level actions field. They expand to built-in actions during DAG load, so you can wrap a common shell, HTTP, SQL, or other pattern behind a typed interface with validated input.
actions:
webhook.send:
input_schema:
type: object
additionalProperties: false
required: [url, text]
properties:
url:
type: string
text:
type: string
template:
action: http.request
with:
method: POST
url: '{{ .input.url }}'
headers:
Content-Type: application/json
body: |
{"text": {{ json .input.text }}}
steps:
- action: webhook.send
with:
url: https://hooks.example.com/ops
text: deploy completeSee Custom Actions and the YAML Specification for the exact actions, action, and run field behavior.
Dagu Actions are official action packages maintained in the dagucloud GitHub organization. They use the same action package runtime as third-party action packages, but callers use the short form action: name@version.
| Dagu Action | Purpose |
|---|---|
node-script@v1 |
Run small JavaScript transforms or glue code with action-owned Node.js |
python-script@v1 |
Run small Python transforms or glue code with action-owned Python and optional requirements |
dbt@v1 |
Run dbt Core commands with action-owned Python and adapter requirements |
duckdb@v1 |
Run DuckDB SQL through the DuckDB CLI without adding DuckDB to the core binary |
ffmpeg@v1 |
Run FFmpeg conversion, transcoding, probing, and stream-processing tasks |
github-cli@v1 |
Run GitHub issue, pull request, release, repository, and API automation through gh |
rclone@v1 |
Run portable copy, sync, check, list, and storage-management workflows through rclone |
Versions are required. Pin production workflows to a version tag or commit SHA. See Official Dagu Actions for the current Dagu Action list and exact input/output contracts.
For non-official packages, use Third-Party Actions such as action: owner/repo@version. They contain a dagu-action.yaml manifest and a DAG entrypoint, run as sub-DAGs, and are transferred to distributed workers as workspace bundles after the reference is resolved. See the documentation for package layout and reference formats.
Dagu supports three top-level authentication modes, configured via DAGU_AUTH_MODE:
none— No authenticationbasic— HTTP Basic authenticationbuiltin— JWT-based authentication with user management, API keys, per-DAG webhook tokens, and optional OIDC/SSO integration
When using builtin auth, five roles control access:
| Role | Capabilities |
|---|---|
admin |
Full access including user management |
manager |
Create, edit, delete, run, stop DAGs; view audit logs |
developer |
Create, edit, delete, run, stop DAGs |
operator |
Run and stop DAGs only (no editing) |
viewer |
Read-only access |
API keys can be created with independent role assignments. Audit logging tracks all actions.
- TLS for the HTTP server (
DAGU_CERT_FILE,DAGU_KEY_FILE) - Mutual TLS for gRPC coordinator/worker communication (
DAGU_PEER_CERT_FILE,DAGU_PEER_KEY_FILE,DAGU_PEER_CLIENT_CA_FILE) - Secret management with environment variables, files, Kubernetes Secrets, HashiCorp Vault, and cloud-provider secret stores
For self-hosted production deployments, treat network exposure and execution boundaries as the primary controls:
- Prefer
auth.mode: builtinfor any shared or network-exposed instance. Usebasiconly for simple private setups, and avoidnoneoutside isolated local development. - Keep
metrics: privateunless the metrics endpoint is reachable only on a trusted private network. - Bind Dagu to loopback or a private interface when possible. If you must use
0.0.0.0, place it behind a trusted reverse proxy, TLS, and network-level access controls. - Leave
terminal.enabled: falseunless the instance is admin-only and tightly scoped. - In distributed deployments, set
peer.insecure=falseand configure peer TLS when coordinator and workers communicate across host or network boundaries. - Treat Docker socket mounts, root containers, and host-level executors as privileged access to the underlying machine.
See Server Configuration, Docker deployment, and Distributed execution for operator-focused guidance.
Dagu exposes Prometheus-compatible metrics:
dagu_info— Build information (version, Go version)dagu_uptime_seconds— Server uptimedagu_dag_runs_total— Total DAG runs by statusdagu_dag_runs_total_by_dag— Per-DAG run countsdagu_dag_run_duration_seconds— Histogram of run durationsdagu_dag_runs_currently_running— Active DAG runsdagu_dag_runs_queued_total— Queued runsdagu_workers_registered— Registered distributed workersdagu_worker_info— Worker heartbeat labels as key/value metadatadagu_worker_heartbeat_timestamp_seconds— Last worker heartbeat timestampdagu_worker_health_status— Worker health by heartbeat freshnessdagu_worker_pollers— Worker poller capacity by statedagu_worker_running_tasks— Running tasks per workerdagu_worker_oldest_running_task_age_seconds— Age of the oldest running task per worker
JSON or text format logging (DAGU_LOG_FORMAT). Logs are stored per-run with separate stdout/stderr capture per step.
- Email notifications on DAG success, failure, or wait status via SMTP
- Per-DAG webhook endpoints with token authentication
Dagu runs can write arbitrary files under ${context.paths.artifacts_dir} in value-resolved fields, with DAG_RUN_ARTIFACTS_DIR also exposed to step processes. Dagu stores those files per run as Artifacts. In the Web UI, operators can browse the file tree, preview Markdown, text, and image files inline, and download any artifact when they need the raw file.
This is useful for generated reports, screenshots, charts, exported JSON or CSV files, and other outputs that do not fit simple key/value outputs.
See the Artifacts documentation and the Web UI guide for the full artifact browser workflow and screenshots.
- Cron scheduling with timezone support and multiple schedule entries per DAG
- Overlap policies:
skip(default — skip if previous run is still active),all(queue all),latest(keep only the most recent) - Catch-up scheduling: Automatically runs missed intervals when the scheduler was down
- Zombie detection: Identifies and handles stalled DAG runs (configurable interval, default 45s)
- Retry policies: Per-step retry with configurable limits, intervals, and exit code filtering
- Human tasks: Pause root DAG runs for acknowledgement or schema-validated operator input, locally or on distributed workers, then expose form values to downstream steps
- Lifecycle hooks:
onInit,onSuccess,onFailure,onAbort,onExit,onWait - Preconditions: Gate DAG or step execution on shell command results
- High availability: Scheduler lock with stale detection for failover
The coordinator/worker architecture distributes DAG execution across multiple machines:
- Coordinator: gRPC server that manages task distribution, worker registry, and health monitoring
- Workers: Connect to the coordinator, pull tasks from the queue, execute DAGs locally, report results
- Worker labels: Route DAGs to specific workers based on labels (e.g.,
gpu=true,region=us-east-1) - Health checks: HTTP health endpoints on coordinator and workers for load balancer integration
- Queue system: File-based persistent queue with configurable concurrency limits
# Start coordinator
dagu coordinator
# Start workers (on separate machines)
DAGU_WORKER_LABELS=gpu=true,memory=64G dagu workerSee the distributed execution documentation for setup details.
| Command | Description |
|---|---|
dagu start <dag> |
Execute a DAG |
dagu start-all |
Start HTTP server + scheduler + coordinator |
dagu server |
Start HTTP server only |
dagu scheduler |
Start scheduler only |
dagu coordinator |
Start coordinator (distributed mode) |
dagu worker |
Start worker (distributed mode) |
dagu stop <dag> |
Stop a running DAG |
dagu restart <dag> |
Restart a DAG |
dagu retry --run-id=<run-id> <dag> |
Retry a failed run |
dagu human-task complete --run-id=<run-id> --step=<id> <dag> |
Complete a waiting human task |
dagu dry <dag> |
Dry run — show what would execute |
dagu status <dag> |
Show DAG run status |
dagu history <dag> |
Show execution history |
dagu validate <dag> |
Validate DAG YAML |
dagu enqueue <dag> |
Add DAG to the execution queue |
dagu dequeue <queue-name> [--dag-run=<dag>:<run-id>] |
Remove a DAG-run from the queue |
dagu cleanup <dag> |
Clean up old run data |
dagu version |
Show version |
The table lists the most common commands. The binary ships 31 in total, including exec, ls, ps, rm, sync, schema, example, config, profile, context, license, upgrade, and completion; run dagu --help or see the CLI reference for all of them.
Precedence: Command-line flags > Environment variables > Configuration file (~/.config/dagu/config.yaml)
| Variable | Default | Description |
|---|---|---|
DAGU_HOST |
127.0.0.1 |
Bind address |
DAGU_PORT |
8080 |
HTTP port |
DAGU_BASE_PATH |
— | Base path for reverse proxy |
DAGU_HEADLESS |
false |
Run without web UI |
DAGU_TZ |
— | Timezone (e.g., Asia/Tokyo) |
DAGU_LOG_FORMAT |
text |
text or json |
DAGU_CERT_FILE |
— | TLS certificate |
DAGU_KEY_FILE |
— | TLS private key |
DAGU_CORS_ALLOWED_ORIGINS |
— | Comma-separated list of allowed CORS origins (e.g. https://app.example.com). When unset, cross-origin browser access is disabled. Exact origins enable credentials. An explicit * allows every origin without credentials and emits a security warning. |
DAGU_PUBLIC_URL |
— | External Web UI URL used in generated links, including notification and incident DAG-run links |
DAGU_SERVER_METRICS |
private |
Metrics endpoint access: private or public |
DAGU_TERMINAL_ENABLED |
false |
Enable the web-based terminal |
DAGU_DEFAULT_SHELL |
$SHELL, then sh |
Default shell for command steps |
DAGU_ENV_PASSTHROUGH_PREFIXES |
— | Comma-separated env var prefixes forwarded to step execution |
DAGU_DEBUG |
— | Enable debug mode |
| Variable | Default | Description |
|---|---|---|
DAGU_HOME |
— | Overrides all path defaults |
DAGU_DAGS_DIR |
~/.config/dagu/dags |
DAG definitions directory |
DAGU_DAG_DISCOVERY_RECURSIVE |
false |
Discover DAGs in subdirectories |
DAGU_DAG_DISCOVERY_SYMLINKS |
false |
Include recursive file symlinks and allow external targets |
DAGU_LOG_DIR |
~/.local/share/dagu/logs |
Log files |
DAGU_DATA_DIR |
~/.local/share/dagu/data |
Application state |
DAGU_TOOLS_DIR |
{DAGU_DATA_DIR}/tools |
Managed DAG tool cache |
DAGU_DAG_STATE_DIR |
{DAGU_DATA_DIR}/dag-state |
Persistent DAG state files |
DAGU_DAG_RUN_WORK_DIR |
{DAGU_DATA_DIR}/dag-run-work |
Per-run working directories |
DAGU_BASE_CONFIG |
— | Shared base configuration applied to all DAGs |
Set the per-run work root in config.yaml, or use the corresponding environment variable above:
paths:
dag_run_work_dir: /mnt/dagu/dag-run-workDAGU_DAG_RUN_WORK_DIR configures this root for Dagu processes. Workflow code should use the runtime DAG_RUN_WORK_DIR variable for its assigned per-run directory instead of constructing paths under DAG-run history.
Processes sharing DAG runs must use the same work root.
Backups that select individual data subdirectories must include both paths.dag_runs_dir and paths.dag_run_work_dir. A backup of the complete paths.data_dir includes both default locations. The Helm chart's default /data/dag-run-work path uses its existing /data volume and does not need an additional volume.
When upgrading a deployment whose processes share a durable work root, do not let old and new Dagu versions execute the same run concurrently: drain or stop the processes, upgrade them together, and then resume execution. Mixed-version processes can otherwise choose the old nested directory and the new separate directory for one run.
Recursive discovery can also be enabled in config.yaml:
dag_discovery:
recursive: trueIt scans paths.dags_dir, excluding workspaces/, dot-directories, and symlinks. File stems and effective DAG names must each be unique, using case-sensitive comparison; conflicting files are excluded until the conflict is resolved. paths.alt_dags_dir remains lookup-only.
Set dag_discovery.symlinks: true (or
DAGU_DAG_DISCOVERY_SYMLINKS=true) to include YAML file symlinks in recursive
discovery and to allow YAML file symlinks whose targets are outside
paths.dags_dir. Symlinked directories are never traversed. External targets
can be viewed, scheduled, and run, but cannot be updated, deleted, or renamed
through Dagu. Without the opt-in, top-level YAML file symlinks whose targets
remain inside paths.dags_dir continue to work. A symlink configured as
paths.dags_dir itself is also supported.
| Variable | Default | Description |
|---|---|---|
DAGU_AUTH_MODE |
builtin |
none, basic, or builtin |
DAGU_AUTH_BASIC_USERNAME |
— | Basic auth username |
DAGU_AUTH_BASIC_PASSWORD |
— | Basic auth password |
DAGU_AUTH_TOKEN_SECRET |
(auto) | JWT signing secret |
DAGU_AUTH_TOKEN_TTL |
24h |
JWT token lifetime (maximum: 8760h / 365 days) |
DAGU_AUTH_BUILTIN_INITIAL_ADMIN_USERNAME |
— | Auto-provision the first admin on startup (requires the password variable) |
DAGU_AUTH_BUILTIN_INITIAL_ADMIN_PASSWORD |
— | Password for the auto-provisioned admin (minimum 8 characters) |
DAGU_LICENSE_KEY |
— | License key for licensed self-host features |
OIDC variables: DAGU_AUTH_OIDC_CLIENT_ID, DAGU_AUTH_OIDC_CLIENT_SECRET, DAGU_AUTH_OIDC_ISSUER, DAGU_AUTH_OIDC_SCOPES, DAGU_AUTH_OIDC_WHITELIST, DAGU_AUTH_OIDC_AUTO_SIGNUP, DAGU_AUTH_OIDC_DEFAULT_ROLE, DAGU_AUTH_OIDC_ALLOWED_DOMAINS.
| Variable | Default | Description |
|---|---|---|
DAGU_SCHEDULER_PORT |
8090 |
Health check port |
DAGU_SCHEDULER_ZOMBIE_DETECTION_INTERVAL |
45s |
Zombie run detection interval (0 to disable) |
DAGU_SCHEDULER_LOCK_STALE_THRESHOLD |
30s |
HA lock stale threshold |
DAGU_QUEUE_ENABLED |
true |
Enable queue system |
| Variable | Default | Description |
|---|---|---|
DAGU_COORDINATOR_HOST |
127.0.0.1 |
Coordinator bind address |
DAGU_COORDINATOR_PORT |
50055 |
Coordinator gRPC port |
DAGU_COORDINATOR_HEALTH_PORT |
8091 |
Coordinator health check port |
DAGU_WORKER_ID |
— | Worker instance ID |
DAGU_WORKER_MAX_ACTIVE_RUNS |
100 |
Max concurrent runs per worker |
DAGU_WORKER_HEALTH_PORT |
8092 |
Worker health check port |
DAGU_WORKER_LABELS |
— | Worker labels (key=value,key=value) |
DAGU_COORDINATOR_ADVERTISE |
auto-detected hostname | Address advertised in the service registry |
DAGU_WORKER_COORDINATORS |
— | Explicit coordinator addresses for shared-nothing mode |
| Variable | Default | Description |
|---|---|---|
DAGU_PEER_CERT_FILE |
— | Peer TLS certificate |
DAGU_PEER_KEY_FILE |
— | Peer TLS private key |
DAGU_PEER_CLIENT_CA_FILE |
— | CA for client verification |
DAGU_PEER_INSECURE |
true |
Use h2c instead of TLS |
DAGU_PEER_SKIP_TLS_VERIFY |
— | Skip TLS certificate verification |
| Variable | Default | Description |
|---|---|---|
DAGU_GITSYNC_ENABLED |
false |
Enable Git sync |
DAGU_GITSYNC_REPOSITORY |
— | Repository URL |
DAGU_GITSYNC_BRANCH |
main |
Branch to sync |
DAGU_GITSYNC_AUTH_TYPE |
token |
token or ssh |
DAGU_GITSYNC_AUTH_TOKEN |
— | Personal access token for HTTPS auth |
DAGU_GITSYNC_AUTH_SSH_KEY_PATH |
— | Path to the SSH private key |
DAGU_GITSYNC_AUTOSYNC_ENABLED |
false |
Enable periodic auto-pull |
DAGU_GITSYNC_AUTOSYNC_INTERVAL |
300 |
Sync interval in seconds |
These tables cover the variables most deployments touch. The full reference lists about 180 DAGU_* variables, including SSE, tunnel, UI, monitoring, audit, and secret-provider settings: see the configuration reference.
Go applications can import Dagu and start DAG runs from the host process:
import "github.com/dagucloud/dagu/v2"engine, err := dagu.New(ctx, dagu.Options{
HomeDir: "/var/lib/myapp/dagu",
})
if err != nil {
return err
}
defer engine.Close(context.Background())
run, err := engine.RunYAML(ctx, []byte(`
params:
- MESSAGE
steps:
- name: hello
run: echo "${params.MESSAGE}"
`), dagu.WithParams(map[string]string{
"MESSAGE": "hello from the host app",
}))
if err != nil {
return err
}
status, err := run.Wait(ctx)
if err != nil {
return err
}
fmt.Println(status.Status)The embedded API is experimental and may change. See the embedded API documentation and examples/embedded.
- Discord — Questions and discussion
- GitHub Issues — Bug reports and feature requests
- Bluesky
Prerequisites: Go 1.26+, Node.js, pnpm
git clone https://github.com/dagucloud/dagu.git && cd dagu
make build # Build frontend + Go binary
make test # Run tests with race detection
make lint # Run golangci-lintSee CONTRIBUTING.md for development workflow and code standards.
We welcome contributions of all kinds. See our Contribution Guide for details.
GNU GPLv3 - See LICENSE. See LICENSING.md for embedded API and commercial embedding notes.






