Databases
Databases are created as custom resources in your namespace. The operators already run in the cluster, there is nothing to install.
PostgreSQL (CloudNativePG)
Group and version: postgresql.cnpg.io/v1, kind Cluster.
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: app-db
spec:
instances: 3
storage:
size: 10Gi
storageClass: hcloud-volumesA single instance with no backup is not production
A Cluster with instances: 1 and no backup configuration is a starting point for trying things out. For production use instances: 3 and configure a backup target. The volume on hcloud-volumes is network-attached and survives the loss of a node, but with one instance the database is unavailable until the volume is attached to another node and the pod has started there. Without a backup target there is no point-in-time restore, and nothing consistent to recover from when data is deleted or corrupted or the volume itself is lost.
When its node is replaced, a cluster with instances: 1 restarts on another node with its volume and is unavailable until it is back. If the cluster keeps its PodDisruptionBudget (the default), the platform sets spec.nodeMaintenanceWindow on the Cluster for that time and removes it again afterwards. With two or more instances the primary moves to another instance first.
You cannot get a shell in a PostgreSQL pod
kubectl exec into a CloudNativePG pod is refused:
Error from server: admission webhook denied the request:
Exec/attach is only allowed into gVisor-sandboxed pods.Your own workloads run sandboxed and accept exec normally. PostgreSQL pods are the exception, so this affects PostgreSQL only. MariaDB and Dragonfly pods take a shell like anything else.
kubectl port-forward is not affected, so the usual way to run SQL against your own database is to forward its port and connect with a local client, using the credentials the operator generated. A short-lived psql pod inside the namespace works too.
MariaDB (mariadb-operator)
Group: k8s.mariadb.com, kind MariaDB.
apiVersion: k8s.mariadb.com/v1alpha1
kind: MariaDB
metadata:
name: app-db
spec:
rootPasswordSecretKeyRef:
name: app-db-root
key: password
storage:
size: 10Gi
storageClassName: hcloud-volumesDragonfly
Group and version: dragonflydb.io/v1alpha1, kind Dragonfly.
apiVersion: dragonflydb.io/v1alpha1
kind: Dragonfly
metadata:
name: cache
spec:
replicas: 2The instance is reachable at redis://<name>:6379, so redis://cache:6379 for the example above.
Dragonfly speaks the Redis wire protocol, so most clients work unchanged. There are, however, documented behavioural differences on individual commands. Test your application before migrating an existing Redis instance.
Backups
Both PostgreSQL and MariaDB can back themselves up on a schedule, to a bucket you own. There is no bucket provided for you, so this is a thing you set up rather than a thing you inherit.
Where backups go
Anywhere S3-compatible. ITSH object storage works and is the easiest option: create a bucket and an access key, then put the key in a secret in your namespace.
Give the key read and write on that bucket. CloudNativePG lists the bucket to keep its backup catalog and deletes old backups to enforce retentionPolicy, and write-only permits neither.
kubectl create secret generic backup-creds \
--from-literal=ACCESS_KEY_ID='<access-key>' \
--from-literal=ACCESS_SECRET_KEY='<secret-key>'PostgreSQL
Add a backup target to the cluster, then schedule it:
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: app-db
spec:
instances: 3
storage:
size: 10Gi
storageClass: hcloud-volumes
backup:
barmanObjectStore:
destinationPath: "s3://my-backups/app-db"
endpointURL: "https://storage.itsh.dev"
s3Credentials:
accessKeyId:
name: backup-creds
key: ACCESS_KEY_ID
secretAccessKey:
name: backup-creds
key: ACCESS_SECRET_KEY
retentionPolicy: "30d"
---
apiVersion: postgresql.cnpg.io/v1
kind: ScheduledBackup
metadata:
name: app-db-daily
spec:
schedule: "0 0 3 * * *"
cluster:
name: app-dbThe schedule is a six-field cron expression, seconds first, so the example runs at 03:00 daily.
barmanObjectStore prints a deprecation warning
Applying the above tells you the field is deprecated. CloudNativePG marked it so in 1.26 and has it slated for removal in 1.31. That is expected: it is the way backups are configured on this platform, and the Barman Cloud Plugin that replaces it upstream is not installed here. Use it. When the plugin does become available, migrating is an edit to the running cluster and the backups you already hold stay readable.
MariaDB
Backup and Restore resources work the same way, taking either an S3 target or a PVC. See the mariadb-operator documentation for the field layout.
Two limits worth knowing
Object versioning, lifecycle rules and object-lock are not available on ITSH object storage buckets, so backups here cannot be made immutable: anything with write access to the bucket can also delete them.
Give backups their own bucket and an access key scoped to it, and keep that key out of workloads that do not need it. If immutable backups are a hard requirement for you, talk to support before you rely on this setup.
There is no volume snapshot and no point-in-time volume restore. Backups are database-level, taken by the operator. A PVC that is not a managed database has no equivalent, so anything you care about in a plain volume needs its own copy somewhere.
Test the restore
A backup nobody has restored is a guess. Restore into a scratch cluster once, before you need it, and check the data is actually there.
What's next
- Storage for the classes these run on
- Billing and quotas for what a database costs