Backups
Every Kheeper host comes with an S3-shaped object store and a localhost proxy that lets any S3 client (wal-g, restic, rclone, the AWS SDK, etc.) write to it without managing credentials. This guide explains how the pieces fit together and walks through a complete wal-g setup for PostgreSQL.
Concepts
Object store
Each host has a dedicated namespace inside an org-scoped bucket on us.kheeper.com. Paths are S3 path-style:
us.kheeper.com/<org>/<host>/<your data>
A host can only read and write under its own <org>/<host>/... prefix. The org owner can read across all hosts in the org for restore and migration purposes.
Authentication is the same ed25519 host-JWT every other kheeper command uses — there is no sigv4, no per-host static credentials, and nothing to rotate on your side.
Storage and bandwidth are billed at the same rates as the registry — see pricing.
Object store proxy
The Kheeper object store is not S3 compatible. We provide a proxy that you can point your S3 clients at.
kheeper object-proxy
By default this will listen on 127.0.0.1:5000.
The credentials and signature supplied by the S3 client will be dropped and the proxy will use the host's authentication key.
Quick start: env-file for an S3 client
kheeper hosts me --env prints an env-file with everything an S3 client needs to talk to the local proxy:
$ kheeper hosts me --env
AWS_ACCESS_KEY_ID=dummy
AWS_SECRET_ACCESS_KEY=dummy
AWS_REGION=us-east-1
AWS_DEFAULT_REGION=us-east-1
AWS_ENDPOINT=http://127.0.0.1:5000
WALG_S3_PREFIX=s3://myorg/web-01
AWS_S3_FORCE_PATH_STYLE=true
WALG_S3_FORCE_PATH_STYLE=true
wal-g + PostgreSQL
The canonical setup is published at github.com/kheepercom/public/tree/main/postgres. The snippets below are excerpts — read the repo for the full, working image.
1. Run the proxy as a system service
kheeper-object-proxy.service keeps the proxy running on loopback. It runs as root so it can read the host's ed25519 private key from /etc/kheeper/default/:
[Unit]
Description=Localhost S3 proxy: wal-g -> kheeper objects backend
After=network-online.target
Wants=network-online.target
ConditionPathExists=/etc/kheeper/default/identity.json
[Service]
Environment=KHEEPER_CONFIG=/etc/kheeper
ExecStart=/usr/local/bin/kheeper object-proxy --listen 127.0.0.1:5000
Restart=on-failure
RestartSec=3
ProtectSystem=strict
ProtectHome=true
PrivateTmp=true
NoNewPrivileges=true
ReadOnlyPaths=/etc/kheeper
[Install]
WantedBy=multi-user.target
2. Render the env-file at boot
kheeper-walg-env.service runs once at boot and writes /etc/kheeper/walg.env. Any unit that wants wal-g env vars takes it as an EnvironmentFile=:
[Unit]
Description=Render /etc/kheeper/walg.env from this host's identity
After=network-online.target
Wants=network-online.target
Before=postgresql.service walg-base-backup.service walg-retention.service
ConditionPathExists=/etc/kheeper/default/identity.json
[Service]
Type=oneshot
RemainAfterExit=yes
Environment=KHEEPER_CONFIG=/etc/kheeper
ExecStart=/bin/sh -c '/usr/local/bin/kheeper hosts me --env > /etc/kheeper/walg.env.tmp && install -m 0644 /etc/kheeper/walg.env.tmp /etc/kheeper/walg.env && rm /etc/kheeper/walg.env.tmp'
[Install]
WantedBy=multi-user.target
3. Wire wal-g into PostgreSQL
Append the archive settings to postgresql.conf:
archive_mode = on
archive_command = 'wal-g wal-push %p'
archive_timeout = 60
restore_command = 'wal-g wal-fetch %f %p'
And give postgresql.service the same env-file via a drop-in:
[Unit]
Wants=kheeper-object-proxy.service kheeper-walg-env.service
After=kheeper-object-proxy.service kheeper-walg-env.service
[Service]
EnvironmentFile=-/etc/kheeper/walg.env
With this in place, every WAL segment Postgres archives is pushed via the proxy to your host's prefix in the object store.
4. Schedule base backups
walg-base-backup.timer fires daily and runs:
[Service]
Type=oneshot
User=postgres
Group=postgres
EnvironmentFile=/etc/kheeper/walg.env
Environment=PGHOST=/var/run/postgresql
ExecStart=/usr/local/bin/wal-g backup-push /var/lib/pgsql/data
5. Prune old backups
walg-retention.timer keeps the most recent 7 full backups and discards the rest:
[Service]
Type=oneshot
User=postgres
Group=postgres
EnvironmentFile=/etc/kheeper/walg.env
ExecStart=/usr/local/bin/wal-g delete retain FULL 7 --confirm
Restoring
Restoring to the same host is just wal-g backup-fetch against the same WALG_S3_PREFIX — credentials and prefix are already in walg.env.
To restore onto a different host (say, web-01 died and you're rebuilding it as web-02), the new host can't read web-01's prefix by default. Run the restore from a workstation where you have org-owner credentials:
WALG_S3_PREFIX=s3://myorg/web-01 \
AWS_ENDPOINT=http://127.0.0.1:5000 \
AWS_ACCESS_KEY_ID=dummy AWS_SECRET_ACCESS_KEY=dummy \
AWS_S3_FORCE_PATH_STYLE=true WALG_S3_FORCE_PATH_STYLE=true \
wal-g backup-fetch /var/lib/pgsql/data LATEST
Then rsync the data directory across to the new host before starting Postgres.
Using other S3 clients
Any S3-compatible tool works the same way. Examples assuming walg.env is sourced:
# rclone
rclone --s3-endpoint=$AWS_ENDPOINT --s3-force-path-style ls myorg-host:myorg/web-01/
# AWS CLI
aws --endpoint-url=$AWS_ENDPOINT s3 ls s3://myorg/web-01/
# restic
RESTIC_REPOSITORY=s3:$AWS_ENDPOINT/myorg/web-01/restic restic init
The proxy sees standard S3 requests, so the only configuration most tools need is the endpoint URL, path-style addressing, and any dummy credentials they insist on.