Skip to content

Commit 7d4c456

Browse files
authored
feat(immich): Add Restic backup (#182)
* feat(backup): Add Restic backup service and configuration * feat(backup): Enhance Rclone backup instructions and clarify usage for various storage backends * feat(backup): Update immich-backup service image to use mazzolino/restic * feat(backup): Add pre and post commands for PostgreSQL database backup in immich-backup service * fix: Rename CRON variable to BACKUP_CRON for clarity in backup schedule configuration * feat(backup): Simplify backup commands in README and update retention policy usage
1 parent 9775078 commit 7d4c456

4 files changed

Lines changed: 287 additions & 1 deletion

File tree

immich/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
*
22
!README.md
33
!docker-compose.yml
4-
!healthcheck
4+
!healthcheck
5+
!backup.env.example

immich/README.md

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,218 @@ Add the necessary DNS records in your domain.
1818
| `IMMICH_UPLOAD_LOCATION` | Path where the assets will be stored | `/mnt/data/photos` |
1919
| `IMMICH_API_KEY` | Immich API key to show information in the homepage | `1000` |
2020
| `IMMICH_DB_PASSWORD` | Postgres database password, change for more security | `postgres` |
21+
22+
## Backup
23+
24+
Immich's database and media files can be backed up to any cloud storage product using [Restic](https://restic.readthedocs.io/) via [resticker](https://github.com/djmaze/resticker).
25+
26+
Restic provides:
27+
- **Incremental backups**: Only changed data is backed up
28+
- **Deduplication**: Identical content across snapshots uses minimal space
29+
- **Encryption**: All data encrypted with your repository password
30+
- **Compression**: Optional, doesn't interfere with deduplication
31+
- **Remote backends**: S3, B2, SFTP, Rclone remotes, and more
32+
33+
### What Gets Backed Up
34+
35+
- **Upload directory** (`${IMMICH_UPLOAD_LOCATION}`): All original photos and videos uploaded by users
36+
- **PostgreSQL database**: Dumped as SQL before each backup
37+
- **Library data**: Library-stored assets (if enabled in Immich settings)
38+
- **User profiles**: User profile images
39+
40+
### What Is Excluded
41+
42+
To save space, the following non-critical data is excluded and can be regenerated if needed:
43+
- **Thumbnails** (`/data/thumbs/`): ~20-30% space savings
44+
- **Encoded videos** (`/data/encoded-video/`): Transcoded versions
45+
- **Auto backups** (`/data/backups/`): Immich's built-in database backups
46+
47+
### Initial Setup
48+
49+
#### 1. Configure Backup Environment
50+
51+
Copy the backup environment template and customize it:
52+
53+
```bash
54+
cp immich/backup.env.example immich/backup.env
55+
```
56+
57+
Edit `immich/backup.env` and set the following:
58+
59+
| Variable | Description | Example |
60+
|------------------------|-----------------------------------------------|----------------------------|
61+
| `RESTIC_REPOSITORY` | Backup destination URI for Restic | See examples below |
62+
| `RESTIC_PASSWORD` | Strong password to encrypt the repository | Generate with `openssl rand -base64 32` |
63+
| `CRON` | Backup schedule (cron format with seconds) | `0 30 3 * * *` (3:30 AM daily) |
64+
| `TIMEZONE` | Timezone for cron scheduling | `America/New_York` |
65+
| `RESTIC_FORGET_ARGS` | Backup retention policy | `--keep-last 7 --keep-daily 7 --keep-weekly 4 --keep-monthly 3` |
66+
67+
#### 2. Choose Your Backup Destination
68+
69+
Restic supports multiple backends. Pick one based on your infrastructure:
70+
71+
**Rclone Remote** (Optional - reuse existing rclone configuration):
72+
73+
Rclone is optional. Use this only if your `RESTIC_REPOSITORY` URI starts with `rclone:`. If you prefer to use Rclone, it allows you to back up to any service supported by Rclone (S3, B2, SFTP, Google Drive, OneDrive, Dropbox, etc.) without direct Restic support, or if you want to reuse existing Rclone configurations.
74+
75+
First, configure your rclone remote destination:
76+
77+
```bash
78+
docker compose run --rm -it immich-backup rclone config
79+
```
80+
81+
This interactive command will guide you through:
82+
1. Creating a new remote (choose `n`)
83+
2. Naming your remote (e.g., `backup-s3`, `backup-b2`, `backup-sftp`, etc.)
84+
3. Selecting your storage type (S3, B2, SFTP, etc.)
85+
4. Entering credentials and configuration
86+
87+
The configuration will be saved to `immich/.rclone/rclone.conf`. Do not manually edit this file; use the `rclone config` command above to modify it.
88+
89+
Then set in `backup.env`:
90+
91+
```bash
92+
RESTIC_REPOSITORY=rclone:myremote:/nas-backups/immich
93+
```
94+
95+
This allows you to use any rclone-supported backend seamlessly.
96+
97+
**S3-Compatible (Direct)** (AWS, Wasabi, MinIO, DigitalOcean Spaces, etc. - no Rclone needed):
98+
99+
You can also back up directly to S3-compatible services without using Rclone:
100+
101+
```bash
102+
# Set in backup.env:
103+
RESTIC_REPOSITORY=s3:s3.amazonaws.com/my-bucket/immich
104+
105+
# Or for S3-compatible services:
106+
RESTIC_REPOSITORY=s3:https://s3.wasabisys.com/my-bucket/immich
107+
108+
# Additional environment variables:
109+
AWS_ACCESS_KEY_ID=your_key
110+
AWS_SECRET_ACCESS_KEY=your_secret
111+
```
112+
113+
**Backblaze B2** (Direct - no Rclone needed):
114+
```bash
115+
# Set in backup.env:
116+
RESTIC_REPOSITORY=b2:my-bucket:immich
117+
118+
# And provide credentials:
119+
B2_ACCOUNT_ID=your_account_id
120+
B2_ACCOUNT_KEY=your_account_key
121+
```
122+
123+
**SFTP** (Direct - no Rclone needed):
124+
```bash
125+
# Set in backup.env:
126+
RESTIC_REPOSITORY=sftp://user@backup.example.com/immich
127+
128+
# Password authentication is interactive or set:
129+
SFTP_PASSWORD=your_sftp_password
130+
```
131+
132+
**Local Path** (NAS mounted volume or local directory - no Rclone needed):
133+
```bash
134+
# Set in backup.env:
135+
RESTIC_REPOSITORY=/mnt/backup-drive/immich
136+
137+
# Ensure the directory exists and is writable:
138+
mkdir -p /mnt/backup-drive/immich
139+
```
140+
141+
**Google Cloud Storage** (Direct - no Rclone needed):
142+
```bash
143+
# Set in backup.env:
144+
RESTIC_REPOSITORY=gs://my-bucket/immich
145+
146+
# And provide credentials (via service account JSON):
147+
GOOGLE_APPLICATION_CREDENTIALS=/path/to/credentials.json
148+
```
149+
150+
**Azure Blob Storage** (Direct - no Rclone needed):
151+
```bash
152+
# Set in backup.env:
153+
RESTIC_REPOSITORY=azure://immich-container/immich
154+
155+
# And provide credentials:
156+
AZURE_ACCOUNT_NAME=myaccount
157+
AZURE_ACCOUNT_KEY=mykey
158+
```
159+
160+
#### 3. Generate a Secure Password
161+
162+
```bash
163+
# Generate a strong random password
164+
openssl rand -base64 32
165+
166+
# Copy the output and paste it into backup.env as RESTIC_PASSWORD
167+
```
168+
169+
**Important**: Store this password securely. You'll need it to restore backups. If lost, your backup data becomes inaccessible.
170+
171+
### Testing the Backup
172+
173+
#### List Existing Snapshots
174+
175+
```bash
176+
docker compose run --rm immich-backup snapshots
177+
```
178+
179+
This will list all backup snapshots. If the repository doesn't exist yet, it will be initialized automatically on the first scheduled backup.
180+
181+
#### Manually Trigger a Backup
182+
183+
Perform a one-time backup:
184+
185+
```bash
186+
docker compose run --rm immich-backup backup /data
187+
```
188+
189+
Then apply the retention policy to clean up old snapshots:
190+
191+
```bash
192+
docker compose run --rm immich-backup c forget --prune --keep-last 7 --keep-daily 7 --keep-weekly 4 --keep-monthly 3
193+
```
194+
195+
(Note: Replace the `--keep-*` arguments with your configured `RESTIC_FORGET_ARGS` from `backup.env`)
196+
197+
#### Check Repository Status
198+
199+
```bash
200+
docker compose run --rm immich-backup check
201+
```
202+
203+
#### Monitor Scheduled Backups
204+
205+
Start the Immich service with the backup profile enabled:
206+
207+
```bash
208+
COMPOSE_PROFILES=immich-backup docker compose up -d
209+
```
210+
211+
Then monitor backup logs:
212+
213+
```bash
214+
docker compose logs -f immich-backup
215+
```
216+
217+
The backup will run automatically according to the schedule in `backup.env` (default: 3:30 AM daily).
218+
219+
### Restoration
220+
221+
For detailed instructions on restoring Immich from backups, see the [Immich Restore Documentation](https://docs.immich.app/administration/backup-and-restore/).
222+
223+
**Quick restore overview**:
224+
1. Stop the Immich service
225+
2. Restore the database dump: `psql -U postgres < db-dump.sql`
226+
3. Restore the upload directory from the Restic snapshot
227+
4. Start Immich again
228+
229+
To restore a specific snapshot:
230+
231+
```bash
232+
docker compose run --rm immich-backup restore <snapshot-id> --target /restored
233+
```
234+
235+
Then copy the files and database from `/restored` back to their original locations.

immich/backup.env.example

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
# Restic backup configuration for Immich
2+
# Configure the backup repository destination and schedule
3+
4+
# Restic repository URL (required)
5+
# Supports all Restic backends. Uncomment the appropriate example for your setup:
6+
#
7+
# Rclone remote (requires prior: docker compose run --rm -it immich-backup rclone config):
8+
# RESTIC_REPOSITORY=rclone:myremote:/nas-backups/immich
9+
#
10+
# S3 (AWS, Wasabi, MinIO, DigitalOcean Spaces, etc.):
11+
# RESTIC_REPOSITORY=s3:s3.amazonaws.com/my-bucket/immich
12+
# RESTIC_REPOSITORY=s3:https://s3-compatible.example.com/bucket/immich
13+
#
14+
# Backblaze B2:
15+
# RESTIC_REPOSITORY=b2:my-bucket:path/to/immich
16+
#
17+
# SFTP:
18+
# RESTIC_REPOSITORY=sftp://user@host/path/to/immich
19+
#
20+
# Local path:
21+
# RESTIC_REPOSITORY=/mnt/backup/immich
22+
#
23+
# Azure Blob Storage:
24+
# RESTIC_REPOSITORY=azure://container/immich
25+
#
26+
# Google Cloud Storage:
27+
# RESTIC_REPOSITORY=gs://bucket-name/immich
28+
RESTIC_REPOSITORY=
29+
30+
# Restic repository password (required)
31+
# Used to encrypt and decrypt the restic backup repository
32+
# Generate a strong password with: openssl rand -base64 32
33+
RESTIC_PASSWORD=
34+
35+
# Backup schedule (cron format with optional seconds)
36+
# Examples:
37+
# 0 30 3 * * * = 3:30 AM daily
38+
# 0 0 4 * * 0 = 4:00 AM every Sunday
39+
# 0 0 */12 * * * = Every 12 hours
40+
BACKUP_CRON=0 30 3 * * *
41+
42+
# Timezone for cron scheduling
43+
# Used to interpret the CRON schedule
44+
TIMEZONE=America/New_York
45+
46+
# Retention policy (backup rotation)
47+
# Keep the last 7 daily snapshots, 4 weekly, and 3 monthly for long-term storage
48+
RESTIC_FORGET_ARGS=--keep-last 7 --keep-daily 7 --keep-weekly 4 --keep-monthly 3

immich/docker-compose.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,5 +68,27 @@ services:
6868
profiles:
6969
- immich
7070

71+
immich-backup:
72+
container_name: immich_backup
73+
image: mazzolino/restic:latest
74+
env_file:
75+
- ${CONFIG_ROOT:-.}/immich/backup.env
76+
environment:
77+
RESTIC_BACKUP_SOURCES: /data
78+
RESTIC_BACKUP_ARGS: --tag immich --exclude /data/thumbs --exclude /data/encoded-video --exclude /data/backups
79+
PRE_COMMANDS: docker exec immich_postgres pg_dumpall -U postgres | gzip > /data/db-dump.sql.gz
80+
POST_COMMANDS_SUCCESS: rm -f /data/db-dump.sql.gz
81+
volumes:
82+
- ${IMMICH_UPLOAD_LOCATION}:/data
83+
- ${CONFIG_ROOT:-.}/immich/postgresql:/postgresql:ro
84+
- /var/run/docker.sock:/var/run/docker.sock:ro
85+
- ${CONFIG_ROOT:-.}/immich/.rclone:/root/.config/rclone:ro
86+
depends_on:
87+
- immich-server
88+
- immich-database
89+
restart: always
90+
profiles:
91+
- immich-backup
92+
7193
volumes:
7294
immich-model-cache:

0 commit comments

Comments
 (0)