Database dumps
Each cluster backup first dumps every instance of the configured engines (backup-cluster-<engine>) into borg.dbBackupLocation/<instance>/, which the cluster backup then archives. <instance> is the instance's name (the *.conf file name of a static config).
An engine is configured when databases.<engine>.enabled is true and it has at least one instance or an existingSecret; an enabled engine with neither fails the render. Only configured engines get their credentials mounted, their backup-cluster-<engine> published to the cluster agent and attached as a pre-backup hook (plan mode), or run in the cluster CronJob (the chart passes them as BORG_DB_ENGINES to backup-cluster-databases).
| Engine | File | Content |
|---|---|---|
| PostgreSQL | globals.sql | pg_dumpall --globals-only: roles, password hashes, memberships, role settings, tablespaces |
| PostgreSQL | <db>.dump | pg_dump -Fc -Z0, one per database that allows connections (as pg_dumpall; others are named in the log), postgres and template1 included: schema, data, privileges, extensions, the database's owner, settings and grants |
| MariaDB | <db>.sql | one per database, with routines, events and triggers |
| MariaDB | <instance>-full-dump.sql | every database (-A), the mysql schema with the users and grants included |
| MySQL | <db>.sql, <instance>-full-dump.sql | as MariaDB, written by MySQL's own mysqldump 8.4, without GTIDs |
| MongoDB | <instance>.archive | one mongodump archive of every database, users and roles included, with the oplog on a replica set — see MongoDB dumps |
| Redis/Valkey | <instance>.rdb | the server's RDB snapshot, every database: a standalone server, or a replica |
| Redis/Valkey | <instance>-slots-<first>-<last>.rdb | a cluster: one RDB snapshot per primary, named after its first slot range |
| Redis/Valkey | <instance>-cluster-nodes.txt | a cluster: CLUSTER NODES at dump time, which primary held which slots |
Every file is mode 600. The dumps are not compressed: a compressed stream differs from the first changed byte on, so every dump would be new data in every archive. Uncompressed, Borg deduplicates them and compresses on its own; a second archive of an unchanged instance adds next to nothing. The directory needs room for the uncompressed dumps, several times the size of the gzipped ones before 1.1.9. A dump that fails fails the backup and leaves the previous dump of that database in place; the dump of a database that no longer exists stays in the directory.
Restore
Restoring the dumps is described under Restore.
Privileges of the backup user
- PostgreSQL: a superuser dumps everything. Without superuser rights the role password hashes cannot be read: the roles are dumped without their passwords (
--no-role-passwords) and the dump ends with rc1, a warning in Borg UI. Such a user needspg_read_all_data(PostgreSQL 14+),CONNECTon every database, andSELECTon every large object —pg_read_all_datadoes not cover large objects, and a dump that cannot read one fails. - MariaDB: the dumps read every database, the
mysqlschema included:GRANT SELECT, SHOW VIEW, TRIGGER, EVENT, SHOW DATABASES ON *.*is enough (tested), orroot. A restore with--allwrites themysqlschema and needsroot. - MySQL: as MariaDB, plus
PROCESS:GRANT SELECT, SHOW VIEW, TRIGGER, EVENT, SHOW DATABASES, PROCESS ON *.*(tested), orroot. - Redis/Valkey: an ACL user needs
ACL SETUSER backup on >… +info +sync +replconf +cluster|nodes(tested):INFOtells a cluster from a standalone server,SYNC/REPLCONFcarry the snapshot,CLUSTER NODESlists the primaries. Without auserthe default user is used (requirepass). A server that renames or disablesSYNCcannot be dumped, nor can Dragonfly (see Dragonfly).
PostgreSQL operators
A secretRef instance reads the user and the password from the operator's Secret; the host is always given on the instance, the port defaults to 5432.
| Operator | secretRef | host |
|---|---|---|
| Zalando | name: postgres.<cluster>.credentials.postgresql.acid.zalan.do, usernameKey: username, passwordKey: password | <cluster>.<ns>.svc |
| CloudNativePG | name: <cluster>-superuser, usernameKey: username, passwordKey: password | <cluster>-rw.<ns>.svc |
CloudNativePG's Secrets carry a host too, but only the service name (<cluster>-rw), which resolves in the cluster's own namespace alone. Its <cluster>-superuser Secret exists only with enableSuperuserAccess: true on the Cluster (off by default). Without it, a role of its own can dump: one declared under spec.managed.roles with login: true, inRoles: [pg_read_all_data] and a passwordSecret (a kubernetes.io/basic-auth Secret with username and password), or the application owner from <cluster>-app. Both dump a cluster as CloudNativePG bootstraps it, the roles without their passwords (rc 1, see above). What is added later needs the grants above: CONNECT on every database, SELECT on every large object, and, for the application owner, which reads only what it owns or is granted, read access to every table. A dump that cannot read something fails the backup (rc ≥ 2). Tested against CloudNativePG 1.30.1 with all three Secrets.
MariaDB servers without TLS
The image's MariaDB client (11.8) asks for TLS by default and refuses a server that offers none, as a MariaDB before 11.4 without a certificate of its own does. For such a server, set tls: false on a secretRef instance (the resolver writes skip-ssl into its credentials file), or add a skip-ssl line to the [client] section of a static config.
MySQL and Percona Server
databases.mysql takes MySQL 8.4 and Percona Server 8.4 (the Percona Operator for MySQL included), with the same instance options as databases.mariadb. It dumps and restores with MySQL's own client (8.4, in /opt/mysql/bin): mariadb-dump writes the values of generated columns into its dumps, which MySQL refuses on restore, and copies system tables MySQL does not take back. An instance under the wrong engine fails its backup with rc 2, naming the engine to use.
- TLS: MySQL's client uses TLS when the server offers it, without checking the certificate (
ssl-mode=PREFERRED).tls: falseon asecretRefinstance writesssl-mode=DISABLEDandget-server-public-key(acaching_sha2_passworduser cannot log in without TLS otherwise). Neither authenticates the server: someone who can intercept the connection can pose as the server, and withget-server-public-keyhand out the RSA key the password is encrypted with, and read the password. Both are meant for a network you trust, such as the cluster's own. Elsewhere use a staticconfigthat checks the server:ssl-mode=VERIFY_IDENTITYwithssl-ca, or, without TLS,server-public-key-pathwith the server's key. - GTIDs: the dumps carry none (
--set-gtid-purged=OFF). A restore brings back the data into a server that keeps its own GTIDs; it does not make that server a replica of the source. - Percona Operator for MySQL:
<cluster>-secretsholds one key per system user (user: root,passwordKey: root);<cluster>-psuser-rootholdsuserandpassword(usernameKey: user). Both tested with operator 1.2.0. Restore single databases into an operator's cluster:--allreplaces the users the operator manages.