AIV Backup and Restore with Amazon S3
This guide explains how to configure the AIV backup system to store restic snapshots in an Amazon S3 bucket instead of a local folder or MinIO. PostgreSQL database dumps continue to be stored on the local bind-mounted volume alongside the S3-backed repository snapshots.
1. How It Works
| Item | Tool | Storage Location |
|---|---|---|
| PostgreSQL database | pg_dump | Local bind-mount ../backups-docker/YYYY-MM-DD_HHMMSS/db.dump |
| AIV repository folder | restic snapshots | Amazon S3 bucket (e.g. s3:https://s3.amazonaws.com/your-bucket) |
The backup container uses restic with its native S3 backend. Restic encrypts all data before uploading — your AWS credentials and the restic encryption password are the only two secrets required.
2. Prerequisites
- Docker Desktop installed and running
- The AIV Docker stack (
docker-aiv-main) running on thedocker-aiv-main_defaultnetwork - An AWS account with permission to create S3 buckets and IAM users
- The
backup/folder from this project
Step 1 — Create an S3 Bucket
-
Sign in to the AWS Management Console.
-
Go to S3 → Buckets → Create bucket.
-
Fill in the settings:
Setting Recommended value Bucket name aiv-restic-backup(must be globally unique)AWS Region Choose the region closest to your server Object Ownership ACLs disabled (recommended) Block all public access Enabled (keep default) Versioning Optional — restic manages its own snapshots Default encryption SSE-S3 or SSE-KMS (optional extra layer) -
Click Create bucket.
Tip: Note the bucket name and region — you will need both in Step 3.
Step 2 — Create an IAM User and Policy
Restic only needs a narrow set of S3 permissions. Creating a dedicated IAM user limits the blast radius if credentials are ever exposed.
2a — Create the IAM policy
- Go to IAM → Policies → Create policy.
- Switch to the JSON tab and paste the following (replace
aiv-restic-backupwith your bucket name):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ResticS3Access",
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject",
"s3:DeleteObject",
"s3:ListBucket",
"s3:GetBucketLocation"
],
"Resource": [
"arn:aws:s3:::aiv-restic-backup",
"arn:aws:s3:::aiv-restic-backup/*"
]
}
]
}
- Name the policy
AIVResticS3Policyand click Create policy.
2b — Create the IAM user
- Go to IAM → Users → Create user.
- Set the username to
aiv-backup-user(or any name you prefer). - On the Permissions step, choose Attach policies directly and select
AIVResticS3Policy. - Complete the wizard and click Create user.
2c — Generate access keys
- Open the newly created user and go to the Security credentials tab.
- Click Create access key → Application running outside AWS.
- Copy the Access key ID and Secret access key — you will not be able to view the secret again.
Step 3 — Configure the Backup Service
Open the .env file in the project root (or backup/conf/backup.conf for the Docker backup service) and set the following variables:
# ── Amazon S3 settings ──────────────────────────────────────────────
# Set to true to use S3 instead of a local restic repo
USE_RESTIC_SNAPSHOT=true
# Full restic S3 repository URL
# Format: s3:https://s3.<region>.amazonaws.com/<bucket-name>
RESTIC_REPO=s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup
# AWS credentials for the IAM user created in Step 2
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
# AWS region (must match the bucket region)
AWS_DEFAULT_REGION=us-east-1
# Restic encryption password — change this to something strong and store it safely
RESTIC_PASSWORD=your-strong-encryption-password
# ── Retention settings ───────────────────────────────────────────────
BACKUP_INTERVAL=3600 # seconds between automatic backups (3600 = 1 hour)
MAX_PG_DUMP_BACKUPS=5 # number of pg_dump folders to keep locally
MAX_RESTIC_SNAPSHOTS=5 # number of restic snapshots to keep in S3
Security note: Never commit
.envfiles containing real credentials to version control. Add.envto your.gitignore.
Step 4 — Start the Backup Service
cd backup
docker compose up -d --build
On first start the container will:
- Detect that the restic repository does not yet exist in S3.
- Run
restic initto initialise the repository (creates theconfig,keys/,index/,data/, andsnapshots/prefixes in your bucket). - Immediately run the first full backup.
- Schedule subsequent backups every
BACKUP_INTERVALseconds.
Step 5 — Verify the First Backup
Check container logs
docker logs aiv-backup
Expected output:
=== Full backup started at 2026-05-28_100000 ===
Starting pg_dump...
Starting restic backup...
snapshot a1b2c3d4 saved
Applying restic retention (keep last 5 snapshots, prune)...
pg_dump finished.
=== Backup completed: /backups/2026-05-28_100000 ===
Verify in the AWS Console
- Open S3 → aiv-restic-backup.
- You should see the following prefixes created by restic:
data/
index/
keys/
snapshots/
config
Taking a Manual Backup
Trigger an immediate backup without waiting for the schedule:
docker exec aiv-backup bash -c "cd /opt/backup && bash backup.sh"
Listing Snapshots
List all restic snapshots stored in S3:
docker exec ^
-e RESTIC_REPOSITORY="s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup" ^
-e AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" ^
-e AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ^
-e AWS_DEFAULT_REGION="us-east-1" ^
-e RESTIC_PASSWORD="your-strong-encryption-password" ^
aiv-backup restic snapshots
Sample output:
ID Time Host Tags Paths
------------------------------------------------------------------------------------------
b043bc18 2026-05-28 10:00:00 aiv-host backup-2026-05-28_100000,repo /data/repo
f93345e6 2026-05-28 11:00:00 aiv-host backup-2026-05-28_110000,repo /data/repo
41714ac9 2026-05-28 12:00:00 aiv-host backup-2026-05-28_120000,repo /data/repo
------------------------------------------------------------------------------------------
3 snapshots
Restoring from S3
Warning: Restoring overwrites the current AIV repository folder and PostgreSQL database. Take a manual backup first if you have unsaved work.
Step 1 — List snapshots and choose a restore point
docker exec ^
-e RESTIC_REPOSITORY="s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup" ^
-e AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" ^
-e AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ^
-e AWS_DEFAULT_REGION="us-east-1" ^
-e RESTIC_PASSWORD="your-strong-encryption-password" ^
aiv-backup restic snapshots
Note the snapshot ID and timestamp of the point you want to restore to.
Step 2 — (Optional) Browse files inside a snapshot
Confirm the files you need are present before restoring:
docker exec ^
-e RESTIC_REPOSITORY="s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup" ^
-e AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" ^
-e AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ^
-e AWS_DEFAULT_REGION="us-east-1" ^
-e RESTIC_PASSWORD="your-strong-encryption-password" ^
aiv-backup restic ls <SNAPSHOT_ID>
Step 3 — Restore the repository
docker exec ^
-e RESTIC_REPOSITORY="s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup" ^
-e AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" ^
-e AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ^
-e AWS_DEFAULT_REGION="us-east-1" ^
-e RESTIC_PASSWORD="your-strong-encryption-password" ^
aiv-backup restic restore <SNAPSHOT_ID> --target /data/repo
Replace <SNAPSHOT_ID> with the ID from Step 1. For example:
docker exec ^
-e RESTIC_REPOSITORY="s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup" ^
-e AWS_ACCESS_KEY_ID="AKIAIOSFODNN7EXAMPLE" ^
-e AWS_SECRET_ACCESS_KEY="wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" ^
-e AWS_DEFAULT_REGION="us-east-1" ^
-e RESTIC_PASSWORD="your-strong-encryption-password" ^
aiv-backup restic restore 41714ac9 --target /data/repo
restic restoremerges files into the target directory — it restores missing or changed files without deleting files added after the snapshot.
Step 4 — Restore the database (optional)
If you also need to roll back the PostgreSQL database, use the matching db.dump file stored locally.
List available dumps:
docker exec aiv-backup ls /backups
Restore the database:
docker exec ^
-e PGPASSWORD="postgres" ^
aiv-backup pg_restore -h postgres -p 5432 -U postgres -d postgres --clean /backups/<TIMESTAMP>/db.dump
Replace <TIMESTAMP> with the folder name that matches your chosen restore point (e.g. 2026-05-28_100000).
Step 5 — Restart AIV
After any restore, restart the AIV application so it clears its in-memory cache and reads the restored data:
docker compose restart
Stopping and Starting
Stop the backup service:
cd backup
docker compose down
Start again:
docker compose up -d
Restic data lives in S3 — it is not affected by stopping or removing the backup container.
Configuration Reference
| Variable | Example value | Description |
|---|---|---|
USE_RESTIC_SNAPSHOT | true | Must be true to enable restic (S3 or local) |
RESTIC_REPO | s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup | Full restic S3 repository URL |
AWS_ACCESS_KEY_ID | AKIAIOSFODNN7EXAMPLE | IAM access key ID |
AWS_SECRET_ACCESS_KEY | wJalrXUtnFEMI/... | IAM secret access key |
AWS_DEFAULT_REGION | us-east-1 | AWS region of the S3 bucket |
RESTIC_PASSWORD | your-strong-encryption-password | Restic repository encryption password |
BACKUP_INTERVAL | 3600 | Seconds between automatic backups |
MAX_PG_DUMP_BACKUPS | 5 | Number of local pg_dump folders to keep |
MAX_RESTIC_SNAPSHOTS | 5 | Number of restic snapshots to keep in S3 |
S3 URL format
s3:https://s3.<region>.amazonaws.com/<bucket-name>
Examples:
| Region | URL |
|---|---|
| US East (N. Virginia) | s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup |
| EU (Ireland) | s3:https://s3.eu-west-1.amazonaws.com/aiv-restic-backup |
| Asia Pacific (Singapore) | s3:https://s3.ap-southeast-1.amazonaws.com/aiv-restic-backup |
Troubleshooting
NoCredentialProviders or InvalidAccessKeyId error
Verify AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are set correctly in .env and that the container was restarted after the change.
NoSuchBucket error
The bucket name in RESTIC_REPO does not match the bucket you created. Check for typos and confirm the bucket exists in the correct region.
AccessDenied error
The IAM user is missing one or more required permissions. Re-check the IAM policy in Step 2 and ensure it is attached to the correct user.
Fatal: unable to open config file on first run
This is normal — restic prints this message before initialising a new repository. The container automatically runs restic init on first start. If the error persists after init, check your credentials and bucket permissions.
Snapshots are not being pruned
Confirm MAX_RESTIC_SNAPSHOTS is set to a positive integer in .env and that the container was restarted after the change.
pg_dump fails with authentication error
Verify AIV_DB_USER and AIV_DB_PASSWORD in .env match the values used by the db service in application/docker-compose.yml.
Quick Reference
| Action | Command |
|---|---|
| Start backup service | docker compose up -d --build |
| Stop backup service | docker compose down |
| View backup logs | docker logs aiv-backup |
| Take backup now | docker exec aiv-backup bash -c "cd /opt/backup && bash backup.sh" |
| List snapshots | docker exec -e RESTIC_REPOSITORY="s3:https://s3.us-east-1.amazonaws.com/aiv-restic-backup" -e AWS_ACCESS_KEY_ID="..." -e AWS_SECRET_ACCESS_KEY="..." -e AWS_DEFAULT_REGION="us-east-1" -e RESTIC_PASSWORD="..." aiv-backup restic snapshots |
| Browse snapshot files | docker exec ... aiv-backup restic ls <SNAPSHOT_ID> |
| Restore repository | docker exec ... aiv-backup restic restore <SNAPSHOT_ID> --target /data/repo |
| List local db dumps | docker exec aiv-backup ls /backups |
| Restore database | docker exec -e PGPASSWORD="postgres" aiv-backup pg_restore -h postgres -p 5432 -U postgres -d postgres --clean /backups/<TIMESTAMP>/db.dump |