Dagu on Ubuntu 24.04 on Azure User Guide
Overview
Dagu is a self-contained workflow and cron orchestration engine. You describe your work as a DAG - a directed acyclic graph of steps - in a single, readable YAML file, and Dagu runs it on demand or on a schedule, resolving dependencies between steps and handling retries, timeouts, preconditions and lifecycle hooks. A built-in web UI lets you browse workflows, start and stop runs, watch each step of a run graph in real time and inspect per-run logs and execution history, while a REST API and a full command line interface drive the same engine from scripts. Because it needs no database and no message broker and keeps all of its state on the local filesystem, it starts instantly and is ideal for cron replacement, data pipelines, backups, batch jobs and edge automation on a single machine.
The cloudimg image installs the pinned Dagu 2.10.7 single Go binary running under systemd as dagu start-all (the HTTP server, scheduler and executor in one process), then locks it down for a marketplace appliance. Dagu binds to loopback 127.0.0.1:8080 so its UI and API are never exposed directly, and an nginx reverse proxy on port 80 adds a per-VM HTTP Basic Auth gate in front of it. Dagu's own web UI is unauthenticated by default, so this image closes that gap: nginx requires the admin credential (a unique password generated on the first boot of your VM) for the entire UI and API, while an unauthenticated /healthz endpoint is exposed for load balancer probes. An example workflow is bundled so the engine has something to run immediately. Backed by 24/7 cloudimg support.
What is included:
- Dagu 2.10.7 installed as a single Go binary (
/usr/local/bin/dagu) running as thedagusystemd service (dagu start-all) - The Dagu web UI and REST API on
:80, fronted by nginx with the binary bound to loopback only - Per-VM HTTP Basic Auth (user
admin) protecting the entire UI and API, with a unique password generated on first boot - Dagu bound to
127.0.0.1:8080- nothing is exposed to the network directly - Dagu's own native basic auth also enabled as defence-in-depth on the loopback interface
- An example workflow at
/var/lib/dagu/dags/hello.yamlthat runs on first boot to prove the engine DAGU_HOME=/var/lib/daguholding your workflow definitions (dags/), run logs and statedagu.service+nginx.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - Ubuntu 24.04 LTS base, latest patches, unattended security upgrades enabled
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2s (2 vCPU / 4 GiB RAM) is a comfortable starting point; Dagu is light on resources. NSG inbound: allow 22/tcp from your management network, 80/tcp for the UI and API, and 443/tcp if you add TLS. Dagu serves plain HTTP on port 80; for production use, terminate TLS in front of it with your own domain and restrict access to trusted IP ranges (see Maintenance).
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for Dagu by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name dagu \
--image <marketplace-image-urn> \
--size Standard_B2s \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name dagu --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
systemctl is-active dagu.service nginx.service
Both report active. Dagu serves its web UI and REST API on the loopback address 127.0.0.1:8080; nginx fronts it on port 80 and adds the per-VM HTTP Basic Auth gate. Nothing binds a public application port directly - the only way in is through nginx.

