Install and enrol
SafeGrd runs on a host that can already reach what you are protecting. It compresses and encrypts there, and only encrypted data leaves the machine. Setup has two halves: a local half that generates your keypair, and an optional half that registers the host with SafeGrd so the console can track it.
Install the binary
The installer detects your platform, downloads the matching release, checks it
against the release's checksums.txt, and puts safegrd in
/usr/local/bin (or ~/.local/bin when it cannot use sudo).
To read it before you run it, fetch the script itself; it
is the same file as install.sh
in the CLI repository, and a test fails the build if the two ever differ.
The console's setup wizard (Protect a Surface) prints this same command with the project you picked already filled in, so the host lands in the right place without a flag to remember.
What it asks, when there is a terminal
Run in a terminal, the installer offers to connect the host straight away, so one command takes you from nothing to an enrolled host:
- A browser login. It prints a link and a confirmation code. Open the link in a browser on any device, check the code matches, and approve. On a server you reached over SSH or PuTTY that browser is the one on your laptop; the CLI does not try to open one on the server.
- Enrolment. It generates the keypair and registers the host. SafeGrd
keeps the private key sealed unless you set
SAFEGRD_KEY_CUSTODY=local, or chose that for this host in the console's setup (below).
Without a terminal (CI, cloud-init, a cron job) it installs and stops. Running it again
on a host that is already enrolled upgrades the binary and changes nothing else. To add
surfaces to that host, name them in the console (Add surface on its row
under Nodes) and run safegrd claim on it
(details).
Options
Options are environment variables, and they go on the sh after the pipe:
curl -fsSL https://safegrd.dev/install.sh | VERSION=v0.0.3 sh. Written before
curl they set the variable for curl, and the script never sees it.
| Variable | Effect |
|---|---|
| VERSION | Release tag to install. Default: the latest release. |
| SAFEGRD_INSTALL_DIR | Where the binary goes. |
| SAFEGRD_NO_SETUP=1 | Install only; do not offer to log in and enrol. Use it on a machine you are restoring to. |
| SAFEGRD_FORCE_INSTALL=1 | Download and install even when the release asked for is already installed. Without it, a host that has the release goes straight to setup. |
| SAFEGRD_PROJECT | Project ID or slug to enrol this host into. |
| SAFEGRD_NODE_NAME | The name the console shows for this host. Default: the machine's hostname, without its domain. |
| SAFEGRD_KEY_CUSTODY | local keeps this host's key on the host. Without it SafeGrd keeps the key sealed. |
| SAFEGRD_CLAIM | The claim code the console shows for this host. The host enrols into the project, with the storage and surfaces chosen there. |
| SAFEGRD_TOKEN | A token from Tokens in the console (sg_pat_…). The host enrols with no terminal and no browser login, for CI, cloud-init or ssh host 'curl … | sh'. |
There is no Windows build. The daemon runs on Linux and macOS, and in the Docker image on a Windows host.
Other ways to install
The image carries pg_dump 18, the MariaDB client and the MongoDB tools, and a
PostgreSQL 18 server so a drill restores a database into a throwaway cluster on the volume,
and runs as its own user. compose.yaml
runs it beside a database, and the
Helm chart runs
it on Kubernetes with the config and key copied from a Secret into an in-memory volume.
With no server, 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. Where the plan includes it, SafeGrd can also take the backup itself:
Back up on SafeGrd under Surfaces in the console takes the connection string, and a
machine SafeGrd starts for each backup does the rest (how).
None of these connects the host on its own. Run safegrd login and then
safegrd enroll. The CLI is
source-available (Business Source License
1.1). The build needs only Go, so go build ./cmd/safegrd from a clone is the
whole build. At run time a database surface needs its engine's client tools on the host,
listed per engine in Databases; SQLite, files and mail need
nothing else. Release archives and their checksums are on the
releases page.
Two ways to set a host up
Whichever of these you run first generates the Age keypair; the other finds it already there, so the order never matters.
safegrd login, then safegrd enroll: connected to SafeGrd
What the installer runs for you. login prints a link and a code to
approve in any browser; enroll then registers this host as a node, so
the console can show its snapshots, schedule Fire Drills against them and tell you
when the host goes quiet. It runs the local setup for you if this machine has no
config yet. Where no browser can be involved at all, pass a token from
Tokens in the console instead:
safegrd enroll --token sg_pat_YOUR_TOKEN.
safegrd init: standalone and offline
The local half and nothing else: generates the keypair, writes
~/.safegrd/config.yaml, and contacts no SafeGrd server (with an S3 bucket it
asks the bucket whether Object Lock is on). A node set up this
way backs up, restores and verifies entirely on its own. This is the right
command for an air-gapped host, and the right command to try the product without
an account.
Who holds the private key
Key custody decides who can decrypt a host's snapshots, and whether you can still restore
them after losing that host. It is chosen per host, when the host enrols, with
--key-custody. SafeGrd keeps the key unless you pass local. It
applies to a key the host generates. A key you supply yourself is never sent anywhere.
The console shows on each surface who holds its key.
| Mode | What happens |
|---|---|
| --key-custody safegrd (default) | SafeGrd-managed key. SafeGrd keeps your key sealed. The private key is sealed and released only to your organization's enrolled hosts, for restores and drills. Losing a host never loses your backups: enrol a new one and restore (the steps). |
| --key-custody local |
Customer-managed key. Only you can decrypt these backups. The private key is
written to key_path at 0600 and only the public half is registered. Keep a
copy of the key file somewhere safe.
|
With a customer-managed key, keep a copy of ~/.safegrd/keys/daemon.key.
It is the key that decrypts your snapshots, and it stays with you. Store a copy with your most important credentials before the first backup.
The private key is never written into config.yaml. It lives at
key_path, and is resolved from there or from
SAFEGRD_PRIVATE_KEY when a restore or a drill needs it. See
Security and key custody for what SafeGrd does and does
not hold.
Where configuration lives
| Path | What it is |
|---|---|
| ~/.safegrd/config.yaml | Node config: surfaces, storage sink, retention, encryption public key. Keep it 0600; it holds connection strings. |
| ~/.safegrd/keys/daemon.key | The Age private identity, 0600. Never transmitted when the key is customer-managed. |
| ~/.safegrd/ | Daemon state and per-surface locks, unless state_dir says otherwise. |
Point any command at a different file with the global --config flag,
and at a different control plane with --server-url or
SAFEGRD_SERVER_URL. Run safegrd config validate to check a
file before the daemon depends on it.
Check the machine before you trust it
doctor is the preflight: it checks config permissions, that the keypair
is present and coherent, that the storage sink answers and really has Object Lock,
that the control plane is reachable, that the host clock has not drifted, and that
every configured surface opens with the credential its backup will use, a credential
SafeGrd holds included. On an enrolled host it reports the results to the console,
which shows the host as checked, or what failed. Run it after setup and after any
config edit. --json makes it machine-readable for CI.