Config file reference
This reference covers every configuration key supported in ~/.safegrd/config.yaml.
The file must be readable by its owner only (0600); commands will reject files with broader permissions.
Commands read the file specified by --config, defaulting to ~/.safegrd/config.yaml.
When running safegrd daemon install, the explicit path is embedded into the service definition
so it runs consistently regardless of the working directory.
Use safegrd config validate to check syntax and structure without running tasks,
or safegrd doctor to verify credential resolution and storage sink connectivity.
This page is checked against the program on every build. A key that exists and is not listed here fails the build, and so does a key listed here that does not exist.
The host
Written by safegrd init and safegrd enroll. You rarely edit these by hand.
| Key | What it does |
|---|---|
node_id | The node this host reports as, and the folder its snapshots go under in the bucket (<prefix>/<node_id>/). enroll sets it. |
node_name | The name enroll registers the node under, shown in the console. |
project_id | The project the node was enrolled into. A project whose bucket is set in the console routes this host's backups there. A storage.bucket naming another bucket, or a secret_access_key where SafeGrd holds the key, is refused rather than used. A bucket named here in a project with none is registered as the project's: SafeGrd keeps a fingerprint of its key, never the key. |
server_url | The control plane. Default https://safegrd.dev. It is contacted only when server_token is set. Also SAFEGRD_SERVER_URL. |
server_token | The node token enroll issued (sg_tok_…). Without one the host is standalone: it backs up and says so, and reports nothing. Also SAFEGRD_SERVER_TOKEN. |
database_url | The database a one-off safegrd backup protects: postgres://…, mysql://…, mongodb://… or sqlite:///path/to/file.db. With no surfaces listed, the daemon protects it as a single surface. Also SAFEGRD_DATABASE_URL. |
storage
Where snapshots are written. A surface can override the whole block with its own storage.
| Key | What it does |
|---|---|
storage.type | s3, local or hosted (SafeGrd's locked bucket, reached through URLs the remote server signs per request; Hosted storage). Default local. |
storage.bucket | The S3 bucket. Also SAFEGRD_STORAGE_BUCKET. |
storage.region | The bucket's region. Also SAFEGRD_S3_REGION, then AWS_REGION. |
storage.endpoint | An S3-compatible endpoint: MinIO, Backblaze B2, Wasabi, DigitalOcean Spaces. Leave it out on AWS. Also SAFEGRD_S3_ENDPOINT. |
storage.prefix | The key prefix in the bucket. init writes safegrd/snapshots. A restore on another machine needs the same value. |
storage.retention_days | How long each snapshot is locked under Object Lock. Default 14. A lock can never be shortened, so set it deliberately (Storage and retention). |
storage.expire_after_lock | Off by default. When true, the daemon deletes snapshots from your bucket once a day after their Object Lock ended more than a day ago, by the bucket's clock; never a surface's newest min_keep snapshots or the last known good one before an open anomaly. Needs s3:DeleteObjectVersion and s3:GetObjectRetention (Storage and retention). |
storage.min_keep | How many of each surface's newest snapshots pruning never deletes, whatever their locks say. Default 3. safegrd prune --min-keep sets it for one run. |
storage.worm_mode | COMPLIANCE (the default: nobody can delete before the date), GOVERNANCE (a principal with the bypass permission can), or NONE for a sink with no Object Lock. Matched exactly, in upper case. |
storage.local_path | The directory for type: local. Default ./safegrd-storage. |
storage.access_key_id | The S3 access key. Prefer SAFEGRD_S3_ACCESS_KEY (or AWS_ACCESS_KEY_ID), or a sink credential held by the control plane, over writing it here. |
storage.secret_access_key | The S3 secret. Prefer SAFEGRD_S3_SECRET_KEY (or AWS_SECRET_ACCESS_KEY). |
storage.force_path_style | Path-style bucket addressing, which MinIO needs. |
storage.node_id | The folder under prefix. It defaults to node_id; set it only to read another node's snapshots. |
storage.iam_role_arn | An IAM role to assume through STS for bucket access, instead of static keys. |
encryption
| Key | What it does |
|---|---|
encryption.public_key | The Age recipient every snapshot is encrypted to (age1…). Also SAFEGRD_PUBLIC_KEY. |
encryption.key_path | The identity file that decrypts them, kept at 0600. The private key itself is never written into this file. SAFEGRD_PRIVATE_KEY can supply it instead. |
alert
| Key | What it does |
|---|---|
alert.slack_webhook_url | Not read. Hosts do not send alerts; the control plane does. Set the webhook in the console under Settings → Alerts (Alerts). config validate and the daemon warn when this is set. |
alert.discord_webhook_url | Not read, for the same reason. |
daemon
The daemon (Daemon).
| Key | What it does |
|---|---|
daemon.interval | How often the daemon looks for due work. Default 5m. |
daemon.state_dir | Where the daemon keeps its state and locks. Default ~/.safegrd. Never a network filesystem. |
daemon.metrics_addr | Accepted, not acted on. There is no metrics endpoint. |
defaults
What every surface inherits unless it sets its own.
| Key | What it does |
|---|---|
defaults.schedule | @hourly, @daily, @weekly, or an interval such as 6h. The minimum interval is one hour to avoid creating excessive immutable objects in retention storage. |
defaults.timezone | Accepted, not acted on. Schedules are intervals from the last success, not wall-clock times. |
defaults.retention_days | The lock period for surfaces that set none. |
defaults.keep_daily | Lock the first backup of each UTC day for this many days (GFS). |
defaults.keep_weekly | Lock the first backup of each ISO week for this many weeks (GFS). |
defaults.keep_monthly | Lock the first backup of each month for this many months. |
surfaces[]
One entry per thing the daemon protects. Each becomes its own node in the console and counts as one surface on your plan.
| Key | What it does |
|---|---|
surfaces[].id | A stable identifier. Changing it makes a new surface. |
surfaces[].type | postgres, mysql, mongodb, sqlite, files or email. |
surfaces[].name | The name shown in the console. Defaults to the id. |
surfaces[].schedule | Overrides defaults.schedule. |
surfaces[].retention_days | Overrides the lock period. |
surfaces[].keep_daily | Overrides defaults.keep_daily. |
surfaces[].keep_weekly | Overrides defaults.keep_weekly. |
surfaces[].keep_monthly | Overrides defaults.keep_monthly. |
surfaces[].pre_backup | A shell command run before each of the daemon's backups of this surface: flush a cache, pause a writer. If it fails, the backup is not taken, and the hook's own output says why in the daemon's log. It has 10 minutes. SAFEGRD_SURFACE_ID and SAFEGRD_SURFACE_TYPE are set. |
surfaces[].post_backup | A shell command run after every attempt, whether it succeeded or not, so whatever pre_backup paused is resumed. SAFEGRD_BACKUP_STATUS is success or failed, and SAFEGRD_SNAPSHOT_ID names the snapshot. Its failure is reported but does not undo the backup. |
surfaces[].database_url | A database surface's connection URL, written out, for one with no secret in it (a local socket, peer authentication). For one with a password, use credential. A SQLite surface gives its file here, as sqlite:///var/lib/app/app.db; there is no path key. |
surfaces[].credential.from | Where a database or mailbox surface's credential comes from: the whole connection URL for a database, the password for a mailbox. safegrd: SafeGrd holds it, encrypted, and gives it only to this host when the surface backs up; nothing is set on the host and it is never written to this file. env, command or file: it stays on this host, as below. A credential has one origin: a block and a database_url together are refused, and one that names a variable, command or file it cannot read fails that surface rather than using anything else. |
surfaces[].credential.name | With from: env, the environment variable that holds it. Under a service, set it in the service's environment (details). |
surfaces[].credential.run | With from: command, a command whose output is the secret, such as a secret manager's CLI. It runs under /bin/sh for up to 30 seconds. If it fails, that surface fails (details). |
surfaces[].credential.path | With from: file, a file whose contents are the secret, such as a mounted secret. |
surfaces[].roots | A files surface's directories. |
surfaces[].excludes | Glob patterns a files surface leaves out. |
surfaces[].format | How a files, PostgreSQL or SQLite surface is stored: repo (incremental: each run uploads only what changed) or tar (one archive per backup). Unset is repo. MySQL and MongoDB are always one archive. A surface added in the console has the Backup type chosen there written here. See incremental file backups and databases. |
surfaces[].change_log | A PostgreSQL surface skips reading tables nothing wrote since its last run. Puts a trigger on each table and a safegrd schema in the database. See the change log. |
surfaces[].roles_without_passwords | A PostgreSQL surface backs up the roles its schema names without their password hashes. Default: with them. A surface SafeGrd backs up for you has no switch. See roles, owners and grants. |
surfaces[].recovery_doc_sealed | Seal the recovery document written beside each snapshot with the surface's recipient. Default: plain text, so it can be read without SafeGrd; it holds no secret. See the recovery document. |
surfaces[].one_filesystem | For format: repo: stay on each root's filesystem. Default yes when a root is /, no otherwise. |
surfaces[].host | A mailbox's IMAP server. Default imap.gmail.com. |
surfaces[].port | Its TLS port. Default 993. |
surfaces[].username | The mailbox login. |
surfaces[].folders | The folders to back up. Default: all but spam and trash. |
surfaces[].ca_file | A PEM bundle trusted for the mail server's certificate, added to the system roots: a server behind a private CA. Default: SAFEGRD_EMAIL_CA_FILE, if set. |
surfaces[].storage | A whole storage block for this surface alone, with the same keys as above. |
surfaces[].encryption | An encryption block for this surface alone, to encrypt it to a different key. |
surfaces[].drill.sandbox_url | An empty database of the same kind that this surface's Fire Drills restore into. The drill refuses one that holds tables, and empties it afterwards. Without it, a Postgres surface on a plan with sandbox drills restores into a throwaway cluster the daemon starts from the host's PostgreSQL server, if one is installed. Otherwise drills run in memory. |
surfaces[].drill.sandbox_url_env | The name of an environment variable holding the sandbox URL. |
surfaces[].drill.keep_failed_sandbox | When a drill restores the snapshot and then fails a check (the digest or the row counts), leave the sandbox as the drill left it for 24 hours and print where it is, password left out. A sandbox_url database holds the next drill back until then and is emptied by the first drill after. A throwaway cluster keeps running and is removed by the daemon after the 24 hours. A restore that fails is rolled back, so it leaves nothing to keep. The drill's record says the sandbox was kept. Default false. |