Dockflow

Exit codes and errors

Every exit code of the dockflow CLI, the format of its errors, and every error and warning code, with what to do about each.

Every dockflow command ends with an exit code that tells a script or a CI job what happened, without parsing text. When a command fails, it also prints an error with a stable code and a hint that says what to do next.

This page lists every exit code, the error format, every error code grouped by exit code, and the warning codes. To act on a failure in a script, check the exit code first, then the error code for detail.

Exit codes are fixed for the whole of 1.x: a new one only comes with a major release. New error and warning codes may appear in a minor release, each mapped to an existing exit code.

Exit codes

CodeNameMeaningTypical causes
0OKSuccess, including a plan with nothing to doA deploy that went live, a deploy with nothing to change, a read command
1InternalUnexpected error. This is a bug: please report itThe agent stopped before finishing, a corrupt state file, the switch's admin API not answering
2UsageWrong usageUnknown command or flag, a bad argument, an unknown run id for attach, a CI tag whose suffix names no environment
3ConfigInvalid configurationAn error in dockflow.yml or Compose, a variable with no value, an unreadable .env.dockflow, a key from a later Dockflow, an SSH key protected by a passphrase
4ConnectCannot connectSSH unreachable or refused, unknown or changed host key, Docker API unreachable, Docker older than 27, the agent binary cannot be downloaded
5LockedAnother run holds the lockA deploy already running on the same environment, a manual lock taken with dockflow lock acquire
6PlanThe plan is refused; nothing was changedA saved plan that is stale, a domain routed twice, an image the server cannot pull, routes on a server without a switch, nothing to roll back to
7BuildBuild or push failed; nothing was changed on the serverA failing Dockerfile, buildx or Podman missing, a registry that refuses the push
8Rolled backThe run failed and was rolled back: the previous release is runningA service that does not become healthy, a failed health check, a failed job, a fatal after_deploy hook
9DegradedThe run failed and was not rolled back: the environment is degradedA failed health check with on_failure: fail or --no-rollback, a rollback that failed too; dockflow diagnose found a problem
10Lock lostThe lock was lost during the runReserved: the lock is held until the agent exits, so this version never reports it
11HookA fatal hook failed before anything changedA fatal before_build, after_build or before_deploy hook that exited with a non-zero code
12BackupA backup failedA dump that exits with an error, a repository that cannot be opened or fails its check, a service that is not running
13StateThe server's state needs another Dockflow; nothing was changedA newer Dockflow changed this environment or this server, and recorded something this version does not know: a CI pinned to an older version, after a deploy with a newer one
130Interrupted or declinedThe run was cancelled and rolled back, or never startedCtrl+C or dockflow cancel (run.cancelled), answering no at a confirmation prompt (run.declined), detaching with a second Ctrl+C (run.detached). See cancelling

dockflow exec, dockflow run and dockflow ssh exit with the exit code of the command they ran. When they cannot start it, they use the codes above.

In CI, two runs that may overlap (a push to main and a release tag) do not need to fail with code 5: dockflow deploy --wait-lock 10m waits for the other run's lock instead.

Error format

Every error Dockflow understands carries three parts:

PartDescription
codeA stable, dotted identifier, such as lock.held or ssh.host_key_mismatch. Scripts may match on it; it does not change between versions. A code a script does not know is handled by its exit code.
messageWhat went wrong, in one sentence. Secret values in it are replaced by ***.
hintWhat to do about it. Not every error has one.

Human output

On a terminal, the error goes to standard error, with the hint on a second line:

error: Compose: variable API_KEY is not defined: error while interpolating services.web.environment.API_KEY: variable API_KEY is not defined
hint: define it under environments.<env>.vars or as a secret, or write ${API_KEY:-default}

JSON output

With --json, an error found by the CLI is printed on standard output as one JSON document, with the exit code:

{"error":{"code":"config.invalid","message":"Compose: variable API_KEY is not defined: error while interpolating services.web.environment.API_KEY: variable API_KEY is not defined","hint":"define it under environments.<env>.vars or as a secret, or write ${API_KEY:-default}"},"exit_code":3}

The hint field is left out when the error has none.

