Self-Hosted Immich on Proxmox with ZFS: Complete 8-Step Family Photo Search Guide

self-hosted-immich-on-proxmox-with-zfs.md
title Self-Hosted Immich on Proxmox with ZFS: Complete 8-Step Family Photo Search Guide
date
author VahaC
read 11 min read
category Self-Hosting
tags #Homelab #Linux #Onedrive #Proxmox
Self-Hosted Immich on Proxmox with ZFS: Complete 8-Step Family Photo Search Guide

Google Photos has one killer feature — you type “beach 2022” or “grandma” and it just finds the photo. This guide shows you how to get exactly that on your own hardware, with zero monthly fees and full control over your data. 📸

Immich on Proxmox with ZFS is not used here as a photo storage replacement. Your photos already live in OneDrive and sync to local ZFS storage via rclone — that part is covered in the OneDrive + Proxmox LXC + ZFS sync guide. What Immich on Proxmox with ZFS adds is a powerful search and browsing layer on top: face recognition, location search, semantic search, and a clean timeline interface — all pointing at your existing photo library without touching or duplicating the originals.

If you haven’t set up Proxmox yet, start with the Proxmox VE install guide first.


🏗️ Architecture Overview

Phones / cameras
    │
    ▼
OneDrive (Microsoft cloud)
    │
    ▼ rclone sync (nightly)
ZFS Mirror on Proxmox  (pool name e.g. mir16tb, tank, datastore)
    └── /YOUR_POOL/onedrive/YOUR_ACCOUNT/Pictures ← your actual photos live here
    └── /YOUR_POOL/immich                          ← thumbs, encoded-video, DB backups only

Immich LXC
    ├── indexes /mnt/onedrive (read-only External Library)
    ├── stores thumbnails + metadata on /mnt/immich (ZFS)
    └── Web UI at :2283 — search by face, location, date, object

The originals never move. Immich reads them from the OneDrive sync folder and generates its own thumbnails and metadata on ZFS. If you ever remove Immich — your photos are untouched.

Prerequisites:

  • ZFS mirror pool already set up on Proxmox (any pool name works — mir16tb, tank, datastore, etc.)
  • OneDrive synced to a folder on that pool — see the OneDrive + Proxmox LXC + ZFS guide

🚀 Step 1: Install Immich via Community Scripts

Immich on Proxmox with ZFS is dramatically simpler to deploy with the Community Scripts helper than with Docker Compose. Run this command directly in the Proxmox shell — not inside any VM or container:

bash -c "$(curl -fsSL https://raw.githubusercontent.com/community-scripts/ProxmoxVE/main/ct/immich.sh)"

Always get the latest command from community-scripts.org/scripts/immich — it may change between releases.

Installation Options

Choose Advanced when prompted. Recommended settings:

OptionRecommended valueWhy
Container typeUnprivilegedMore secure; default
Disk size32 GBPhotos are on ZFS, not here
CPU cores4ML processing needs headroom
RAM8192 MB4096 minimum; 8192 if using OpenVINO
IPStaticEasier to bookmark and proxy
OpenVINO (HW-accelerated ML)Yes if Intel iGPUSpeeds up face detection significantly

The installer walks you through many additional prompts. Most can be left at their defaults — below are the ones where you should change the default value:

PromptDefaultChooseWhy
IPv4 ConfigurationDHCPStaticFixed IP for a server — easier to bookmark and proxy
IPv6 ConfigurationautononePrevents curl from trying IPv6 during install and failing
SSH Key SourcefoundCopies your Proxmox host SSH key into the container
Enable root SSH accessnoyesAllows direct SSH into the container
FUSE SupportnoyesRequired for rclone bind mounts
GPU PassthroughyesnoOnly useful with Intel/AMD iGPU — Xeon E5 has none
Container ProtectionnoyesPrevents accidental deletion of the container
Verbose ModenoyesShows full output — useful for diagnosing install issues

⚠️ IPv6 note: if the installer fails with curl: Failed to connect (network unreachable / host down) — this is almost always IPv6. Setting IPv6 to none during setup prevents this entirely.

GitHub Rate Limit — PAT Token

During installation the script fetches several packages from GitHub. If GitHub’s API rate limit is hit (HTTP 403), the installer will pause and ask:

Would you like to enter a GitHub Personal Access Token (PAT)? [y/N]:

Enter Y and provide a token. To generate one:

  1. Open https://github.com/settings/tokens
  2. Click Generate new token → Generate new token (classic)
  3. Give it any name (e.g. immich-install)
  4. Set expiration to 1 day — it’s only needed for this install
  5. Leave all scopes unchecked — no permissions needed, just rate limit bypass
  6. Click Generate token and copy it immediately
  7. Paste it into the installer prompt

