Skip to content

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

ItemToolStorage Location
PostgreSQL databasepg_dumpLocal bind-mount ../backups-docker/YYYY-MM-DD_HHMMSS/db.dump
AIV repository folderrestic snapshotsAmazon 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 the docker-aiv-main_default network
  • An AWS account with permission to create S3 buckets and IAM users
  • The backup/ folder from this project

Step 1 — Create an S3 Bucket

  1. Sign in to the AWS Management Console.

  2. Go to S3 → Buckets → Create bucket.

  3. Fill in the settings:

    SettingRecommended value
    Bucket nameaiv-restic-backup (must be globally unique)
    AWS RegionChoose the region closest to your server
    Object OwnershipACLs disabled (recommended)
    Block all public accessEnabled (keep default)
    VersioningOptional — restic manages its own snapshots
    Default encryptionSSE-S3 or SSE-KMS (optional extra layer)
  4. 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

  1. Go to IAM → Policies → Create policy.
  2. Switch to the JSON tab and paste the following (replace aiv-restic-backup with 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/*"
      ]
    }
  ]
}
  1. Name the policy AIVResticS3Policy and click Create policy.

2b — Create the IAM user

  1. Go to IAM → Users → Create user.
  2. Set the username to aiv-backup-user (or any name you prefer).
  3. On the Permissions step, choose Attach policies directly and select AIVResticS3Policy.
  4. Complete the wizard and click Create user.

2c — Generate access keys

  1. Open the newly created user and go to the Security credentials tab.
  2. Click Create access key → Application running outside AWS.
  3. 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 .env files containing real credentials to version control. Add .env to your .gitignore.


Step 4 — Start the Backup Service

cd backup
docker compose up -d --build

On first start the container will:

  1. Detect that the restic repository does not yet exist in S3.
  2. Run restic init to initialise the repository (creates the config, keys/, index/, data/, and snapshots/ prefixes in your bucket).
  3. Immediately run the first full backup.
  4. Schedule subsequent backups every BACKUP_INTERVAL seconds.

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

  1. Open S3 → aiv-restic-backup.
  2. 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 restore merges 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

VariableExample valueDescription
USE_RESTIC_SNAPSHOTtrueMust be true to enable restic (S3 or local)
RESTIC_REPOs3:https://s3.us-east-1.amazonaws.com/aiv-restic-backupFull restic S3 repository URL
AWS_ACCESS_KEY_IDAKIAIOSFODNN7EXAMPLEIAM access key ID
AWS_SECRET_ACCESS_KEYwJalrXUtnFEMI/...IAM secret access key
AWS_DEFAULT_REGIONus-east-1AWS region of the S3 bucket
RESTIC_PASSWORDyour-strong-encryption-passwordRestic repository encryption password
BACKUP_INTERVAL3600Seconds between automatic backups
MAX_PG_DUMP_BACKUPS5Number of local pg_dump folders to keep
MAX_RESTIC_SNAPSHOTS5Number of restic snapshots to keep in S3

S3 URL format

s3:https://s3.<region>.amazonaws.com/<bucket-name>

Examples:

RegionURL
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

ActionCommand
Start backup servicedocker compose up -d --build
Stop backup servicedocker compose down
View backup logsdocker logs aiv-backup
Take backup nowdocker exec aiv-backup bash -c "cd /opt/backup && bash backup.sh"
List snapshotsdocker 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 filesdocker exec ... aiv-backup restic ls <SNAPSHOT_ID>
Restore repositorydocker exec ... aiv-backup restic restore <SNAPSHOT_ID> --target /data/repo
List local db dumpsdocker exec aiv-backup ls /backups
Restore databasedocker exec -e PGPASSWORD="postgres" aiv-backup pg_restore -h postgres -p 5432 -U postgres -d postgres --clean /backups/<TIMESTAMP>/db.dump