When the error happens during the agent's run on the server, it is already in the event stream, in the error field of the run.finished event. The CLI does not print it a second time.

Errors from the argument parser itself (an unknown command or flag) are printed as error: … on standard error, also with --json, and exit with code 2.

Error codes

Each error code below maps to the exit code given in its row. A few errors use a generic code for their class, and their message says what went wrong: config.invalid (3), usage.invalid (2), connect.failed (4) and internal (1). A script that meets a code it does not know can act on the exit code.

Configuration and usage

CodeExitWhat it meansWhat to do
config.invalid3dockflow.yml, Compose, a secret or a variable is wrong. The message names the file and the key.Fix what the message names. A missing variable needs a value in vars, a secret, or a default (${X:-default}). Run dockflow validate to check without a server.
config.newer3dockflow.yml is in a newer format version, or its requires names a later Dockflow than this one.Upgrade Dockflow.
secrets.file3The environment's SOPS file cannot be decrypted.Give the age key: SOPS_AGE_KEY in CI, or the key file SOPS reads on your machine.
usage.invalid2A bad argument or flag, or an environment prefix that matches several environments.Check dockflow <command> --help.
init.exists2dockflow init would replace a file that exists.Run it with --force, or move the file.
run.unknown2dockflow attach was given a run that is not on the server.Only the 10 most recent finished runs are kept.

Connecting and provisioning

