restic Repository Setup (Cloudflare R2 / S3)
Create the encryption password, wire up the storage credentials, and initialize the repository. restic always encrypts client-side (AES-256) — the password is the only key; lose it and the backup is permanently unrecoverable. R2 server-side encryption is redundant here.
Step 1 — Encryption password
install -d -m700 /root/.restic
openssl rand -base64 48 > /root/.restic/password # strong random key
chmod 600 /root/.restic/password
STOP — copy this password off the server right now (password manager / vault). Without it, none of the backups can ever be restored. This is the single most important action in the whole setup.
Step 2 — Storage credentials env file
Write /root/.restic/r2.env (mode 600). For Cloudflare R2:
cat > /root/.restic/r2.env <<'EOF'
AWS_ACCESS_KEY_ID=<R2 access key id>
AWS_SECRET_ACCESS_KEY=<R2 secret access key>
AWS_DEFAULT_REGION=auto
RESTIC_REPOSITORY=s3:https://<account_id>.r2.cloudflarestorage.com/<bucket>
RESTIC_PASSWORD_FILE=/root/.restic/password
RESTIC_COMPRESSION=auto
EOF
chmod 600 /root/.restic/r2.env
- Repository URL form:
s3:https://<account_id>.r2.cloudflarestorage.com/<bucket>— note thes3:prefix in front of the fullhttps://endpoint. AWS_DEFAULT_REGION=autois required for R2 (or pass-o s3.region=auto). Standard AWS region names do not work.- Other S3-compatible providers: same shape, e.g. Wasabi
s3:https://s3.<region>.wasabisys.com/<bucket>, MinIOs3:http://host:9000/<bucket>, AWSs3:s3.amazonaws.com/<bucket>(+ real region).
Sourcing for any manual restic command:
set -a; . /root/.restic/r2.env; set +a
Step 3 — Initialize and verify
set -a; . /root/.restic/r2.env; set +a
# IDEMPOTENCY: never re-init over an existing repo. Check first.
if restic cat config >/dev/null 2>&1; then
echo "Repository already exists — do NOT re-init (would not overwrite, but never regenerate the password)."
else
restic init # creates the encrypted repo
fi
restic cat config # readable config => keys + password work; expect "version":2
"version":2 confirms repository format v2 — compression is active. If it shows v1 (older binary created it), upgrade in place: restic migrate upgrade_repo_v2.
R2 API token requirements
The R2 API token must have:
- Object Read & Write permission
- a scope that includes this bucket (or all buckets in the account)
- the bucket must exist in the same account whose
account_idis in the endpoint
Troubleshooting init (AccessDenied / connection)
| Symptom | Cause | Fix |
|---|---|---|
AccessDenied |
token lacks Object Read & Write, or scope excludes the bucket | re-issue the R2 token with Object Read & Write covering the bucket |
AccessDenied but token looks right |
account_id in the endpoint ≠ the token's account |
match the endpoint's account id to the token's account (the #1 cause) |
| signature / region errors | missing region | AWS_DEFAULT_REGION=auto (or -o s3.region=auto) |
| bucket lookup / vhost failures | provider needs path-style | add -o s3.bucket-lookup=path |
NoSuchBucket |
bucket not created | create the bucket in the R2 dashboard first |
SignatureDoesNotMatch |
clock skew or wrong secret | sync time (timedatectl), re-check the secret key |
Fix R2 tokens in the Cloudflare dashboard → R2 → Manage R2 API Tokens.
R2 cost & lifecycle
- R2 has zero egress fees; you pay for storage + Class A/B operations. restic's pack format keeps operation counts reasonable.
- CRITICAL — never set a bucket lifecycle rule that deletes or expires objects. restic owns retention via
forget --prune(seebackup-script); any external deletion corrupts the repository. A lifecycle rule that only aborts incomplete multipart uploads is safe and recommended.
Encryption at rest
restic encrypts everything client-side before upload, so the objects in R2 are already ciphertext. Enabling R2/S3 SSE on top is harmless but redundant — it does not protect you if the restic password is lost.
Gotchas
- Password loss = total, unrecoverable loss. Force an off-server copy before
init. - Idempotency: check
restic cat configbeforeinit; never regenerate the password over an existing repo — you'd lose access to every prior snapshot. - Both
/root/.restic/passwordand/root/.restic/r2.envmust be mode 600, root-owned. Never commitr2.envto git. - Repo format v2 / compression needs restic ≥ 0.14 (see
setup).
What this skill does NOT cover
- Choosing what to back up →
discover-backup-sources - The backup script and retention →
backup-script - Restoring →
disaster-recovery