How to Set Up Immich with ZFS on Proxmox (LXC)
Requirements
Before you begin, make sure the following prerequisites are met:
- Proxmox VE host with a working ZFS pool
- A ZFS dataset that will store the Immich data (example:
data_nvme) - Debian 12 LXC template available on the Proxmox host
- Support for unprivileged LXC containers
- LXC features enabled:
nesting,keyctl,fuse - At least 8 GB RAM for the container (more recommended depending on library size)
- Network access for the container
- Shell access to the Proxmox host
ZFS Dataset Layout
We will use the following ZFS dataset structure:
/data_nvme
└── immich
├── data
└── databaseThe datasets data_nvme/immich, data_nvme/immich/data, and data_nvme/immich/database are created separately so they can use different ZFS properties. This allows tuning based on workload type.
Create the datasets in the Proxmox host shell:
zfs create data_nvme/immich
zfs create data_nvme/immich/data
zfs create data_nvme/immich/databaseVerify the result:
zfs listExample output:
NAME USED AVAIL REFER MOUNTPOINT
data_nvme 117M 3.51T 104K /data_nvme
data_nvme/immich 296K 3.51T 104K /data_nvme/immich
data_nvme/immich/data 96K 3.51T 96K /data_nvme/immich/data
data_nvme/immich/database 96K 3.51T 96K /data_nvme/immich/databaseWhy Separate ZFS Datasets?
The data and database directories are stored in separate ZFS datasets because they have different I/O patterns and tuning requirements.
Media dataset (data)
- Large sequential files
- Optimized with large recordsize (1M)
- Strong compression (zstd)
- Focus on throughput and storage efficiency
Database dataset (database)
- Many small random reads and writes
- Small recordsize (16K)
- Low-latency tuning (logbias=latency)
- Focus on IOPS and commit latency
Recommended ZFS Properties
Database Dataset (data_nvme/immich/database)
This dataset stores database files with many small random reads and writes. Recommended settings:
recordsize=16Kcompression=lz4atime=offlogbias=latency
Apply:
zfs set recordsize=16K data_nvme/immich/database
zfs set compression=lz4 data_nvme/immich/database
zfs set atime=off data_nvme/immich/database
zfs set logbias=latency data_nvme/immich/databaseWarning: Adjust dataset paths if your pool name or mountpoints differ.
ZFS Ownership
Change ownership so the datasets belong to user data-share (UID/GID 1000):
chown -R data-share:data-share /data_nvme/immichExample:
ls -al /data_nvme/immich/Tip: If the user
data-sharedoes not yet exist, create it on the Proxmox host first. See: here
Create the LXC Container
pct create 104 local:vztmpl/debian-12-standard_12.12-1_amd64.tar.zst \
--hostname immich \
--cores 4 \
--memory 8192 \
--swap 1024 \
--rootfs local-lvm:20 \
--net0 name=eth0,bridge=vmbr0,ip=dhcp \
--unprivileged 1 \
--features nesting=1,keyctl=1,fuse=1 \
--mp0 /data_nvme/immich/data,mp=/immich/data \
--mp1 /data_nvme/immich/database,mp=/immich/database \
--mp2 /data_nvme/user_data/immich,mp=/immich/data/library \
--password 'YOUR_PASSWORD'Adjust container ID, storage target, and template name to match your environment. Ensure the CT ID is not already in use.
Configure UID/GID Mapping
To ensure correct file ownership with bind-mounted datasets, configure custom UID/GID mappings.
Edit the container config on the Proxmox host:
nano /etc/pve/lxc/104.confAdd:
lxc.idmap: u 0 100000 1000
lxc.idmap: u 1000 1000 1
lxc.idmap: u 65534 165534 1
lxc.idmap: g 0 100000 1000
lxc.idmap: g 1000 1000 1
lxc.idmap: g 65534 165534 1This maps UID/GID 1000 (data-share) 1:1 between host and container, while all other IDs remain remapped for security.
Reference: Unprivileged LXC containers
Configure Permissions Inside the Container
Create the data-share user and group inside the container with UID/GID 1000:
groupadd -g 1000 data-share
useradd -u 1000 -g 1000 -m -s /bin/bash data-shareTest access:
ls -al /immichThe user data-share should be able to write to /immich/data and /immich/database.
Install Docker Inside the Container
Start the container, update packages, and install Docker: Docker installation on Debian
Configure Docker Storage Driver (LXC)
In an unprivileged LXC container, the default Docker storage driver may not work correctly. Use fuse-overlayfs instead:
apt update
apt install -y fuse-overlayfs
mkdir -p /etc/docker
cat > /etc/docker/daemon.json << 'EOF'
{
"storage-driver": "fuse-overlayfs"
}
EOF
systemctl restart dockerInstall Immich
Follow the official Docker Compose installation guide: Immich installation
Before starting, edit the .env file:
UPLOAD_LOCATION=/immich/data
DB_DATA_LOCATION=/immich/databaseStart Immich:
docker compose up -dFirst Login
Open the Immich web interface on port 2283.
Create your admin account and enable the storage template engine. Otherwise, you will not get a user-friendly folder structure for mounting in other VMs or containers.
Now you have a folder structure in /immich/data/. There is a /immich/data/uploads folder. You have so execute there commands in proxmox shell:
chmod g+s /data_nvme/immich/data/upload
chown data-share:data-share /data_nvme/immich/data/upload