Airgapped or cron
Running the agent without a live orchestrator connection. The
open-source, self-sufficient path. Useful in airgapped
environments, on hosts that cannot reach
app.restorable.app, or when you want to drive the
agent from your own crontab.
Standalone mode
The agent has two modes, selected by the mode: key
in config.yaml:
-
managed: orchestrator-driven. Heartbeats, commands, receipts submitted back. The default the quickstart sets up. -
standalone: no orchestrator. Schedules come from your own cron or systemd timers. Receipts land on local disk for you to forward however you like.
Standalone is the mode that works with zero orchestrator reachability.
Generating standalone keys
Use init --standalone:
sudo -u restorable restorable init --standalone
The agent's install root resolves from
RESTORABLE_HOME (the systemd default is
/var/lib/restorable). init generates the
signing key and age key into <root>/keys/, then
writes a standalone-mode config.yaml
skeleton at <root>/config.yaml.
There is no auth key. There is no orchestrator URL. The
customer's public key is what the auditor uses to verify
receipts, same as managed mode. Export it from
/var/lib/restorable/keys/signing.pub and publish it
however you publish attestations to auditors.
Configuring sources and checks
Edit config.yaml. Standalone mode honors the
schedule fields managed mode ignores:
mode: standalone
agent:
id: prod-eu-agent
org_id: internal
# No api_token needed in standalone mode.
storage:
# Ciphertext goes straight to your own S3-compatible bucket;
# no orchestrator involved. Scaleway, Hetzner S3, MinIO all work.
s3:
endpoint: https://s3.fr-par.scw.cloud
region: fr-par
bucket: my-restorable-backups
access_key: env:S3_ACCESS_KEY
secret_key: env:S3_SECRET_KEY
sources:
- slug: prod-db
kind: postgres
connection:
uri: env:RESTORABLE_DB_URI
checks:
- healthy
# Standalone-only fields:
backup_schedule: "0 3 * * *"
restore_schedule: "0 2 * * 1"
retention_days: 30
checks:
- id: healthy
summary_sql: "SELECT 1 AS ok"
expect: "ok == 1"
The schedule fields are documentation for your crontab; the
agent does not run a scheduler in standalone mode. You run
backup and restore-test yourself at
those cadences.
Running from cron
/etc/cron.d/restorable:
# Daily backup at 03:00.
0 3 * * * restorable RESTORABLE_HOME=/var/lib/restorable /usr/bin/restorable backup --source prod-db
# Weekly restore test on Mondays at 02:00.
0 2 * * 1 restorable RESTORABLE_HOME=/var/lib/restorable /usr/bin/restorable restore-test --source prod-db Cron inherits a thin environment. Load secrets from the env file before the agent runs:
0 3 * * * restorable . /var/lib/restorable/env && RESTORABLE_HOME=/var/lib/restorable /usr/bin/restorable backup --source prod-db Running from systemd timers
Standalone does not use the long-lived
restorable.service. Drop a pair of
oneshot services and matching timers:
# /etc/systemd/system/restorable-backup@.service
[Unit]
Description=Restorable backup of %i
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
User=restorable
Group=restorable
SupplementaryGroups=docker
Environment=RESTORABLE_HOME=/var/lib/restorable
EnvironmentFile=/var/lib/restorable/env
ExecStart=/usr/bin/restorable backup --source %i
# /etc/systemd/system/restorable-backup@.timer
[Unit]
Description=Daily Restorable backup of %i
[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true
[Install]
WantedBy=timers.target sudo systemctl enable --now restorable-backup@prod-db.timer
Repeat with a restorable-restore-test@.service +
matching weekly timer for the restore side.
Where the receipts go
Into /var/lib/restorable/receipts/ as
.intoto.json files. One file per restore test run.
A small cron job can forward them: mail them to the auditor,
push them to a compliance bucket, archive them, whatever fits.
The restorable digest subcommand renders a
terminal-friendly weekly summary from the local receipts. Pipe
it to mail for an offline version of the weekly
evidence email:
0 8 * * 1 restorable RESTORABLE_HOME=/var/lib/restorable /usr/bin/restorable digest --since 7d \
| mail -s "Restorable weekly digest" ops@example.com Serverless caveats
The agent runs standalone inside Lambda, Cloud Run, Scaleway Serverless Jobs, or equivalent. Two hard limits:
- Execution time. The backup has to finish inside the platform's function budget. A 50 GB Postgres does not fit Lambda's 15-minute cap. Scaleway Serverless Jobs are more forgiving; Cloud Run is in between.
- Scratch DB. Container-in-container is
unavailable in most serverless runtimes. Use
restore_scratch.kind: uripointing at a managed Postgres.
In practice: serverless works for very small databases run by customers who refuse to operate a VM. Everyone else: the €4/month systemd path is simpler.
Moving to managed later
Standalone and managed use the same signing key. To move a
standalone agent onto the orchestrator, run
restorable init against an auth key; the
command picks up the existing keys and rewrites
config.yaml to managed mode. Your history of
signed receipts stays valid; past receipts signed in standalone
mode verify the same way as future receipts signed in managed
mode.