The token is used only to authenticate API requests and bypass the rate limit — it doesn’t grant any access to your GitHub account.

⏱️ Installation takes 5–15 minutes. The script sets up Immich, PostgreSQL, Redis, and all system dependencies inside a fresh LXC — no Docker involved.

When done, open http://CONTAINER_IP:2283 and create your admin account before anything else. 🎉


🔗 Step 2: Mount ZFS Storage into the Immich LXC

The Immich LXC runs unprivileged — the immich user inside the container maps to a different UID on the Proxmox host. Getting this right is the difference between a working Immich on Proxmox with ZFS setup and a wall of permission errors.

Create the ZFS Dataset

Before setting permissions, make sure the Immich dataset actually exists:

# Check your pool name first
zpool list

# Check existing datasets
zfs list

If the dataset doesn’t exist yet, create it:

# Replace 'datastore' with your actual pool name (e.g. mir16tb, tank, etc.)
zfs create datastore/immich

# Verify
zfs list

The dataset will mount automatically at /datastore/immich (or /YOUR_POOL/immich).

Find the Immich User UID

Run on the Proxmox host (replace 201 with your LXC ID):

pct exec 201 -- id immich

Expected output looks something like: uid=999(immich) gid=991(immich). The exact GID may vary — 991, 996, or another value depending on what else was installed. What matters is the UID: with default unprivileged LXC mapping, uid=999 inside maps to uid=100999 on the host.

Fix Permissions on ZFS Datasets

On the Proxmox host:

# Immich working data (thumbs, encoded-video, DB backups)
chown -R 100999:100000 /datastore/immich
chmod -R 770 /datastore/immich

# OneDrive photos — read-only, so immich only needs execute + read
chown -R 100999:100000 /datastore/onedrive/personal
chmod -R 750 /datastore/onedrive/personal

⚠️ If /datastore/onedrive/personal is owned by a different user (e.g. the rclone sync process user), you may need to add that user to a shared group instead. The key requirement is that uid=100999 on the host can read the directory.

Add Both Bind Mounts

⚠️ If you enabled Container Protection during install, you must disable it first before making any changes — otherwise pct set will fail with “protection mode enabled”:

pct set 201 --protection 0

Re-enable it after you’re done with the configuration changes.

pct stop 201

# Immich working data — read/write
pct set 201 -mp0 /datastore/immich,mp=/mnt/immich

# OneDrive photos — read-only
# Point to the specific subfolder that contains your photos, not the root
pct set 201 -mp1 /datastore/onedrive/YOUR_ACCOUNT/Pictures,mp=/mnt/onedrive,ro=1

# Re-enable protection
pct set 201 --protection 1

pct start 201

Verify from the host:

pct exec 201 -- ls -la /mnt/immich
pct exec 201 -- ls /mnt/onedrive

/mnt/immich should be owned by immich:root. /mnt/onedrive should show your photo folders and albums. ✅

💡 Which subfolder to mount? Don’t mount the entire OneDrive root — mount only the folder that contains photos. In a typical OneDrive structure this is Pictures or Photos. Check what’s inside your sync folder with ls /YOUR_POOL/onedrive/YOUR_ACCOUNT/ and pick the right subdirectory.


📦 Step 3: Move the Upload Location to ZFS

By default the installer puts Immich working data under /opt/immich/upload on the LXC disk. For a proper Immich on Proxmox with ZFS setup, we redirect this to the ZFS mount before Immich processes any photos.

Enter the container:

pct enter 201

Stop Immich Services

systemctl stop immich-web immich-ml

Create the Folder Structure on ZFS

Immich requires specific subdirectories with hidden .immich marker files:

for dir in library thumbs encoded-video profile backups upload; do
  mkdir -p /mnt/immich/$dir
  touch /mnt/immich/$dir/.immich
done

chown -R immich:immich /mnt/immich

Update the .env File

nano /opt/immich/.env

Set:

IMMICH_MEDIA_LOCATION=/mnt/immich

Recreate the Symlinks

rm /opt/immich/app/upload
rm /opt/immich/app/machine-learning/upload

ln -sf /mnt/immich /opt/immich/app/upload
ln -sf /mnt/immich /opt/immich/app/machine-learning/upload

Restore Ownership and Start

chown -R immich:immich /opt/immich

systemctl start immich-ml immich-web

tail -f /var/log/immich/web.log

Wait for the server to come up cleanly, then confirm the correct storage path in Administration → Storage. 🖥️

Official guide for upload location change: https://github.com/community-scripts/ProxmoxVE/discussions/5075


🖼️ Step 4: Add the OneDrive Folder as External Library

This is the core of the Immich on Proxmox with ZFS setup — connecting your existing photo archive to Immich’s search engine without moving a single file. 📚

