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"}Install PostgreSQL extensions
Section titled “Install PostgreSQL extensions”PostgreSQL 18 uses ghcr.io/labstack/onebox-postgres:18. No optional extension
is installed by default. Declare the extensions the application requires and
Onebox installs them in its managed database before application migrations run:
services: database: driver: postgres version: 18 features: extensions: pg_trgm: {} vector: {}The application does not need a separate CREATE EXTENSION migration. Onebox
creates only missing extensions; it never upgrades or removes an installed
extension during an ordinary apply. An extension absent from the selected image
is reported before application migrations start. A deploy automatically
converges a database with declared extensions before running migration jobs.
The PostgreSQL 18 image includes the common built-in extensions plus a curated
2026 set: pgvector, pgvectorscale, PostGIS, pg_cron, pgAudit, pg_repack,
pg_partman, and HypoPG. Onebox automatically configures required preload
libraries for pg_cron, pgAudit, and pg_stat_statements. Optional PostGIS
extensions such as postgis_topology are available through the same declaration.
Use the PostgreSQL extension name in the declaration. For example, the pgvector
project provides vector, while pgvectorscale provides vectorscale. Declaring
vectorscale automatically establishes its vector dependency:
features: extensions: vectorscale: {}TimescaleDB is not bundled in this first distribution. Its Apache-only package currently creates a maintenance job that repeatedly fails, while its Community package has a license unsuitable for a general customer database image. Use a dedicated TimescaleDB service until the upstream Apache package is clean.
An existing backup-protected PostgreSQL 18 service may still be pinned to the older official image. Migrate it deliberately: disable backup while the old project file is still active, add the extension declaration and deploy, then enable backup again:
ob backup disable database --confirm database# add features.extensions to onebox.ymlob deployob backup enable databaseThe off-host backup repository is retained throughout this one-time image transition.
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.