6.2 KiB
Build Tracking
Important
New as of 0.38.0
Every deploy that flows through Dokku - whether triggered by git push, ps:rebuild, ps:restart, config:set, or git:from-archive / git:from-image / git:sync / git:load-image - is recorded as a structured build record on disk. The builds plugin lets operators inspect what is currently deploying, look up the result of an old deploy, and stream the captured build log without depending on journalctl.
builds:cancel <app> # Cancel a running build for an app
builds:info <app> <build-id> [--format json] # Show details for a single build
builds:list [<app>] [--format json] [--kind ...] [--status ...]
# List builds (running across all apps, or running + history for one)
builds:output <app> [<build-id>|current] # Show build output (tail for live, cat for finished)
builds:prune <app> [--all-apps] # Reap abandoned records and apply retention
builds:report [<app>] [<flag>] # Display a build report
builds:set [--global|<app>] <key> [<value>] # Set or clear a builds property
Build records
Every build is identified by a sortable base36 ULID-style id (DOKKU_BUILD_ID) generated at deploy start. For each build, Dokku writes:
$DOKKU_LIB_ROOT/data/builds/<app>/<build-id>.json- the structured record$DOKKU_LIB_ROOT/data/builds/<app>/<build-id>.log- the captured stdout/stderr of the deploy
Output is also tagged into syslog as dokku-<build-id> so journalctl -t dokku-<build-id> continues to work. The on-disk log file is the durable source of truth and is read for builds:output even when journald has rotated old entries away.
Record schema
{
"id": "01j8c4xv7bk5w3",
"app": "myapp",
"kind": "build",
"pid": 12345,
"started_at": "2026-04-30T13:50:00Z",
"finished_at": "2026-04-30T13:51:14Z",
"status": "succeeded",
"source": "git-hook",
"exit_code": 0
}
- kind -
buildfor paths that produce a new image (git push,git:*,ps:rebuild);deployfor paths that re-deploy an existing image (ps:restart,ps:start,dokku deploy,config:set). - status - on-disk values are
running | succeeded | failed | canceled.abandonedis a fifth display-only value computed at read time forrunningrecords whose PID is no longer alive; it is never persisted to the record. - source - the user-typed command that originated the deploy (e.g.
git-hook,ps:restart,config-redeploy,git:sync).
Listing builds
Without an app argument, builds:list shows currently-running builds across every app on the host:
dokku builds:list
=====> Currently running builds
App Build ID Kind PID Source Started
myapp 01j8c4xv7bk5w3 build 12345 git-hook 2026-04-30T13:50:00Z
With an app, builds:list returns running builds plus the most recent finalized records up to the configured retention:
dokku builds:list myapp
Filter by kind or status:
dokku builds:list myapp --kind build
dokku builds:list myapp --status running
dokku builds:list --format json
Inspecting a single build
dokku builds:info myapp 01j8c4xv7bk5w3
=====> Build 01j8c4xv7bk5w3
Build ID: 01j8c4xv7bk5w3
App: myapp
Kind: build
Status: succeeded
PID: 12345
Source: git-hook
Started: 2026-04-30T13:50:00Z
Finished: 2026-04-30T13:51:14Z
Duration: 1m14s
Exit Code: 0
Log: /var/lib/dokku/data/builds/myapp/01j8c4xv7bk5w3.log
JSON output includes the same fields plus a computed log_path:
dokku builds:info myapp 01j8c4xv7bk5w3 --format json | jq .
Streaming build output
# tail -f the live build, or cat the log for a finished one
dokku builds:output myapp 01j8c4xv7bk5w3
# resolve "current" from the in-flight deploy
dokku builds:output myapp current
If the on-disk log file is missing (for example, a build from before this plugin was installed), builds:output falls back to journalctl -t dokku-<build-id>.
Cancelling a build
builds:cancel reads the active .deploy.lock for an app, looks up the matching build record, and sends SIGQUIT to the deploy's process group. The record is finalized as canceled (or failed if the process had already exited without finalizing - in that case Dokku marks the record so it doesn't sit in running forever).
dokku builds:cancel myapp
If the record is already finalized when cancel runs, no signal is sent and the record is left untouched.
Retention
Build records are pruned by count, not by age. The default retention is 20 records per app. Live in-flight deploys are never pruned regardless of count.
Set a per-app retention:
dokku builds:set myapp retention 50
Set the global default:
dokku builds:set --global retention 10
Clear an override (falls back to the global value, then the default):
dokku builds:set myapp retention
Manual pruning
builds:prune invokes the same logic that runs at the end of every deploy - it reaps abandoned records (status=running with a dead PID, finalized as failed) and trims the directory to the configured retention. Useful after lowering retention via builds:set or to clean up after a host reboot:
dokku builds:prune myapp
dokku builds:prune --all-apps
Reports
dokku builds:report
dokku builds:report myapp
dokku builds:report myapp --build-status
Available flags:
--build-id,--build-kind,--build-status,--build-pid,--build-source,--build-started-at,--build-finished-at,--build-exit-code: details of the most recent build for the app--builds-retention: per-app retention override (empty if none)--builds-global-retention: global retention override (empty if none)--builds-computed-retention: the resolved retention applied to this app
--build-status returns the display status, so an abandoned in-flight build shows abandoned rather than running. The raw on-disk status is only visible by reading the JSON record directly.