Affected files: .obsidian/workspace.json 2 Personal/Home Lab/NAS/immich_v1.1_setup.md
772 lines
14 KiB
Markdown
772 lines
14 KiB
Markdown
# Immich V1 setup on Proxmox + Synology NAS + Authentik + Pangolin
|
|
|
|
## Status
|
|
|
|
This document is the **ground truth** for the current Immich V1 deployment and the first troubleshooting reference.
|
|
|
|
Current state:
|
|
|
|
- **Platform:** Proxmox VM
|
|
- **Guest OS:** Debian 13 server, headless
|
|
- **Networking:** LAN IP via DHCP reservation, plus ZeroTier installed
|
|
- **Container runtime:** Docker + Docker Compose
|
|
- **Immich deployment:** official Immich `docker-compose.yml`
|
|
- **Storage model:**
|
|
- **media/library on Synology NAS via NFS**
|
|
- **Postgres on local VM storage**
|
|
- **Planned next steps:** Authentik OIDC login, Pangolin public reverse proxy, Synology snapshots verification
|
|
|
|
---
|
|
|
|
## Why this architecture
|
|
|
|
This is the correct V1 shape because:
|
|
|
|
- Immich recommends a **full VM** in virtualized environments, not Docker in LXC.
|
|
- Immich recommends **Docker Compose** for normal deployment.
|
|
- Immich explicitly states that the **Postgres database should stay on local SSD storage and not on a network share**.
|
|
- Synology **Btrfs snapshots** are a good fit for the media share.
|
|
- NFS is a cleaner Linux-to-Linux storage mount than SMB for this use case.
|
|
|
|
---
|
|
|
|
## Current implemented architecture
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
subgraph Users
|
|
U1[Browser users]
|
|
U2[Immich mobile app users]
|
|
U3[Admin via SSH]
|
|
end
|
|
|
|
subgraph Edge
|
|
ZT[ZeroTier]
|
|
PG[Pangolin - planned]
|
|
end
|
|
|
|
subgraph Proxmox
|
|
VM[Debian 13 VM\nimmich-vm]
|
|
DC[Docker Compose]
|
|
IS[Immich Server]
|
|
IML[Immich ML]
|
|
R[(Redis)]
|
|
DB[(Postgres\nlocal disk)]
|
|
CFG[/opt/immich-app\ncompose + .env/]
|
|
NFSM[/mnt/immich-prod\nNFS mount/]
|
|
end
|
|
|
|
subgraph Synology
|
|
SHARE[Shared folder: immich-prod]
|
|
SNAP[Snapshots - to configure/verify]
|
|
end
|
|
|
|
subgraph Identity
|
|
AK[Authentik - planned]
|
|
end
|
|
|
|
U3 --> ZT --> VM
|
|
U1 --> PG --> IS
|
|
U2 --> PG --> IS
|
|
VM --> DC
|
|
DC --> IS
|
|
DC --> IML
|
|
DC --> R
|
|
DC --> DB
|
|
IS --> NFSM
|
|
IML --> NFSM
|
|
NFSM --> SHARE
|
|
SHARE --> SNAP
|
|
IS --> AK
|
|
```
|
|
|
|
---
|
|
|
|
## VM build decisions
|
|
|
|
### Guest type
|
|
|
|
- **Debian 13 server/headless**
|
|
- No desktop environment
|
|
- SSH server installed
|
|
- Standard system utilities installed
|
|
|
|
### Proxmox VM settings used/recommended
|
|
|
|
- **Machine type:** `q35`
|
|
- **BIOS:** `OVMF (UEFI)`
|
|
- **Graphics:** default / minimal, no desktop needed
|
|
- **CPU type:** ideally `host`
|
|
- acceptable fallback: `x86-64-v2-AES`
|
|
- **vCPU:** 4
|
|
- **RAM:** 8 GB recommended
|
|
- **Disk:** local SSD-backed VM disk, enough for OS + Docker + Postgres
|
|
- good V1 default: **64 GB**
|
|
- **NIC model:** VirtIO
|
|
|
|
### Why
|
|
|
|
- `q35` + `OVMF` is the modern sane default.
|
|
- Debian headless keeps the VM simple and low-maintenance.
|
|
- Immich itself does not need a GUI on the host.
|
|
- Local disk is used for DB because the DB must **not** live on NFS.
|
|
|
|
---
|
|
|
|
## Directory layout
|
|
|
|
### On the VM
|
|
|
|
```text
|
|
/opt/immich-app/
|
|
├── docker-compose.yml
|
|
├── .env
|
|
└── postgres/
|
|
|
|
/mnt/immich-prod/
|
|
├── library/
|
|
└── model-cache/ # optional, if you keep ML cache here
|
|
```
|
|
|
|
### Why this layout
|
|
|
|
- `/opt/immich-app` is for the **application deployment**, not user files.
|
|
- `/mnt/immich-prod` is the mounted NAS share.
|
|
- `postgres/` stays on **local VM storage**.
|
|
- Do **not** put the project under `/home/cef/...` for production-style operation.
|
|
|
|
---
|
|
|
|
## Installed packages / components
|
|
|
|
Installed on the VM:
|
|
|
|
- `docker`
|
|
- `docker compose`
|
|
- `zerotier`
|
|
- `nfs-common`
|
|
- `sudo`
|
|
|
|
Useful verification commands:
|
|
|
|
```bash
|
|
docker --version
|
|
docker compose version
|
|
zerotier-cli info
|
|
showmount -e 192.168.1.34
|
|
mount | grep immich
|
|
```
|
|
|
|
---
|
|
|
|
## Synology NFS setup
|
|
|
|
### NAS
|
|
|
|
- **Synology IP:** `192.168.1.34`
|
|
- **Shared folder / export:** `/volume1/immich-prod`
|
|
- **Allowed client:** `192.168.1.52`
|
|
|
|
Verified export list:
|
|
|
|
```bash
|
|
sudo showmount -e 192.168.1.34
|
|
```
|
|
|
|
Expected output:
|
|
|
|
```text
|
|
Export list for 192.168.1.34:
|
|
/volume1/Downloads 192.168.1.35/24
|
|
/volume1/immich-prod 192.168.1.52
|
|
```
|
|
|
|
### VM mountpoint
|
|
|
|
```bash
|
|
sudo mkdir -p /mnt/immich-prod
|
|
```
|
|
|
|
### Manual mount command
|
|
|
|
```bash
|
|
sudo mount -t nfs 192.168.1.34:/volume1/immich-prod /mnt/immich-prod
|
|
```
|
|
|
|
### Important typo that already happened once
|
|
|
|
Wrong:
|
|
|
|
```bash
|
|
sudo mount -t nfs 192.168.1.34:/volumel/immich-prod /mnt/immich-prod
|
|
```
|
|
|
|
Correct:
|
|
|
|
```bash
|
|
sudo mount -t nfs 192.168.1.34:/volume1/immich-prod /mnt/immich-prod
|
|
```
|
|
|
|
The mistake was **`volumel`** with letter `l` instead of **`volume1`** with number `1`.
|
|
|
|
### Recommended persistent mount in `/etc/fstab`
|
|
|
|
Use this:
|
|
|
|
```fstab
|
|
192.168.1.34:/volume1/immich-prod /mnt/immich-prod nfs rw,hard,_netdev,x-systemd.automount,noatime 0 0
|
|
```
|
|
|
|
Then test it:
|
|
|
|
```bash
|
|
sudo mount -a
|
|
mount | grep immich-prod
|
|
df -h | grep immich-prod
|
|
```
|
|
|
|
### Why these mount options
|
|
|
|
- `rw` -> read/write
|
|
- `hard` -> keep retrying if the NAS drops briefly
|
|
- `_netdev` -> network-dependent mount
|
|
- `x-systemd.automount` -> avoids ugly boot timing issues
|
|
- `noatime` -> reduces metadata writes
|
|
|
|
---
|
|
|
|
## Immich deployment files
|
|
|
|
### Project directory
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
```
|
|
|
|
### Compose file
|
|
|
|
Use the **official Immich release** `docker-compose.yml` unchanged unless there is a specific reason to change it.
|
|
|
|
This is important because:
|
|
|
|
- the official file stays aligned with the current release
|
|
- random blog versions drift
|
|
- hand-written compose files become future maintenance debt
|
|
|
|
### Current `.env`
|
|
|
|
Current deployment values:
|
|
|
|
```dotenv
|
|
# You can find documentation for all the supported env variables at https://docs.immich.app/install/environment-variables
|
|
|
|
# The location where your uploaded files are stored
|
|
UPLOAD_LOCATION=/mnt/immich-prod/library
|
|
|
|
# The location where your database files are stored
|
|
# MUST stay on local VM storage, not on NFS
|
|
DB_DATA_LOCATION=/opt/immich-app/postgres
|
|
|
|
# Set your timezone
|
|
TZ=Europe/Zurich
|
|
|
|
# Immich Settings
|
|
IMMICH_ENV=production
|
|
|
|
# Immich version
|
|
IMMICH_VERSION=v2
|
|
|
|
# Database credentials
|
|
# Use only A-Za-z0-9 for this value
|
|
DB_PASSWORD=my-secret-pw. # See Lastpass
|
|
|
|
# Usually leave these as default unless you have a reason to change them
|
|
DB_USERNAME=postgres
|
|
DB_DATABASE_NAME=immich
|
|
```
|
|
|
|
### Why these values are correct
|
|
|
|
- `UPLOAD_LOCATION=/mnt/immich-prod/library`
|
|
- correct because media belongs on the NAS share
|
|
- `DB_DATA_LOCATION=/opt/immich-app/postgres`
|
|
- correct because DB must stay local
|
|
- `TZ=Europe/Zurich`
|
|
- good operational default
|
|
- `IMMICH_ENV=production`
|
|
- correct for this VM
|
|
- `IMMICH_VERSION=v2`
|
|
- matches current official release convention
|
|
|
|
---
|
|
|
|
## Useful Docker commands
|
|
|
|
### Start / recreate stack
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
docker compose up -d
|
|
```
|
|
|
|
### Stop stack
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
docker compose down
|
|
```
|
|
|
|
### See running containers
|
|
|
|
```bash
|
|
docker ps
|
|
```
|
|
|
|
### Follow logs
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
docker compose logs -f
|
|
```
|
|
|
|
### Follow a single service log
|
|
|
|
```bash
|
|
docker compose logs -f immich-server
|
|
docker compose logs -f database
|
|
docker compose logs -f redis
|
|
docker compose logs -f immich-machine-learning
|
|
```
|
|
|
|
### Restart stack
|
|
|
|
```bash
|
|
docker compose restart
|
|
```
|
|
|
|
### Pull updated images later
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
docker compose pull
|
|
docker compose up -d
|
|
```
|
|
|
|
---
|
|
|
|
## Current data model
|
|
|
|
### Media
|
|
|
|
Stored on NAS via:
|
|
|
|
```text
|
|
/mnt/immich-prod/library
|
|
```
|
|
|
|
This means:
|
|
|
|
- media is on Synology storage
|
|
- Synology snapshots can protect it
|
|
- the actual photos/videos do **not** live on the VM disk
|
|
|
|
### Database
|
|
|
|
Stored locally via:
|
|
|
|
```text
|
|
/opt/immich-app/postgres
|
|
```
|
|
|
|
This means:
|
|
|
|
- DB is not exposed to NFS consistency problems
|
|
- VM backup strategy must include this path
|
|
- DB and media are separate backup concerns
|
|
|
|
### Automatic Immich DB backups
|
|
|
|
Immich also stores automatic DB dumps under the upload location, typically in:
|
|
|
|
```text
|
|
UPLOAD_LOCATION/backups
|
|
```
|
|
|
|
Those backups contain **metadata only**, not the photo/video files.
|
|
|
|
So:
|
|
|
|
- NAS snapshots help protect media and DB dump files
|
|
- but media + DB are still two separate pieces of the system
|
|
|
|
---
|
|
|
|
## Synology snapshots
|
|
|
|
### Goal
|
|
|
|
The `immich-prod` shared folder should have Synology snapshots enabled.
|
|
|
|
### Why
|
|
|
|
This gives:
|
|
|
|
- fast rollback after accidental deletion
|
|
- protection against bad imports or user mistakes
|
|
- short-term recovery without touching full backups
|
|
|
|
### Good V1 retention suggestion
|
|
|
|
- hourly snapshots for 24 hours
|
|
- daily snapshots for 14 days
|
|
- weekly snapshots for 8 weeks
|
|
|
|
### Important truth
|
|
|
|
Snapshots are **not enough by themselves**.
|
|
They are rollback protection, not the full backup strategy.
|
|
|
|
The real backup picture later must include:
|
|
|
|
- Synology media share backup offsite
|
|
- VM / Postgres backup
|
|
- restore testing
|
|
|
|
---
|
|
|
|
## Authentik plan
|
|
|
|
Planned next phase:
|
|
|
|
- configure Immich login through **Authentik OIDC**
|
|
- keep local Immich login enabled until OIDC is proven working
|
|
|
|
### Important future redirect URIs
|
|
|
|
When creating the Immich OIDC application/provider in Authentik, include:
|
|
|
|
```text
|
|
app.immich:///oauth-callback
|
|
https://immich.<your-domain>/auth/login
|
|
https://immich.<your-domain>/user-settings
|
|
```
|
|
|
|
The mobile callback is required for app login.
|
|
|
|
### Safe rollout rule
|
|
|
|
Do this in order:
|
|
|
|
1. verify local Immich admin login works
|
|
2. configure Authentik OIDC
|
|
3. test browser login
|
|
4. test mobile login
|
|
5. only then consider disabling password login
|
|
|
|
---
|
|
|
|
## Pangolin plan
|
|
|
|
Planned next phase:
|
|
|
|
- expose Immich publicly through **Pangolin**
|
|
- do **not** expose port `2283` directly to the internet
|
|
|
|
### Reverse proxy requirements for Immich
|
|
|
|
The reverse proxy must correctly pass:
|
|
|
|
- `Host`
|
|
- `X-Real-IP`
|
|
- `X-Forwarded-Proto`
|
|
- `X-Forwarded-For`
|
|
|
|
It also must:
|
|
|
|
- allow **large uploads**
|
|
- serve Immich on the **root of a subdomain**, not a sub-path
|
|
|
|
Correct:
|
|
|
|
```text
|
|
https://immich.example.com
|
|
```
|
|
|
|
Wrong:
|
|
|
|
```text
|
|
https://example.com/immich
|
|
```
|
|
|
|
---
|
|
|
|
## Networking notes
|
|
|
|
### DHCP reservation issue already seen
|
|
|
|
The VM originally still held the old IP lease after a router DHCP reservation change.
|
|
|
|
Fastest fix was simply:
|
|
|
|
```bash
|
|
su -
|
|
reboot
|
|
```
|
|
|
|
Reason:
|
|
|
|
- DHCP reservation does not always force immediate address change
|
|
- the client often keeps the old lease until renew/reboot/expiry
|
|
|
|
### Current relevant IPs
|
|
|
|
- **Synology NAS:** `192.168.1.34`
|
|
- **Immich VM:** `192.168.1.52`
|
|
|
|
---
|
|
|
|
## Troubleshooting logbook
|
|
|
|
### Problem: `sudo: command not found`
|
|
|
|
Cause:
|
|
|
|
- user did not yet have sudo available / configured
|
|
|
|
Fix:
|
|
|
|
```bash
|
|
su -
|
|
apt update
|
|
apt install sudo
|
|
usermod -aG sudo cef
|
|
reboot
|
|
```
|
|
|
|
---
|
|
|
|
### Problem: `apt update` permission denied / lock file errors
|
|
|
|
Cause:
|
|
|
|
- command was run as non-root user without sudo
|
|
|
|
Fix:
|
|
|
|
```bash
|
|
su -
|
|
apt update
|
|
```
|
|
|
|
---
|
|
|
|
### Problem: `showmount: command not found`
|
|
|
|
Cause:
|
|
|
|
- NFS client tools not installed yet
|
|
|
|
Fix:
|
|
|
|
```bash
|
|
sudo apt update
|
|
sudo apt install -y nfs-common
|
|
```
|
|
|
|
Then:
|
|
|
|
```bash
|
|
sudo showmount -e 192.168.1.34
|
|
```
|
|
|
|
---
|
|
|
|
### Problem: `mkdir: cannot create directory '/mnt/immich-prod': Permission denied`
|
|
|
|
Cause:
|
|
|
|
- `/mnt` requires root privileges
|
|
|
|
Fix:
|
|
|
|
```bash
|
|
sudo mkdir -p /mnt/immich-prod
|
|
```
|
|
|
|
---
|
|
|
|
### Problem: `mount.nfs: access denied by server while mounting ...`
|
|
|
|
Actual cause in this case:
|
|
|
|
- typo in mount source path: used `volumel` instead of `volume1`
|
|
|
|
Correct command:
|
|
|
|
```bash
|
|
sudo mount -t nfs 192.168.1.34:/volume1/immich-prod /mnt/immich-prod
|
|
```
|
|
|
|
If it happens again with the correct path, then check:
|
|
|
|
1. Synology NFS service enabled
|
|
2. Synology export path correct
|
|
3. Synology NFS permissions allow `192.168.1.52`
|
|
4. VM actually has IP `192.168.1.52`
|
|
5. export visible via `showmount -e 192.168.1.34`
|
|
|
|
---
|
|
|
|
### Problem: changed router DHCP reservation but VM kept old IP
|
|
|
|
Cause:
|
|
|
|
- client kept existing lease
|
|
|
|
Fix:
|
|
|
|
```bash
|
|
su -
|
|
reboot
|
|
```
|
|
|
|
Alternative debugging commands:
|
|
|
|
```bash
|
|
ip a
|
|
networkctl
|
|
systemctl status systemd-networkd
|
|
```
|
|
|
|
---
|
|
|
|
### Problem: hidden `.env` file does not show in normal `ls`
|
|
|
|
Cause:
|
|
|
|
- dotfiles are hidden by default
|
|
|
|
Fix:
|
|
|
|
```bash
|
|
ls -la /opt/immich-app
|
|
```
|
|
|
|
---
|
|
|
|
## Operational rules
|
|
|
|
### Rules to keep
|
|
|
|
1. **Never move Postgres onto the NAS share.**
|
|
2. **Keep using the official Immich compose file.**
|
|
3. **Do not improvise custom Dockerfiles for V1.**
|
|
4. **Do not expose raw Immich directly to the internet.** Use Pangolin.
|
|
5. **Do not disable local login until Authentik login is proven working.**
|
|
6. **Keep the Synology shared folder dedicated to Immich.**
|
|
7. **Treat snapshots as rollback, not as the only backup.**
|
|
|
|
### Rules for changes later
|
|
|
|
If changing `.env` or updating Immich:
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
docker compose pull
|
|
docker compose up -d
|
|
```
|
|
|
|
If something acts weird after env changes:
|
|
|
|
```bash
|
|
docker compose up -d --force-recreate
|
|
```
|
|
|
|
---
|
|
|
|
## What still needs to be done
|
|
|
|
### Must do next
|
|
|
|
1. verify `/etc/fstab` persistent NFS mount works across reboot
|
|
2. verify Synology snapshots are enabled on `immich-prod`
|
|
3. configure Authentik OIDC
|
|
4. configure Pangolin public access
|
|
5. test large upload through the reverse proxy
|
|
6. test Immich mobile login through OIDC
|
|
|
|
### Should do soon
|
|
|
|
1. rotate the DB password because it was shared in chat
|
|
2. document exact Pangolin config once implemented
|
|
3. document exact Authentik provider/app config once implemented
|
|
4. create first restore notes for:
|
|
- Immich DB
|
|
- media share
|
|
- full VM restore
|
|
|
|
---
|
|
|
|
## Fast command reference
|
|
|
|
### Check mount
|
|
|
|
```bash
|
|
mount | grep immich
|
|
df -h | grep immich
|
|
ls -la /mnt/immich-prod
|
|
```
|
|
|
|
### Check Docker
|
|
|
|
```bash
|
|
docker ps
|
|
docker compose logs -f
|
|
```
|
|
|
|
### Restart Immich
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
docker compose restart
|
|
```
|
|
|
|
### Full stack recreate
|
|
|
|
```bash
|
|
cd /opt/immich-app
|
|
docker compose down
|
|
docker compose up -d
|
|
```
|
|
|
|
### Reboot VM
|
|
|
|
```bash
|
|
sudo reboot
|
|
```
|
|
|
|
---
|
|
|
|
## Final blunt summary
|
|
|
|
The current setup is on the right track.
|
|
|
|
The important good decisions already made are:
|
|
|
|
- full VM instead of LXC
|
|
- Debian server instead of desktop bloat
|
|
- official Immich compose
|
|
- media on NAS via NFS
|
|
- Postgres on local storage
|
|
- clear deployment path under `/opt/immich-app`
|
|
|
|
The main things that can still hurt later are:
|
|
|
|
- forgetting to persist and verify the NFS mount properly
|
|
- forgetting to enable/test Synology snapshots
|
|
- breaking login while introducing Authentik
|
|
- exposing Immich badly when adding Pangolin
|
|
- leaving the documented DB password unchanged
|
|
|
|
This document should be updated after every meaningful change.
|