Where to run it
The daemon runs wherever it can reach what it backs up. Each way below runs the same
safegrd daemon with the same config, so you can start with one and move to
another by copying ~/.safegrd.
| Where | Use it when |
|---|---|
| A service on the host | The database or files are on a VM or a server you can SSH into. |
| Docker Compose | The database runs in Compose, or you want the client tools without installing them on the host. |
| Kubernetes CronJob | You run on Kubernetes and an hourly pass is often enough. |
| Kubernetes with Helm | You run on Kubernetes and want the daemon resident, so a "Back up now" or a drill from the console starts within a minute. |
| GitHub Actions | There is no server of yours to run it on, and a scheduled workflow can reach the database. |
| On SafeGrd | Nothing of yours should run it. SafeGrd starts a machine for each backup, where your plan includes it. |
Every way needs the host enrolled once (Install and enroll), which
writes ~/.safegrd/config.yaml and, with a customer-managed key, the key beside
it. In a container, that directory lives in a volume or a Secret.
A service on the host
safegrd daemon install writes a systemd unit on Linux or a launchd agent on
macOS and starts it. Daemon covers the unit, its environment and
its logs.
Docker Compose
The image ghcr.io/safegrd/cli carries pg_dump 18, the MariaDB client,
the MongoDB tools and a PostgreSQL 18 server for sandbox drills, and runs as its own user.
compose.yaml
runs it beside a PostgreSQL service with its config and key in the
safegrd-home volume.
The claim code is the one the console shows when you add a host, and it writes the surfaces
into the config. A surface whose credential is {from: env, name: APP_DATABASE_URL}
reads its connection string from the service's environment, so the password stays out of the
config. Give the volume room for the largest database you back up: a sandbox drill restores
into a throwaway cluster there. With a customer-managed key, keep a copy of the key somewhere
safe outside the volume.
Kubernetes CronJob
cronjob.yaml
runs one safegrd daemon run --once pass each hour: it backs up every surface that
is due, runs a drill when SafeGrd's server asks for one, and exits. Enroll on any machine,
then put the config and the key in a Secret:
Leave daemon.key out when SafeGrd holds the key for you. The CLI refuses a config
or key that others can read, and a Secret volume cannot be both 0600 and owned by the
container's user, so an init container copies both into an in-memory volume at 0600. The
daemon's state (the last backup of each surface, the weekly and monthly slots) is on a small
PersistentVolumeClaim, so a pod that restarts does not take an extra backup.
concurrencyPolicy: Forbid stops two passes backing up the same surface at once.
A "Back up now" or "Drill now" from the console waits for the next pass. For an hour's wait
or less, change schedule; to start them within a minute, run the daemon
resident with the chart below.
Kubernetes with Helm
The Helm chart
runs safegrd daemon run as a Deployment, from the same Secret as the CronJob.
From a clone of safegrd/cli:
existingSecret names the Secret (safegrd by default), and
env adds the variables your surfaces read their credentials from. The state
volume (persistence.size, 2 Gi by default) holds the throwaway cluster a sandbox
drill restores into: size it for the largest database plus a quarter. A drill that would leave
less than 5% of the volume free runs in memory instead, and the console says so.
GitHub Actions
The GitHub Action backs up from a scheduled workflow and drills into a database it creates on the runner. Back up Supabase with GitHub Actions walks through it.
On SafeGrd
Back up on SafeGrd under Surfaces in the console takes a connection string, and a machine SafeGrd starts for each backup does the rest. SafeGrd keeps the connection string encrypted and uses it only on that machine. How it works.