# 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./auth/login https://immich./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.