Self‑hosting Immich is straightforward in theory: spin up a container, point it at storage, and let it index your photos. In practice, you can easily end up with:
- Immich indexing its own thumbnails as photos
- File‑system races at boot where the container starts before the NAS is ready
- Confusing folder‑check errors about missing
.immichfiles - GPU passthrough that silently breaks after a kernel update
This post documents a full setup that avoids those problems. It’s based on a real deployment using:
- Proxmox VE 9 on a Minisforum MS‑12 mini‑PC
- Synology DS218+ NAS (Btrfs,
/volume1) - Immich running inside an LXC container on Proxmox
Goals and Architecture
The design aims to:
- Keep Immich application & database on Proxmox local stora…
Self‑hosting Immich is straightforward in theory: spin up a container, point it at storage, and let it index your photos. In practice, you can easily end up with:
- Immich indexing its own thumbnails as photos
- File‑system races at boot where the container starts before the NAS is ready
- Confusing folder‑check errors about missing
.immichfiles - GPU passthrough that silently breaks after a kernel update
This post documents a full setup that avoids those problems. It’s based on a real deployment using:
- Proxmox VE 9 on a Minisforum MS‑12 mini‑PC
- Synology DS218+ NAS (Btrfs,
/volume1) - Immich running inside an LXC container on Proxmox
Goals and Architecture
The design aims to:
- Keep Immich application & database on Proxmox local storage
- Keep all media (uploads, thumbs, encoded video, backups) on the Synology NAS
- Mount NAS storage into Proxmox via NFS, then into the Immich LXC via bind mounts
- Allow Immich to index existing photo trees on the NAS as external libraries
- Survive reboots cleanly (NAS → Proxmox → Immich), without manual intervention
High‑Level Layout
-
Proxmox host:
pve.example.lan(Minisforum MS‑12, Proxmox VE 9) -
NAS:
nas.example.lan(Synology DS218+,/volume1) -
Immich LXC:
-
CTID:
115 -
Hostname:
immich -
IP:
192.168.10.115(example) -
OS: Debian 13
On the NAS:
- Dedicated Immich share:
/volume1/Immich - Existing photos:
/volume1/homes/user/Photos(Synology “homes” feature)
On Proxmox:
/mnt/immich-data→ NFS mount of/volume1/Immich/mnt/nas-homes→ NFS mount of/volume1/homes
Inside the Immich LXC:
/opt/immich→ Immich application/opt/immich/upload→ Immich media root (mapped to/volume1/Immich)/mnt/nas-homes→ read‑only view of/volume1/homesfor external libraries
Step 1 – Prepare NFS Shares on Synology DS218+
On the Synology DSM UI:
- Create a shared folder called
Immich:
- Filesystem: Btrfs (or ext4)
- Path:
/volume1/Immich
- Ensure NFS is enabled in Control Panel → File Services → NFS.
- For Immich and homes shares, add NFS permissions:
- Host:
192.168.10.7(your Proxmox host IP, example) - Privilege: Read/Write
- Squash:
Map all users to adminorMap all users to admin group(depending on your UID mapping strategy) - Security:
sys
From Proxmox you can verify exports:
showmount -e nas.example.lan
You should see something like:
/volume1/Immich 192.168.10.7
/volume1/homes 192.168.10.7
/volume1/Proxmox 192.168.10.7
On the NAS itself:
ssh admin@nas.example.lan
df -h /volume1/Immich /volume1/homes
ls -ld /volume1/Immich /volume1/homes /volume1/homes/user/Photos
Make sure the Immich share is writable and the Photos tree exists.
Step 2 – Mount NFS Shares on Proxmox
On the Proxmox host (pve.example.lan), edit /etc/fstab:
# <file system> <mount point> <type> <options> <dump> <pass>
192.168.10.5:/volume1/homes /mnt/nas-homes nfs defaults,_netdev,noatime,nofail,x-systemd.automount,x-systemd.after=network-online.target 0 0
192.168.10.5:/volume1/Immich /mnt/immich-data nfs defaults,_netdev,noatime,nofail,x-systemd.automount,x-systemd.after=network-online.target 0 0
Replace 192.168.10.5 with your DS218+ IP.
Key options:
_netdev,x-systemd.after=network-online.target– avoid racing the NIC at bootx-systemd.automount– defer actual mounting until first accessnofail– boot even if the NAS is temporarily unavailable
Reload and validate:
systemctl daemon-reload
systemctl status mnt-nas\x2dhomes.mount mnt-immich\x2ddata.mount
df -h /mnt/nas-homes /mnt/immich-data
You should see the NFS paths mounted.
Step 3 – Create and Configure the Immich LXC
Use your preferred Immich installer (e.g. the community Proxmox script) to create a Debian‑based LXC for Immich. Once created, adjust its config in /etc/pve/lxc/115.conf:
hostname: immich
unprivileged: 1
rootfs: local-lvm:vm-115-disk-0,size=40G
mp0: /mnt/immich-data,mp=/opt/immich/upload
mp1: /mnt/nas-homes,mp=/mnt/nas-homes,ro=1
# GPU passthrough (example)
dev0: /dev/dri/renderD128,gid=992
dev1: /dev/dri/card0,gid=44
onboot: 1
memory: 12288
cores: 8
swap: 2048
Notes:
mp0binds the NAS Immich share into the container as/opt/immich/upload.mp1exposes the entirehomestree read‑only for Immich external libraries.- GPU devices (
/dev/dri/renderD128and/dev/dri/card0) may differ on your system – always check:
ls -l /dev/dri
and adjust dev0/dev1 accordingly.
Ensure the Container Waits for NAS
Create a drop‑in to force CT 115 to wait for the homes mount:
mkdir -p /etc/systemd/system/pve-container@115.service.d
cat >/etc/systemd/system/pve-container@115.service.d/wait-nas.conf <<'EOF'
[Unit]
Requires=mnt-nas\x2dhomes.mount
After=mnt-nas\x2dhomes.mount
EOF
systemctl daemon-reload
This avoids the “folder checks failed” situation where Immich starts before NFS is mounted.
Step 4 – Immich In‑Container Configuration
Exec into the container:
pct exec 115 -- bash
Key paths:
- Immich app root:
/opt/immich - Env file:
/opt/immich/.env
The installer should set:
IMMICH_MEDIA_LOCATION=/opt/immich/upload
DB_HOSTNAME=127.0.0.1
DB_USERNAME=immich
DB_PASSWORD=<your_password>
DB_DATABASE_NAME=immich
REDIS_HOSTNAME=127.0.0.1
Required Folder Layout and .immich Sentinels
Immich performs folder checks on startup. It expects the following under IMMICH_MEDIA_LOCATION:
/opt/immich/upload
.immich
backups/.immich
encoded-video/.immich
library/.immich
profile/.immich
thumbs/.immich
upload/.immich
If these are missing or not readable/writable, Immich will refuse to start and you’ll see errors like:
Failed to read: "/encoded-video/.immich ..."
Fixing Folder Checks
Inside CT 115:
rm -rf /opt/immich/upload/*
# Create folders without relying on brace expansion
mkdir -p /opt/immich/upload/backups \
/opt/immich/upload/encoded-video \
/opt/immich/upload/library \
/opt/immich/upload/profile \
/opt/immich/upload/thumbs \
/opt/immich/upload/upload
touch /opt/immich/upload/.immich
touch /opt/immich/upload/backups/.immich
touch /opt/immich/upload/encoded-video/.immich
touch /opt/immich/upload/library/.immich
touch /opt/immich/upload/profile/.immich
touch /opt/immich/upload/thumbs/.immich
touch /opt/immich/upload/upload/.immich
ls -R /opt/immich/upload
Then restart Immich:
systemctl restart immich-web
systemctl restart immich-ml
Check:
systemctl status immich-web immich-ml
ss -tulpn | grep 2283
curl -sS -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:2283/
You should see immich-api listening on *:2283 and HTTP 200.
Step 5 – Clean‑Slate Reset (Optional but Useful)
If you’ve experimented and want to start over cleanly without recreating the container:
WARNING: This wipes all Immich state (users, albums, assets, ML data), but leaves your NAS photos intact.
Inside CT 115:
systemctl stop immich-web immich-ml
sudo -u postgres psql -c "DROP DATABASE IF EXISTS immich;"
sudo -u postgres psql -c "CREATE DATABASE immich OWNER immich;"
Then rebuild the media root as shown above, restart services, and verify the DB is empty:
PGPASSWORD=<DB_PASSWORD> psql -h 127.0.0.1 -U immich -d immich \
-c 'SELECT COUNT(*) AS assets FROM "asset";'
You should get 0 assets – Immich will present the onboarding UI again.
Step 6 – External Libraries: Where People Go Wrong
This is where the biggest “face‑palm” bug came from.
The Anti‑Pattern (What NOT to Do)
- Putting Immich’s media root inside your Photos tree, e.g.:
/volume1/homes/user/Photos/immich
- Then adding
/mnt/nas-homes/user/Photosas an external library.
Result:
-
Immich happily indexes:
-
Your real photos, and
-
Its own
thumbs/andencoded-video/directories. -
You start seeing lots of tiny 106×106 face crops as “photos”.
-
Indexing takes forever and you waste storage.
The Correct Pattern
- Keep Immich’s internal data in its own NAS share:
/volume1/Immich
- Keep your photo library under a separate tree:
/volume1/homes/user/Photos
Inside CT 115, these appear as:
/opt/immich/upload # Immich internal data (uploads, thumbs, encoded, backups, etc.)
/mnt/nas-homes/user/Photos # Real photos for external libraries
Configuring External Libraries in the UI
In the Immich web UI:
- Go to Administration → Libraries.
- Click Create library (or similar).
- For the import path, use in‑container paths; for example:
/mnt/nas-homes/user/Photos
or per‑folder:
/mnt/nas-homes/user/Photos/DJI
/mnt/nas-homes/user/Photos/Moments
/mnt/nas-homes/user/Photos/iPhone XS
- Save the library.
- IMPORTANT: Click Scan / Scan Library.
If you forget step 5, the library will show zero assets even though the path is valid.
You can verify from inside CT 115:
sudo -u postgres psql -d immich -c 'SELECT * FROM "library";'
sudo -u postgres psql -d immich -c 'SELECT COUNT(*) AS assets FROM "asset";'
You should see your library and an increasing asset count while scanning.
GPU Passthrough and VAAPI Gotchas
If your Minisforum box has an Intel iGPU, you can offload video transcoding via VAAPI.
On the Proxmox host:
ls -l /dev/dri
You might see:
card0
renderD128
Map these into the LXC via dev0/dev1 as shown earlier. After a kernel upgrade, the numbering can change (e.g. card1 appears instead of card0), so:
- After updates, always re‑check
/dev/drion the host. - Adjust
/etc/pve/lxc/115.confsodev0/dev1refer to devices that actually exist.
Inside CT 115 you can confirm VAAPI works by inspecting Immich logs and/or running a test ffmpeg command (if installed).
Operational Checklist
This is the condensed runbook I use to keep the setup healthy.
After Proxmox / NAS Reboot
On the Proxmox host:
systemctl status mnt-nas\x2dhomes.mount mnt-immich\x2ddata.mount
df -h /mnt/nas-homes /mnt/immich-data
Check the Immich LXC:
pct list
pct start 115 # if not already running
Inside CT 115:
df -h / /opt/immich/upload /mnt/nas-homes
systemctl status immich-web immich-ml
ss -tulpn | grep 2283
curl -sS -o /dev/null -w "HTTP %{http_code}\n" http://127.0.0.1:2283/
You want:
/opt/immich/uploadand/mnt/nas-homesmounted via NFSimmich-webandimmich-mlactiveimmich-apilistening on*:2283HTTP 200from the local curl
When Adding or Editing External Libraries
- Use only paths under
/mnt/nas-homes/.../Photos... - Never use
/opt/immich/uploador anything under/mnt/immich-data - After saving a library, always click Scan in the Immich UI
- Optionally monitor progress:
sudo -u postgres psql -d immich -c 'SELECT COUNT(*) AS assets FROM "asset";'
When Folder Checks Fail
If you see errors about .immich files in the logs:
- Confirm NFS is mounted (
df -h /opt/immich/upload). - Rebuild the directory structure and
.immichsentinels as shown in Step 4. - Restart
immich-weband re‑check logs.
When You Want a Clean Reset
Follow the clean‑slate procedure:
- Stop services
- Drop & recreate the
immichdatabase - Rebuild
/opt/immich/upload - Start services
- Confirm
assetcount is0 - Recreate external libraries and scan
Conclusion
The key lessons from running Immich on Proxmox with a Synology NAS:
- Separate Immich’s internal data from your photo library – use a dedicated NAS share for Immich (
/volume1/Immich) and keep photos underhomesor another share. - Never point an external library at Immich’s own data path – otherwise Immich indexes its own thumbnails and encoded videos.
- Treat NFS and folder checks as first‑class citizens – make sure the container waits for mounts, and that the
.immichsentinels are present. - Expect GPU device names to change – always verify
/dev/driafter updates and keep LXC config in sync. - Remember to click Scan – creating a library alone does nothing until you trigger a scan.
With this layout, Immich becomes a robust, low‑maintenance part of your homelab: app and database on fast local storage, media safely on the NAS, and clean separation between internal Immich data and your actual photo library. Feel free to adapt the paths and resource sizes to your environment, but keep the structural ideas the same.***