Skip to content

Add a database

services:
postgres: 17

That 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:

Terminal window
ob service apply --output ndjson

On 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"}

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:

Terminal window
ob backup disable database --confirm database
# add features.extensions to onebox.yml
ob deploy
ob backup enable database

The 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.

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: 17

The 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.

  • 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.

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: password
env:
DB_HOST: pgbouncer.internal

An application that reads a single connection URL cannot do this, because the URL carries the credential and the credential never travels.

servicesdaemon workload
ImageOnebox picks it from driver + versionYou name it
CredentialGenerated on the server, onceYours to supply
Release couplingOutlives every releaseRecreated on deploy
Connection wiringAutomatic via needsYou wire it
Available for11 drivers onlyAnything containerised

Use a daemon when you need something outside the driver set, or a topology the driver does not provide.

Full field list: services.