Add a database
Declare it
Section titled “Declare it”services: postgres: 17That is sufficient as a declaration. Onebox supplies the image, a durable volume, a health check, a credential generated on the server, and the connection details your application reads. You supply no password, no port, and no data path.
Declaring it does not start it. Services live outside the release lifecycle, so
ob deploy wires up connections but never creates or replaces a service
container — that would make every deploy a potential database restart. Apply the
service explicitly:
ob service apply --output ndjsonOn a host you have not bootstrapped yet, ob bootstrap does this for you. If you
skip it on a live application, the next deploy stops at preflight with
service "postgres" is not running — start it with ob bootstrap or ob service apply.
The service’s name is its driver unless driver says otherwise, so a second
Postgres is:
services: postgres: 17 events: {driver: postgres, version: 17} cache: {driver: redis, version: "7.4"}Eleven drivers are supported: postgres, mysql, mariadb, redis, valkey,
mongodb, rabbitmq, minio, meilisearch, clickhouse, nats. Anything
else is refused with unknown_service_driver — guessing an image from a name
produces a container that starts and stores nothing durable.
Wire the connection
Section titled “Wire the connection”A workload that declares needs receives <SERVICE>_URL and its parts:
_HOST, _PORT, _USER, _PASSWORD, _DATABASE.
workloads: web: role: application image: ghcr.io/acme/shop:1.4.0 needs: - {name: postgres, condition: healthy}services: postgres: 17workloads: n8n: role: application image: docker.n8n.io/n8nio/n8n:1.70.0 needs: - name: postgres env: DB_POSTGRESDB_HOST: host DB_POSTGRESDB_USER: user DB_POSTGRESDB_PASSWORD: passwordservices: postgres: 16The right-hand side is a connection part: url, host, port, user,
password, database. A part the driver does not have — a database on a cache —
is omitted rather than written empty.
needs also decides release order when deployment.order is absent.
Three properties that always hold
Section titled “Three properties that always hold”- A service outlives every release. It runs in its own Compose project, so no deploy and no rollback can stop it or remove its volume.
- Its credential is generated on the server, once. Not in your project, not in the generated runtime, not in the digest. Never rotated by a re-apply.
- Its version binds into the release digest, so a database upgrade under an untouched application cannot pass unnoticed.
Reaching a pooler or a read replica
Section titled “Reaching a pooler or a read replica”Connections own the credential, not the endpoint. Map only the parts you want and author the host yourself:
needs: - name: postgres env: DB_USER: user DB_PASSWORD: passwordenv: DB_HOST: pgbouncer.internalAn application that reads a single connection URL cannot do this, because the URL carries the credential and the credential never travels.
When to use a daemon workload instead
Section titled “When to use a daemon workload instead”services | daemon workload | |
|---|---|---|
| Image | Onebox picks it from driver + version | You name it |
| Credential | Generated on the server, once | Yours to supply |
| Release coupling | Outlives every release | Recreated on deploy |
| Connection wiring | Automatic via needs | You wire it |
| Available for | 11 drivers only | Anything containerised |
Use a daemon when you need something outside the driver set, or a topology the driver does not provide.
Full field list: services.