Install

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: uri pointing 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.