Docs / Install and enrol

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

# macOS and Linux, amd64 and arm64
curl -fsSL https://safegrd.dev/install.sh | sh
# the host is listed under its hostname; to choose the name:
curl -fsSL https://safegrd.dev/install.sh | SAFEGRD_NODE_NAME=web-01 sh

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:

  1. 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.
  2. 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.

VariableEffect
VERSIONRelease tag to install. Default: the latest release.
SAFEGRD_INSTALL_DIRWhere the binary goes.
SAFEGRD_NO_SETUP=1Install only; do not offer to log in and enrol. Use it on a machine you are restoring to.
SAFEGRD_FORCE_INSTALL=1Download and install even when the release asked for is already installed. Without it, a host that has the release goes straight to setup.
SAFEGRD_PROJECTProject ID or slug to enrol this host into.
SAFEGRD_NODE_NAMEThe name the console shows for this host. Default: the machine's hostname, without its domain.
SAFEGRD_KEY_CUSTODYlocal keeps this host's key on the host. Without it SafeGrd keeps the key sealed.
SAFEGRD_CLAIMThe claim code the console shows for this host. The host enrols into the project, with the storage and surfaces chosen there.
SAFEGRD_TOKENA 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

# Homebrew, macOS and Linux: the same release archives
brew install safegrd/tap/safegrd
# Go
go install github.com/safegrd/cli/cmd/safegrd@latest
# Docker: the daemon as a sidecar, with its config and key in a volume
docker run --rm -it -v safegrd:/home/safegrd/.safegrd ghcr.io/safegrd/cli enroll

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

safegrd login
safegrd enroll

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

safegrd init --storage s3 --s3-bucket my-worm-bucket

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.

ModeWhat 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

PathWhat it is
~/.safegrd/config.yamlNode config: surfaces, storage sink, retention, encryption public key. Keep it 0600; it holds connection strings.
~/.safegrd/keys/daemon.keyThe 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

# permissions, keys, sink reachability, Object Lock, clock skew, surfaces
safegrd doctor

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.