In the Immich web UI:

  1. Go to Administration → Libraries
  2. Click Create Library → External Library
  3. Set the Import path to /mnt/onedrive (the path inside the container)
  4. Leave Delete assets after removal disabled — this is a read-only source
  5. Click Save
  6. Click Scan Library to start the initial import

Immich crawls /mnt/onedrive, generates thumbnails on ZFS, runs face detection, and adds everything to the timeline. For a large collection this takes several hours — progress is visible in Administration → Jobs. ⏳

Key things to know about External Libraries:

  • 📝 Originals are never copied — Immich only stores thumbnails and metadata on ZFS
  • 🔍 Face recognition and semantic search work fully on External Library photos
  • 🔄 Set up periodic re-scan under Library settings so new OneDrive photos get picked up automatically
  • You cannot upload to an External Library — it’s read-only by design

Official External Library docs: https://immich.app/docs/features/libraries


👥 Step 5: Create Family Member Accounts

Go to Administration → Users → Create User for each family member. Each person gets their own login and timeline view. 👨‍👩‍👧‍👦

A few things to note for this Immich on Proxmox with ZFS setup:

  • The External Library (OneDrive photos) is associated with the admin account by default
  • You can share albums from the External Library to other users
  • If family members need access from outside the home network, WireGuard VPN on Proxmox is the cleanest approach — no open ports, works on every device 🔒

☁️ Step 6: Backup Immich-Generated Data

The photo originals are safe in OneDrive — no action needed there. But Immich generates its own data on ZFS that is worth backing up: thumbnails, encoded video previews, and most importantly the PostgreSQL database which contains all your face tags, albums, and search index.

The database is automatically backed up by Immich to /mnt/immich/backups on a schedule. Confirm this is enabled in Administration → Jobs → Database Backup.

To back up the Immich ZFS data offsite:

# Install rclone inside the container
curl https://rclone.org/install.sh | bash

# Configure your remote (OneDrive, Backblaze B2, etc.)
rclone config

Nightly backup script:

cat > /opt/backup-immich.sh << 'EOF'
#!/bin/bash
LOG="/var/log/immich-backup.log"
echo "$(date): Starting Immich data backup" >> $LOG
# We only backup Immich-generated data — originals are already in OneDrive
rclone sync /mnt/immich/backups onedrive:immich-db-backups \
  --transfers 2 \
  --log-file="$LOG" \
  --log-level INFO
echo "$(date): Done" >> $LOG
EOF

chmod +x /opt/backup-immich.sh
crontab -e
# Add: 0 4 * * * /opt/backup-immich.sh

💡 The photo originals in /datastore/onedrive/personal are already in Microsoft’s cloud — no need to rclone them anywhere else.

Rclone docs: https://rclone.org/onedrive/


🔄 Step 7: Keeping Immich Updated

Updating Immich on Proxmox with ZFS is a single command from inside the container shell:

pct enter 201
update

⚠️ Community Scripts pins Immich to a tested version — you may be a release or two behind upstream. The update script automatically recreates symlinks using IMMICH_MEDIA_LOCATION from your .env, so the ZFS mount stays intact. 🛡️

Check Immich releases to see what version is current.


⚡ Step 8: ZFS Tuning

A few tweaks make Immich on Proxmox with ZFS noticeably snappier — especially for thumbnail loading.

ARC Cache

ZFS caches frequently read data in RAM. With a generous ARC, repeated thumbnail loads feel instant. Check current ARC size:

cat /proc/spl/kstat/zfs/arcstats | grep -E "^size|^c_max"

Cap it if you need RAM for other VMs:

# Example: limit to 8GB
echo "options zfs zfs_arc_max=8589934592" > /etc/modprobe.d/zfs.conf
update-initramfs -u

Monthly Scrub

Proxmox schedules ZFS scrubs automatically. Verify:

cat /etc/cron.d/zfsutils-linux

Scrubs detect and correct silent bit rot. For a photo archive holding years of irreplaceable memories, this matters. ✅


🎯 Key Takeaways

Immich on Proxmox with ZFS used as a search layer on top of OneDrive gives you the best of both worlds:

  • Photos stay in OneDrive — cloud redundancy, mobile sync, sharing all intact
  • Face recognition and semantic search — find any photo in seconds
  • Zero duplication — External Library indexes originals without copying them
  • ZFS self-healing — thumbnail data protected against silent corruption
  • One-command updates via Community Scripts
  • Your data, your server — no subscription, no algorithmic timeline, no ads

The setup takes an afternoon. The result is a family photo archive that’s both safe and actually searchable. 💾


📚 Official Documentation


Always get the latest install command from community-scripts.org/scripts/immich — it may change between releases.

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.