deployer

deployer.toml reference

An optional file at the root of the repository that tells the deployer how to run your compose project: which service gets the traffic, how it is health-checked and switched, and what runs before a release. Unknown keys are errors (with line and column), so typos never pass silently.

The smallest useful file

[web]
service = "app"
port = 8080
healthcheck = "/up"

Without the file, a single label deployer.expose: "8080" on the web service does the same ([web] service + port, health check /). If both exist and disagree, the deploy fails with a clear error.

Every key

# deployer.toml at the root of the repository. Every key is optional;
# [web] is required unless a compose service has the label deployer.expose.
version = 1
compose = "compose.yaml"            # only needed to override the auto-detection

[web]
service = "app"                     # the compose service that receives HTTP traffic
port = 8080                         # its plain HTTP port inside the container
healthcheck = "/up"                 # GET this path; 2xx/3xx = healthy
healthcheck_timeout = "3s"          # per request
deploy_timeout = "60s"              # start -> healthy
drain_timeout = "30s"               # the old version keeps running for open requests
stop_timeout = "10s"                # SIGTERM -> SIGKILL
strategy = "bluegreen"              # or "recreate" (stop old, then start new)

[release]                           # runs once in the NEW image before traffic moves
command = ["bin/rails", "db:migrate"]
timeout = "10m"

[build]
pull = "newer"                      # missing | newer | always (base images)
secrets = ["NPM_TOKEN"]             # env vars flagged "build", for RUN --mount=type=secret,id=NPM_TOKEN

[services.worker]
strategy = "recreate"               # default for services with build:

[services.redis]
strategy = "keep"                   # default for image-only services
KeyDefaultAllowedMeaning
version11Schema version; other values are rejected.
composeauto-detecteda path in the repositoryUse another compose file, e.g. deploy/compose.prod.yaml.
profilesnonecompose profile namesServices with a profile run only if it is listed here.
[web] servicefrom the labela compose serviceThe service that receives HTTP traffic. It runs in two slots for zero-downtime switches.
[web] portfrom the label1–65535Its plain HTTP port inside the container. ports: in compose are ignored.
[web] healthcheck/ (any answer below 500)a pathProbed before traffic moves; with a path, only 2xx/3xx count. More.
[web] healthcheck_timeout3s1s – 30sPer probe request.
[web] deploy_timeout1m10s – 15mFrom container start until healthy; otherwise the deploy fails and the old version keeps serving.
[web] drain_timeout30s0s – 10mHow long the old version keeps running after the switch, for open requests.
[web] stop_timeoutcompose stop_grace_period, else 10s1s – 2mBetween SIGTERM and SIGKILL.
[web] strategybluegreenbluegreen, recreaterecreate stops the old version before starting the new one (short downtime) for apps that must never run twice.
[release] commandnonea command listRuns once in the new image before traffic moves (migrations). A failure stops the deploy.
[release] timeout10m1m – 1h
[build] pullnewermissing, newer, alwaysWhen base images are pulled. newer picks up a patched node:22 on the next deploy.
[build] secretsnonevariable namesVariables flagged build that the build may read with RUN --mount=type=secret,id=NAME. Never use build args for secrets: they stay in the image.
[services.NAME] strategyrecreate with build:, keep otherwiserecreate, keeprecreate: restarted on the new image after a successful switch. keep: left running unless its definition changes (databases).
[services.NAME] no_new_privilegestruetrue, falseOpt a service out of the no-new-privileges hardening (only if it needs setuid binaries).

Durations are written like 90s, 5m, 1m30s. The file may be at most 64 KiB.

What is deliberately not in this file

Domains, environment variables and secrets, the branch, memory/CPU limits and release retention are set in the deployer, not in the repository: anyone with push access must not be able to claim a domain, read secrets or give the project more resources.