Docs / Config file reference

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.

KeyWhat it does
node_idThe node this host reports as, and the folder its snapshots go under in the bucket (<prefix>/<node_id>/). enroll sets it.
node_nameThe name enroll registers the node under, shown in the console.
project_idThe 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_urlThe control plane. Default https://safegrd.dev. It is contacted only when server_token is set. Also SAFEGRD_SERVER_URL.
server_tokenThe 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_urlThe 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.

KeyWhat it does
storage.types3, local or hosted (SafeGrd's locked bucket, reached through URLs the remote server signs per request; Hosted storage). Default local.
storage.bucketThe S3 bucket. Also SAFEGRD_STORAGE_BUCKET.
storage.regionThe bucket's region. Also SAFEGRD_S3_REGION, then AWS_REGION.
storage.endpointAn S3-compatible endpoint: MinIO, Backblaze B2, Wasabi, DigitalOcean Spaces. Leave it out on AWS. Also SAFEGRD_S3_ENDPOINT.
storage.prefixThe key prefix in the bucket. init writes safegrd/snapshots. A restore on another machine needs the same value.
storage.retention_daysHow 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_lockOff 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_keepHow 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_modeCOMPLIANCE (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_pathThe directory for type: local. Default ./safegrd-storage.
storage.access_key_idThe 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_keyThe S3 secret. Prefer SAFEGRD_S3_SECRET_KEY (or AWS_SECRET_ACCESS_KEY).
storage.force_path_stylePath-style bucket addressing, which MinIO needs.
storage.node_idThe folder under prefix. It defaults to node_id; set it only to read another node's snapshots.
storage.iam_role_arnAn IAM role to assume through STS for bucket access, instead of static keys.

encryption

KeyWhat it does
encryption.public_keyThe Age recipient every snapshot is encrypted to (age1…). Also SAFEGRD_PUBLIC_KEY.
encryption.key_pathThe 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

KeyWhat it does
alert.slack_webhook_urlNot 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_urlNot read, for the same reason.

daemon

The daemon (Daemon).

KeyWhat it does
daemon.intervalHow often the daemon looks for due work. Default 5m.
daemon.state_dirWhere the daemon keeps its state and locks. Default ~/.safegrd. Never a network filesystem.
daemon.metrics_addrAccepted, not acted on. There is no metrics endpoint.

defaults

What every surface inherits unless it sets its own.

KeyWhat 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.timezoneAccepted, not acted on. Schedules are intervals from the last success, not wall-clock times.
defaults.retention_daysThe lock period for surfaces that set none.
defaults.keep_dailyLock the first backup of each UTC day for this many days (GFS).
defaults.keep_weeklyLock the first backup of each ISO week for this many weeks (GFS).
defaults.keep_monthlyLock 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.

KeyWhat it does
surfaces[].idA stable identifier. Changing it makes a new surface.
surfaces[].typepostgres, mysql, mongodb, sqlite, files or email.
surfaces[].nameThe name shown in the console. Defaults to the id.
surfaces[].scheduleOverrides defaults.schedule.
surfaces[].retention_daysOverrides the lock period.
surfaces[].keep_dailyOverrides defaults.keep_daily.
surfaces[].keep_weeklyOverrides defaults.keep_weekly.
surfaces[].keep_monthlyOverrides defaults.keep_monthly.
surfaces[].pre_backupA 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_backupA 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_urlA 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.fromWhere 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.nameWith from: env, the environment variable that holds it. Under a service, set it in the service's environment (details).
surfaces[].credential.runWith 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.pathWith from: file, a file whose contents are the secret, such as a mounted secret.
surfaces[].rootsA files surface's directories.
surfaces[].excludesGlob patterns a files surface leaves out.
surfaces[].formatHow 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_logA 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_passwordsA 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_sealedSeal 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_filesystemFor format: repo: stay on each root's filesystem. Default yes when a root is /, no otherwise.
surfaces[].hostA mailbox's IMAP server. Default imap.gmail.com.
surfaces[].portIts TLS port. Default 993.
surfaces[].usernameThe mailbox login.
surfaces[].foldersThe folders to back up. Default: all but spam and trash.
surfaces[].ca_fileA 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[].storageA whole storage block for this surface alone, with the same keys as above.
surfaces[].encryptionAn encryption block for this surface alone, to encrypt it to a different key.
surfaces[].drill.sandbox_urlAn 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_envThe name of an environment variable holding the sandbox URL.
surfaces[].drill.keep_failed_sandboxWhen 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.