CodeExitWhat it meansWhat to do
ssh.host_key_unknown4The server's host key is neither pinned in the connection string nor in ~/.ssh/known_hosts. Dockflow never trusts a key on first use.Check the fingerprint on the server with ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub, then pin it (host_key: in dockflow.yml, or --host-key for dockflow server connection).
ssh.host_key_mismatch4The server's host key is not the pinned one. Either the server was reinstalled, or something sits between you and it.Verify the new key on the server itself, then print the connection string again with dockflow server connection and update the secret. Do not update it without checking.
connect.failed4The server cannot be reached, SSH authentication failed, or the Docker API does not answer.Check the host, port, user and key. Check that Docker runs on the server and that the user can use its socket.
server.docker_too_old4The server runs Docker older than 27.Upgrade Docker on the server.
agent.no_binary4A development build of the CLI has no release to download the agent binary from: it does not run on Linux with the server's architecture, or it is linked dynamically (built with cgo), and the agent's image holds no C library.Build it with CGO_ENABLED=0 (make build does), or set DOCKFLOW_AGENT_BINARY to a static dockflow binary for the server's architecture.
agent.download_failed4The agent binary for the server could not be downloaded from the release.Check that this machine reaches GitHub (or your mirror in DOCKFLOW_RELEASES_URL), or set DOCKFLOW_AGENT_BINARY.
agent.checksum_mismatch4The downloaded agent binary does not match the checksum the CLI was built with (the release's SHA256SUMS for a CLI built without one). Nothing was sent to the server.Try again. Report it if it persists.
provision.sudo4server init connected as a user that cannot use sudo without a password.Connect as root, or give that user passwordless sudo for the time of server init.
provision.no_docker4Docker is not installed on the server, and --skip-docker-install was given.Install Docker 27 or later, or drop --skip-docker-install.
provision.unsupported_os4Docker is not installed, and the server is not Debian or Ubuntu, where Dockflow installs it.Install Docker 27 or later yourself, then run server init again.
provision.docker_too_old4The server's Docker is older than 27, or not running.Upgrade Docker Engine to 27 or later and start it.
provision.no_nginx4--nginx was given, nginx is not installed, and the server is not Debian or Ubuntu, where Dockflow installs it.Install nginx yourself, then run server init again.
provision.docker_key4The key download.docker.com gave is not Docker's signing key (fingerprint 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88), or comes with another key. Docker is not installed.Something between the server and download.docker.com changed the key: install Docker yourself from a source you trust, then run server init again.
provision.no_ssh_server4server init without a target, on the server itself, found no SSH host key: no SSH server is installed.Install and start OpenSSH, then run server init again.
provision.failed4A step of the provisioning script failed.The output above says which step. Every step can be run again safely.

Locks

CodeExitWhat it meansWhat to do
lock.held5Another run (named in the message, with who started it and when) is working on this environment.Wait for it, or retry with --wait-lock <duration>.
lock.manual5Someone holds the environment's lock by hand, with dockflow lock acquire. The message says who and why.Run dockflow lock release when the maintenance is over, or deploy anyway with --force.

Planning

Nothing was changed on the server.

CodeExitWhat it meansWhat to do
plan.stale6The server changed between the plan you confirmed and the apply.Run the command again to compute a new plan.
plan.image_missing6The server cannot pull an image, or it is absent with pull_policy: never.Check the image name, and that the server can reach the registry and is logged in to it.
plan.pull_policy6A service's pull_policy is one the server cannot apply.Use missing, always or never.
plan.caddy6A route's raw caddy directives do not parse.Fix them: they are Caddyfile directives, as inside a site block.
plan.domain_conflict6A domain is routed by two app environments, at any paths, or a domain and path by two services. Domains are compared lower-cased without a trailing dot, paths without a trailing slash.A domain belongs to one app environment, and a domain and path to one service: change one of them.
plan.domain_invalid6A route stored on the server has a domain that is not a host name.Deploy the app that owns it again, with a valid domain.
plan.no_switch6The app declares routes, and the server has no switch.Run dockflow server init.
plan.routes_without_switch6The server was set up with --edge none, and the app declares routes.Publish ports instead, or run dockflow server init --edge builtin.
server.routes_in_use6--edge none was asked for, and routes still go through the switch.Remove the routes of the apps on this server first.
plan.network_missing, plan.volume_missing6A network or volume marked external: true does not exist on the server.For a shared network, run the dockflow server network create the hint gives; otherwise create it on the server, or remove external: true.
plan.volume_driver6A volume's driver is not a volume plugin the server has.Run dockflow server plugin install <plugin> --alias <driver>.
plan.network_foreign6A network of the app's name exists on the server but belongs to something else: a Docker Swarm stack, another Compose project, or a network created by hand. Dockflow never takes it over.Remove it once nothing uses it (docker stack rm for a Swarm stack), or give the network another name in compose.yml.
plan.only_dependency6--only names a service that depends on one that is not deployed.Add that service to --only.
plan.dependency_removed6destroy --only would remove a service that another one depends on.Destroy them together.
plan.stateful_replicas, plan.container_name_replicas6dockflow scale was asked for several replicas of a service that runs once.Stateful services and services with a container_name keep one container.
secret.not_tmpfs6/run/dockflow on the server is not a tmpfs, so Compose secrets, or the backups' password and storage credentials, cannot be written to memory.Mount a tmpfs on /run/dockflow (systemd mounts one on /run). dockflow server check checks it.
secret.missing, config.missing6A rollback needs a secret or config file that is gone from the server (a reboot empties the tmpfs), and this run does not have its value.Deploy the current version: the CLI sends the values it has.
network.in_use, plugin.in_use6A shared network has containers, or a volume plugin has volumes.Remove them from the apps and deploy, or destroy the environments that use them, first.
network.exists, network.not_shared, plugin.exists, plugin.install6The network's name belongs to a network that is not shared, the plugin's name is taken, or the plugin cannot be pulled or enabled.Choose another name or --alias; check the plugin's name and that the server reaches its registry.
rollback.no_previous, rollback.unknown_release6There is no release to roll back to, or the one asked for is no longer kept.List the releases with dockflow releases.
rollback.current6The release or version asked for is the current one.Nothing to do, or pick another with --to.
rollback.not_deployable6The release asked for never went live: it was rolled back, failed or abandoned.Pick a release whose status is active or superseded in dockflow releases.
env.never_deployed6The command needs a deployed release, and the environment has none.Deploy it first.

Building and pushing

Nothing was changed on the server.

CodeExitWhat it meansWhat to do
build.failed7The image build failed.The build output above the error says why. With -v, every build line is shown.
build.no_buildx7docker buildx cannot run on this machine.Install Docker with the buildx plugin.
build.no_builder7The buildx builder cannot export images.Enable the containerd image store, or create a docker-container builder.
build.no_podman7build.engine: podman is set, and Podman cannot run.Install Podman on this machine.
build.not_pushed7deploy --skip-build found no image of a service for this version in the registry.Build and push it first with dockflow build --push, for the same version, or deploy without --skip-build.
push.failed7An image cannot be pushed to the server's registry or to your external registry.Check that the registry is reachable and that the credentials can push.

During the rollout

CodeExitWhat it meansWhat to do
health.failed8A service's containers did not become healthy: they crashed, exited, failed their Docker health check, or did not answer in time. The previous release was restored.Read the service's logs with dockflow logs <env> <service>, and run dockflow diagnose.
health.check_failed8 or 9An HTTP health check did not get the expected status after its retries.Check the URL and the expected status. on_failure decides whether the deploy rolls back (8) or stays in place (9).
job.failed, job.timeout8A job exited with a non-zero code, or did not finish within the deploy timeout.Its output is in the stream above. If the job needs longer, raise environments.<env>.deploy.timeout.
hook.failed11 or 8A fatal hook failed: before anything changed (11), or an after_deploy hook once the release went live (8, and the previous release is restored). A non-fatal hook only prints a warning.Its output is in the stream above.
upload.failed8An upload could not be written on the server.Check the destination path and its permissions.
engine.port_in_use8 or 1A container cannot start because a host port it publishes is in use, by another container or a process of the server. In a deploy, the previous release was restored (8).Free the port on the server, or publish another one.
state.commit_failed8Everything was ready, but the release could not be recorded as live on the server (a full disk, an I/O error), so the previous release was restored.Check the disk of the server (df -h /var/lib/dockflow), then deploy again.
rollback.failed9The run failed, and restoring the previous release failed too. The environment is degraded.Fix the cause and deploy again. dockflow status and dockflow diagnose show the state.
run.failedthe run'sThe agent's run failed without an error of its own; run.finished and the exit code say how it ended.Read the events above run.finished.

Exit code 8 means the deploy failed but your users still see the previous release. Exit code 9 means they may not: the environment is left as the failed run put it. Treat 9 as the one that pages someone.

Cancelling

Exit code 130 covers three cases, told apart by the error code.

CodeExitWhat it meansWhat to do
run.cancelled130The run was cancelled (Ctrl+C or dockflow cancel) and rolled back.Nothing: the previous release is running.
run.declined130You answered no at the confirmation prompt.Nothing was changed.
run.detached130A second Ctrl+C detached the CLI while the agent rolls back on the server.Follow it with dockflow attach <env> <run>.

Checks

CodeExitWhat it meansWhat to do
check.failed9dockflow server check found a failure on the server.Each failed check says what to do.
diagnose.findings9dockflow diagnose found problems; the message says how many.Each finding is listed above it.

Backups

CodeExitWhat it meansWhat to do
backup.none2The service named does not exist in the current release, or declares no backup.Set x-dockflow.backup on it, and deploy.
backup.failed12The backup of a service failed; its reason is in a warning above. A failed dump keeps no snapshot.Read the dump's error.
backup.not_running12The service has no running container to dump.Start it with dockflow restart.
backup.repository12The repository cannot be opened or listed.Check backups.repository, the credentials in backups.env, and the password.
backup.snapshot2 or 12No snapshot to restore, or none matches --snapshot.dockflow backup list shows the snapshots.
backup.restore_failed12The restore failed; the message gives the restore command's error. A Postgres restore is one transaction: nothing of it stays.Read the error, fix the target, and restore again.
backup.retention12backup prune could not apply a retention.Read restic's error in the message.
backup.check12The repository failed restic check.Run restic check on the repository to see its errors.

Server state

Nothing was changed.

CodeExitWhat it meansWhat to do
state.newer13A newer Dockflow changed this environment (or this server) and recorded fields this version does not know. It can read the state, not change it.Upgrade Dockflow to the version the server was last changed with, in CI too.
state.schema13The server's state is in another format version, written by another major release of Dockflow.Use the Dockflow that wrote it, or a later one of the same major.

Unexpected errors

These exit with code 1. Most come from the server's Docker or files, not from your project. When the cause is not clear from the message, report it.

CodeExitWhat it meansWhat to do
internal1An error Dockflow did not expect.Report it, with the output of the command run with -v.
agent.died1The agent stopped before finishing.The watchdog, or else the next run, finds the unfinished release: it restores the previous release, or completes the new one if it was already live. dockflow releases shows which.
agent.protocol1The agent's last event cannot be read.Report it. Check that the agent's output is not cut off on the server (disk full, killed container).
engine.api1The server's Docker refused a request; the message says which, with Docker's error.Read Docker's error. Check the server's disk and the Docker daemon.
state.io, state.corrupt1A file under /var/lib/dockflow cannot be read or written, or is not a valid state file.Check the server's disk. A corrupt file is named in the message.
state.missing_release1The environment's current release record is missing from the server.Deploy again to write a new release.
switch.unreachable1The switch's admin API does not answer.Check the dockflow-switch container on the server, or run dockflow server init again.
switch.config_refused1The switch refused the configuration Dockflow sent it.Report it, with the message.
server.start_failed1The switch, the registry or the watchdog cannot start.The hint says what to check, usually a port already in use.
filter.no_iptables, filter.failed1The port filter cannot be applied: iptables or ip6tables is missing on the server, or refused the rules.Install iptables on the server, or run dockflow server init --no-port-filter.
filter.inert1Docker does not send forwarded traffic through DOCKER-USER: with its nftables firewall backend, or "iptables": false, the port filter would let everything in.Use Docker's iptables backend, or run dockflow server init --no-port-filter and filter the ports yourself.
filter.ipv61The server has an IPv6 address, but the port filter's IPv6 rules cannot be applied: published ports would stay open over IPv6.Install ip6tables on the server, or turn IPv6 off.
prune.registry1The server-local registry's garbage collection failed.Read the registry's error in the message.

Two codes never reach you as errors:

  • run.abandoned is recorded in the error of a release whose agent died, and shown by dockflow releases;
  • command.exit carries the exit code of the command exec, run or ssh ran.

Warning codes

Warnings appear as warning events with a code. They never change the exit code. New codes may appear in a minor release.

CodeMeaning
compose.warningA Compose key that has no effect on one server, or that Dockflow ignores (the message says which)
hook.failedA hook that is not fatal failed
health.check_skippedAn internal HTTP check was skipped, because the service is on no network of the app
notify.failedA webhook could not be delivered. Webhooks never change the outcome of a run.
run.abandoned, run.leftover, run.restartedAn earlier run stopped before finishing; this run marked its release abandoned, removed its leftover containers or restarted the containers it had stopped
run.unfinishedThe latest release failed and was left in place (on_failure: fail, --no-rollback): this run rolls it back to the current release before its plan, or, from dockflow plan, the next run will
cleanup.failed, destroy.network, state.write_failedA cleanup step (old containers, images, registry manifests, release files, networks) or a state write failed; it does not change the outcome of the run
backup.nonebackup run found no service of the current release that declares a backup: nothing was backed up
backup.password_generatedThe backups' password was generated and stored on the server: keep a copy elsewhere
backup.resumeA volume backup could not start the service again, or run its post command
agent.signal_failedThe CLI could not forward Ctrl+C to the agent
upgrade.pullserver upgrade could not pull an image; the server keeps the one it has
uninstall.network, uninstall.volume, uninstall.filterserver uninstall could not remove a network, a volume or the port filter's rules; the message says which
backup.failed, backup.retentionIn backup run, the backup or the retention of one service failed (level error); the others go on, and the command exits 12
secret.copyThe copy of a secret kept on the server for rollbacks could not be written: a rollback after a reboot will need the value again
filter.failedThe port filter could not be applied after the run; the message says why
cloudflare.rangesCloudflare's IP ranges could not be downloaded; the previous ones, or this version's own list, are used
plan.no_limitsThe server runs other app environments, and services of this one set no deploy.resources.limits
notify.detacheddeploy --detach sends no webhook: the CLI sends them when the run ends
watchdog.failedThe watchdog could not check or restart a container; in the watchdog's own output
watchdog.recoveredThe agent of a run stopped before the run finished (killed, out of memory, Docker restarted), and the watchdog recovered the environment at once, as the next run would have; in the watchdog's own output
Edit on GitHub

On this page