postgres-provisioner
Declarative, idempotent provisioning for PostgreSQL. Describe the roles, databases, schemas, privileges, extensions, migrations and publications your services need in one YAML file; the provisioner makes the server match it, every time it runs, and then exits.
It is built to run before your application starts: as a Docker Compose service your app
depends_on, or as a Kubernetes Job or init container.
Every service needs the same handful of things from Postgres: a login role, a database it
owns, a schema, the right grants, maybe pgcrypto, and a read-only account for reporting or
monitoring. Usually that ends up in an init.sql that only runs on an empty data directory,
in a pile of manual psql sessions, or in the application itself running with far more
privileges than it needs.
postgres-provisioner replaces that with a file you can review in a pull request:
- Idempotent — run it on every deploy. Existing resources are reconciled, not recreated.
- Additive and safe — it never drops roles, databases, schemas, tables or grants that
aren’t in the config. Removing privileges only happens through explicit
revokes. - Secret-friendly —
${VAR}references,*_FILEsecrets (Docker/Kubernetes), and passwords are redacted from every log line. - Previewable —
--dry-runlogs every statement it would execute. - Small — a single static binary in a
scratchimage.
Quick start
Section titled “Quick start”Two files next to each other. A config:
roles: - name: shop_app password: ${SHOP_APP_PASSWORD} options: login: true
databases: - name: shop owner: shop_appAnd a Compose file that runs the provisioner once Postgres is healthy:
services: postgres: image: postgres:16-alpine environment: POSTGRES_USER: admin POSTGRES_PASSWORD: adminpass healthcheck: test: ["CMD-SHELL", "pg_isready -U admin"] interval: 2s timeout: 5s retries: 10
postgres-provisioner: image: ghcr.io/datarocks-ag/postgres-provisioner:2.0 depends_on: postgres: condition: service_healthy environment: POSTGRES_HOST: postgres POSTGRES_USER: admin POSTGRES_PASSWORD: adminpass PGHELPER_CONFIG_PATH: /config.yaml SHOP_APP_PASSWORD: shop-app-secret volumes: - ./config.yaml:/config.yaml:ro$ docker compose up postgres-provisioner...{"level":"INFO","msg":"Creating role","role":"shop_app"}{"level":"INFO","msg":"Creating database","database":"shop"}{"level":"INFO","msg":"postgres-provisioner finished successfully"}Run it again and nothing is recreated. That’s the whole idea. The getting started guide takes this example further: schemas, migrations, grants, a read-only user, and wiring your application to it.
Documentation
Section titled “Documentation”Read in this order if you’re new; jump straight to a how-to if you know what you need.
| Getting started | A guided tour: from an empty Postgres to an application database with migrations and a read-only user. |
| How it works | The model behind a run: what gets reconciled, what is never touched, the order of steps, strategy, dry-run. |
| How-to guides | Task-focused stories: |
| Monitor Postgres with postgres_exporter | A least-privilege pg_monitor account instead of the superuser. |
| Read-only and reporting users | Schema-wide, table- and column-level read access. |
| Harden a database | Revoke PostgreSQL’s PUBLIC defaults and grant access explicitly. |
| Manage secrets | Docker secrets, Kubernetes secrets, password rotation. |
| When the application runs its own migrations | Grants for tables the provisioner doesn’t create. |
| Change data capture with publications | Logical replication for Debezium, Redpanda Connect and friends. |
| Deployment | Docker Compose, Kubernetes, binaries, environment variables, CLI flags, required privileges. |
| Configuration reference | Every field of the YAML file. |
| FAQ | Questions people ask after their first week. |
| Upgrading to 2.0 | Breaking changes and how to migrate from 1.x. |
| Changelog | What changed in each release. |
A complete, commented config lives in config.example.yaml, and docker-compose.yaml runs it against a local Postgres.
Development
Section titled “Development”make build # Build binarymake test # Run unit testsmake test-integration # Run integration tests (requires Docker)make lint # Run golangci-lintmake vet # Run go vetmake fmt # Format the codemake docker # Build Docker imageThe integration tests start PostgreSQL 16 with testcontainers.
Branches and releases: work happens on feature/, bugfix/ and hotfix/ branches merged
into develop through pull requests. A release gets a release/x.y.z branch with a
“Cut CHANGELOG x.y.z” commit, a vx.y.z tag (which builds binaries and images), and is merged
back into develop.
Docs for v2.0.0 · built from the release tag