Step 5 - Retrieve your web UI password
nginx protects the Dagu UI and API with HTTP Basic Auth. The username is admin and a unique password is generated on the first boot of your VM and written to a root-only file:
sudo cat /root/dagu-credentials.txt
This file contains DAGU_USERNAME, DAGU_PASSWORD and the DAGU_URL to open in a browser. The password is stored on disk only as a bcrypt hash in /etc/nginx/.dagu.htpasswd, so no plaintext password ships in the image. Store the password somewhere safe.

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Confirm the authentication gate
Because a password is set on first boot, an unauthenticated request to the UI returns HTTP 401, so nobody reaches Dagu without the password. The following reads the per-VM password from the credentials file and proves the round-trip - unauthenticated is rejected, a wrong password is rejected, and the correct password authenticates:
PW=$(sudo grep '^DAGU_PASSWORD=' /root/dagu-credentials.txt | cut -d= -f2-)
echo "unauth : $(curl -s -o /dev/null -w '%{http_code}' http://127.0.0.1/)"
echo "wrongpw : $(curl -s -o /dev/null -w '%{http_code}' -u admin:wrong-pw http://127.0.0.1/)"
echo "authed : $(curl -s -o /dev/null -w '%{http_code}' -u admin:$PW http://127.0.0.1/)"
It prints unauth : 401, wrongpw : 401, and authed : 200. The whole UI and API are only reachable with the per-VM password, because Dagu itself is bound to loopback and nginx is the only way in.
Step 8 - Run the bundled example workflow
A workflow in Dagu is just a YAML file describing a directed acyclic graph of steps. The image ships an example at /var/lib/dagu/dags/hello.yaml (two steps, greet then show_date, plus a */30 cron schedule). Run it from the command line and watch the engine execute each step to success:
sudo -u dagu env DAGU_HOME=/var/lib/dagu /usr/local/bin/dagu start /var/lib/dagu/dags/hello.yaml
The engine runs greet, then show_date (which depends on greet), and reports Result: Succeeded. Copy this file and edit it to build your own workflows, or create new ones under /var/lib/dagu/dags/.

Step 9 - Sign in and open the dashboard
Browse to http://<vm-public-ip>/. Your browser prompts for a username and password: enter admin and the password from Step 5. Dagu opens on the Overview dashboard, which shows a timeline of recent DAG runs with an at-a-glance summary of how many succeeded, failed or were aborted.

Step 10 - Browse your workflows
Open the Workflows page from the left navigation. It lists every DAG under /var/lib/dagu/dags/ with its latest status, the time of its last run, and its schedule and next run time, plus action buttons to start, stop or reload a workflow. The bundled hello workflow appears here with its */30 cron schedule.

Step 11 - Inspect a workflow and its run graph
Click a workflow to open its detail view. The Status tab renders the DAG as a graph so you can see the dependencies between steps - here greet leads to show_date - with each node coloured by the outcome of the latest run. The Timeline, Outputs and Spec tabs show the run timeline, captured step outputs and the raw YAML definition.

Step 12 - Review execution history
Open the Executions page from the left navigation for a searchable, filterable history of every DAG run across your workflows. Each row shows the workflow name, the run ID, its status, what triggered it (a schedule, a manual start or a retry), the scheduled, queued and started times, the duration and the worker that executed it.

Step 13 - Review the installed components
Dagu ships as a single static Go binary with a small, predictable filesystem layout. DAGU_HOME=/var/lib/dagu holds your workflow definitions in dags/, run logs in logs/ and engine state in data/:
dagu version
ls /usr/local/bin/dagu /etc/dagu/dagu.env /var/lib/dagu/dags/hello.yaml

Maintenance
- Password: the UI password is set on first boot and stored as a bcrypt entry in
/etc/nginx/.dagu.htpasswd. To change it, runsudo htpasswd -B /etc/nginx/.dagu.htpasswd admin, set the same value inDAGU_AUTH_BASIC_PASSWORDin/etc/dagu/dagu.env, thensudo systemctl reload nginxandsudo systemctl restart dagu. - Create workflows: add your own
*.yamlDAG files under/var/lib/dagu/dags/. Each file definessteps:with anid:and arun:command, optionaldepends:for ordering, and an optionalschedule:cron expression. Dagu picks them up automatically and they appear on the Workflows page. - Restrict access: Dagu serves plain HTTP on port 80. For production, restrict access to trusted IP ranges in your Network Security Group, and front it with TLS (for example certbot with your own domain) terminating on
:443. - Loopback binding: Dagu is bound to
127.0.0.1:8080(DAGU_HOST/DAGU_PORTin/etc/dagu/dagu.env), so nginx is the only path in. Keep it that way - do not change the bind address to a public interface. - Scheduling:
dagu start-allruns the scheduler, so any workflow with aschedule:cron expression runs automatically. Overlap policy, catch-up and retry behaviour are configured per workflow in its YAML. - Storage: all workflow definitions, run logs and engine state live under
/var/lib/dagu; back up that directory to protect your workflows and run history. - Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.