The database¶
Muster keeps everything in PostgreSQL 14 or newer, and nothing else stateful: no Redis and no queue. At start Muster
refuses an older PostgreSQL and stops with an error that names the cause. It needs one extension, pg_trgm, which ships
with PostgreSQL and which the database owner can create (a trusted extension since PostgreSQL 13); the first migration
creates it. Neither the Helm chart nor the compose example backs up the database: see
Backup and restore, and keep the master key apart from the backups.
As a URL or as fields¶
Give the database to Muster in one of two forms. The settings are in the environment reference.
- A URL
MUSTER_DATABASE_URL, such aspostgres://muster:<password>@db.example.org:5432/muster?sslmode=require- Fields
MUSTER_DATABASE_HOST,MUSTER_DATABASE_PORT(default5432),MUSTER_DATABASE_NAME,MUSTER_DATABASE_USER,MUSTER_DATABASE_PASSWORDorMUSTER_DATABASE_PASSWORD_FILE,MUSTER_DATABASE_SSLMODE(defaultprefer)
When both forms are set, the URL wins and Muster logs a warning that names both. Muster ignores every PG* variable,
such as PGHOST, PGSSLMODE, PGSSLROOTCERT, PGOPTIONS and PGPASSFILE, and the password file ~/.pgpass. Give
any other libpq setting as a parameter of the URL instead, such as sslrootcert, options or target_session_attrs.
Only libpq's default certificate files still apply when the URL names none: ~/.postgresql/root.crt, and
~/.postgresql/postgresql.crt with postgresql.key; these are files, not variables, and the Muster image has none. Any
secret can come from a *_FILE variable.
Prefer the fields when the host, user and name are plain values and only the password is a secret: the Helm chart does
exactly that, with database.host, database.port, database.name, database.user and database.sslmode as values
and the password from a key of the Secret as MUSTER_DATABASE_PASSWORD_FILE. Use the URL when you need a setting that
has no field, such as pool_max_conns or sslrootcert; in the chart, database.urlKey names the key of the Secret that
holds the whole URL, and the field values are then ignored.
A replica's main pool holds max(10, CPUs) connections unless the URL sets pool_max_conns. On top of that, each
replica keeps about three session connections (see PgBouncer). Size max_connections of PostgreSQL for all
replicas together.
Create the database and its user before the first start. The user owns the database, because Muster creates its tables, indexes and partitions itself:
CREATE ROLE muster LOGIN PASSWORD '<password>';
CREATE DATABASE muster OWNER muster;
Muster applies its migrations at the first start: the compose example sets MUSTER_MIGRATE_ON_START, and the chart runs
muster migrate in an init container.
CloudNativePG¶
CloudNativePG runs PostgreSQL clusters on Kubernetes. A Cluster for Muster creates the
database and its owner:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: muster-db
namespace: muster
spec:
instances: 3
storage:
size: 10Gi
bootstrap:
initdb:
database: muster
owner: muster
CloudNativePG serves the primary at the Service muster-db-rw (and the replicas read-only at muster-db-ro) and enables
TLS on its own. It generates the password and keeps it in the Secret muster-db-app, with the keys username,
password, host, port, dbname and uri among others. The chart reads the database from that Secret when you name
it in database.existingSecret; the Secret muster of Install with Helm then holds only
the master keys and the password of the first Admin:
existingSecret: muster
database:
existingSecret: muster-db-app
urlKey: uri
The uri key holds a URL such as postgresql://muster:<password>@muster-db-rw.muster:5432/muster. It names no
sslmode, so Muster connects with its default, prefer, which uses the TLS of CloudNativePG but does not insist on
it. To require TLS, give the connection as fields and take only the password from the Secret, which also keeps the
password out of the pod's environment:
existingSecret: muster
database:
existingSecret: muster-db-app
host: muster-db-rw.muster.svc
name: muster
user: muster
sslmode: require
passwordKey: password
When you give the Cluster a Secret of your own under bootstrap.initdb.secret, CloudNativePG generates no
muster-db-app, and your Secret holds no uri: use the fields form above, with your Secret in database.existingSecret
and passwordKey: password.
Add an spec.backup section to the Cluster (see the CloudNativePG documentation) and test a restore, together with the
master key, before you rely on it.
Managed PostgreSQL¶
A managed PostgreSQL service works when it offers version 14 or newer and a role that owns an empty database. Check:
- Version. PostgreSQL 14 or newer. Muster is tested on 14 and 17.
- Extensions. Only
pg_trgm; allow it where the service keeps an allow-list of extensions. Muster does not usepg_partman; it creates its partitions itself. - Session features. Muster needs session advisory locks and
LISTEN/NOTIFY. Plain endpoints of managed services provide them. A pooler endpoint that your provider adds in transaction mode does not: see PgBouncer. - Failover. Name the endpoint that follows the primary, not an instance.
- TLS. Most services require it. Set
sslmodeas below. - Connections. Check the connection limit of the plan against the pool sizes above.
Run muster doctor against the database after you set it up. It checks the version, the session features, the pooler
mode and whether each connection is encrypted.
PgBouncer¶
Muster uses two kinds of database connections:
- The main connection carries every query. It may go through PgBouncer in transaction mode.
- The session connection carries what needs a real session: the advisory lock of the Leader, the
LISTENconnection that wakes the workers and feeds live updates to the web UI, partition maintenance and the migrations. A transaction pooler would lose their session state between statements.
Without a separate setting, the session connection is the main connection. At start, Muster takes an advisory lock over
it and completes a LISTEN/NOTIFY round trip. Behind a transaction pooler this fails within five seconds, and Muster
stops with an error that names MUSTER_DATABASE_SESSION_URL.
So when the main connection goes through PgBouncer in transaction mode, give Muster a session connection that reaches PostgreSQL directly or through a pooler in session mode:
MUSTER_DATABASE_SESSION_URL- A whole URL for the session connection.
MUSTER_DATABASE_SESSION_HOSTMUSTER_DATABASE_SESSION_PORT- Only the host and the port; the user, password, database and
sslmodeare those of the main connection.
In the chart, these are database.sessionHost and database.sessionPort. With CloudNativePG, the main connection can
use a Pooler object in transaction mode, whose Service takes the Pooler's name, here muster-db-pooler-rw, and the
session connection the -rw Service of the Cluster:
database:
host: muster-db-pooler-rw.muster.svc
sessionHost: muster-db-rw.muster.svc
sessionPort: 5432
PgBouncer 1.21 or newer
Muster's driver uses prepared statements on the main connection. In transaction mode, PgBouncer supports them from
version 1.21, and only when max_prepared_statements is above 0. An older PgBouncer, or a value of 0, makes
queries fail with errors about missing prepared statements. Set max_prepared_statements to a few hundred, such as
200. A pooler in session mode, or no pooler, needs no such setting.
PgBouncer is optional. Most installations connect straight to PostgreSQL, and then no session setting is needed.
TLS¶
MUSTER_DATABASE_SSLMODE (or sslmode in the URL) takes the values of PostgreSQL's client library:
disable- encrypts: never · checks the server: nothing
allow- encrypts: only when the server insists · checks the server: nothing
prefer(default)- encrypts: when the server offers TLS, else falls back to plain text · checks the server: nothing
require- encrypts: always · checks the server: nothing: protects against listening in, not against an impostor
verify-ca- encrypts: always · checks the server: the certificate chain against a trusted CA
verify-full- encrypts: always · checks the server: the chain, and that the certificate names the host you connect to
prefer makes a fresh installation work, but it also connects without encryption to a server that has none. Muster tells
you: muster doctor reports each connection as encrypted or not, and a start with an unencrypted connection logs a
warning. For anything but a single machine, set require at least, and verify-full where you can: the certificate then
proves that the server is yours.
verify-ca and verify-full check against the file named by sslrootcert in MUSTER_DATABASE_URL, which must exist
in the container; without it, against ~/.postgresql/root.crt when that file exists, and otherwise against the root
certificates of the system. The fields form has no such setting. With
MUSTER_DATABASE_SESSION_HOST and MUSTER_DATABASE_SESSION_PORT, the session connection takes the sslmode of the
main connection; a MUSTER_DATABASE_SESSION_URL carries its own, prefer when it names none.