If you've ever opened the Google Photos app and felt a little uneasy about how much of your life is sitting on someone else's servers, you're not alone. A lot of homelab users end up at the same fork in the road: keep paying for cloud storage, or run something yourself. Immich is the tool most people land on when they choose the second option, and it happens to run really well on Proxmox VE.

This guide walks you through building a dedicated LXC container on Proxmox VE, installing Docker inside it, and getting Immich up and running with your own photo library. You don't need any Docker experience going in. You don't need a GPU either, though we'll talk about when one actually helps.

What You Will Learn

By the end of this tutorial you'll have a working Immich server running in its own Proxmox VE container, reachable from your browser and your phone. Specifically, you'll learn how to:

  • Create an unprivileged LXC container sized correctly for Immich
  • Enable the container features Docker needs to run inside an LXC container
  • Install Docker Engine and the Compose plugin on Debian 13
  • Configure Immich's .env file and bring the stack up with Docker Compose
  • Log in, create your admin account, and connect the mobile app
  • Avoid the handful of mistakes that trip up almost everyone on their first attempt

Plan on about 30 to 40 minutes if this is your first time setting up Docker on Proxmox. Most of that is waiting for template downloads and image pulls, not typing commands.

What Is This Feature?

Immich is an open-source, self-hosted photo and video backup application. Think of it as a Google Photos replacement that you run on your own hardware instead of handing your library to a cloud provider. It has apps for iOS and Android that back up your camera roll automatically in the background, a web interface that looks and feels a lot like Google Photos, face recognition, object detection, map view based on photo location data, and shared albums.

Under the hood, Immich isn't one program. It's a stack of several services that work together:

ServiceWhat It Does
immich-serverThe main API and web interface you interact with
immich-machine-learningHandles face recognition and smart search (finding photos by what's in them)
PostgreSQLThe database storing metadata, albums, and user accounts
Valkey (Redis-compatible)A fast in-memory cache used for background job queues

All four of these run as Docker containers, managed together with a single Docker Compose file. That's why we need Docker inside our Proxmox container in the first place — Immich's developers only officially support the Docker Compose installation method, and honestly, that's the right call. It keeps every component's dependencies isolated and makes updates a one-command affair.

Why Would You Use It?

The obvious reason is privacy. Your photos stay on hardware you control, full stop. But there are a few practical reasons homelab users pick Immich specifically over other self-hosted photo tools.

It actually gets updated. Immich ships new releases constantly, and the mobile apps keep pace with them. The automatic background upload from your phone works the way you'd expect — turn it on once, forget about it, and your photos show up on the server within a minute or two of taking them.

It's also genuinely fast, even on modest hardware. You don't need a beefy GPU to get useful facial recognition and smart search. A four-core VM-equivalent allocation handles a family-sized library without much complaint.

Running it in its own LXC container, separate from your other services, means you can back it up, restore it, or move it to different storage without touching anything else on your Proxmox host. That isolation is worth more than it sounds like on paper — the first time a Docker update breaks something, you'll be glad it's contained to one place.

Prerequisites

Before you start, make sure you have:

  • A working Proxmox VE 9.x host (8.x works too, the steps barely change)
  • At least 4 CPU cores and 6 GB of RAM to spare — Immich's own documentation lists this as the minimum, with 8 GB and 4 cores recommended if you have a large library or multiple family members uploading
  • Storage for your photo library. This is the part people underestimate. If you and your family have 15 years of phone photos, budget for at least 200-500 GB, and put it on a separate disk or mount point rather than growing the container's root disk
  • Basic comfort with a Linux terminal — you'll be editing one text file and running maybe a dozen commands
  • Internet access from the container so it can pull Docker images (a few gigabytes total, mostly the machine learning image)

A note on hardware age: the machine learning container needs a CPU with AVX2 support to run face recognition and smart search efficiently. Anything from roughly the last decade qualifies. If you're running Proxmox on very old hardware, ML features may run slowly or need to be disabled — the core photo backup and viewing still work fine either way.

Step-by-Step Tutorial

Step 1: Download the Debian 13 Template

Log into the Proxmox VE shell (via the web GUI's Shell button on your node, or SSH). First, make sure the template list is current:

pveam update

Then download the Debian 13 template:

pveam download local debian-13-standard_13.1-1_amd64.tar.zst

The exact version number in the filename may differ slightly depending on when you're reading this — run pveam available --section system | grep debian-13 first if the command above says the file isn't found, and use whatever version it lists.

Step 2: Create the Container

Now create the LXC container itself. Adjust the storage name (local-lvm here) to match your setup, and pick a free container ID:

pct create 200 local:vztmpl/debian-13-standard_13.1-1_amd64.tar.zst \
  --hostname immich \
  --cores 4 \
  --memory 6144 \
  --swap 512 \
  --rootfs local-lvm:24 \
  --net0 name=eth0,bridge=vmbr0,ip=dhcp \
  --unprivileged 1 \
  --features nesting=1,keyctl=1 \
  --onboot 1

A couple of things worth calling out here. We're using an unprivileged container, which is the safer default — it maps the container's root user to an unprivileged range on the host, so even if something inside the container gets compromised, it doesn't have real root on your Proxmox node. The nesting=1 feature is what allows Docker to run inside an LXC container at all, since Docker normally expects to manage its own cgroups and namespaces directly. keyctl=1 is needed because some Docker components use the kernel keyring.

The 24 GB root disk is plenty for the OS and Docker images. It is not where your photos should live — we'll add separate storage for that in Step 6.

Step 3: Start the Container and Update It

pct start 200
pct enter 200

Once you're inside the container's shell:

apt update && apt full-upgrade -y

Step 4: Install Docker

Docker's official convenience script is the fastest reliable way to get Docker Engine and the Compose plugin onto a fresh Debian system:

curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

This installs Docker Engine, the CLI, containerd, and the Docker Compose plugin (invoked as docker compose, no hyphen) in one pass. Confirm it worked:

docker --version
docker compose version

Step 5: Set Up the Immich Directory

mkdir -p /opt/immich-app
cd /opt/immich-app
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
wget -O .env https://github.com/immich-app/immich/releases/latest/download/example.env

This pulls the current, officially maintained compose file and an example environment file straight from Immich's GitHub releases — the same files their own installation docs point to.

Step 6: Edit the .env File

Open the file with nano:

nano .env

At minimum, set these values:

  • UPLOAD_LOCATION=/mnt/photos — where your actual photo and video files are stored on disk
  • DB_DATA_LOCATION=/mnt/photos/pgdata — where PostgreSQL keeps its data files
  • DB_PASSWORD= a password you generate yourself, not the placeholder
  • TZ= your local timezone, e.g. America/Chicago, so timestamps in the UI match reality

If /mnt/photos doesn't exist yet, create it before continuing: mkdir -p /mnt/photos. If you're planning to store a large library, this is the point where you'd add a mount point from the Proxmox host pointing at a bigger disk, rather than letting the library fill up your 24 GB root volume.

Step 7: Launch Immich

docker compose up -d

The first run downloads several images, including the machine learning one, which is the largest at around 2 GB. On a decent connection this takes two to five minutes. Watch progress with:

docker compose logs -f

Press Ctrl+C to stop following the logs once you see the server report it's ready — you're not stopping the containers, just detaching from the log stream.

Step 8: Create Your Admin Account

Find your container's IP address with ip a if you didn't set a static one, then open http://:2283 in a browser. Immich's setup wizard asks for an admin email and password on first load. This account is separate from anything on your Proxmox host — it only exists inside Immich's own database.

From there, install the Immich mobile app, point it at http://:2283, log in, and turn on background backup. Give it a few minutes to start uploading your camera roll.

Commands Explained

CommandWhat It Does
pveam updateRefreshes the list of downloadable OS templates Proxmox knows about
pct createBuilds a new LXC container from a template with the settings you specify
--features nesting=1,keyctl=1Grants the container permissions it needs to run Docker internally
pct enter 200Drops you into a root shell inside container 200, no SSH needed
docker compose up -dStarts every service defined in docker-compose.yml in the background (-d for detached)
docker compose logs -fStreams live logs from all running containers so you can watch startup progress
docker compose pullDownloads newer versions of the images referenced in the compose file, used when updating

Common Errors

A few problems show up more than others. Here's what they usually mean.

"Cannot connect to the Docker daemon" right after installing Docker. This almost always means nesting=1 wasn't set on the container, or you set it but never restarted the container afterward. Fix it from the Proxmox host with pct set 200 --features nesting=1,keyctl=1, then pct restart 200.

"immich_postgres" container keeps restarting. Check docker compose logs immich_postgres. Nine times out of ten this is a permissions problem on DB_DATA_LOCATION — the directory needs to be writable by the container. Deleting the directory and letting Docker recreate it (after confirming your .env path is correct) usually clears it.

Machine learning container exits immediately or logs mention illegal instruction. This points to a CPU without AVX2 support. You can disable ML entirely by removing the immich-machine-learning service block from docker-compose.yml, though you'll lose face grouping and smart search.

Web UI loads but uploads fail or hang. Usually a disk space issue on whatever storage backs UPLOAD_LOCATION. Run df -h inside the container to check.

Troubleshooting

Start with docker compose ps from inside /opt/immich-app. It shows every service's status at a glance — you want all four showing "Up" or "healthy," not "Restarting."

If something's unhealthy, pull its specific logs rather than the whole stack: docker compose logs immich-server, for example. The error near the bottom of the output is almost always more useful than anything higher up.

When in doubt, a clean restart of the whole stack fixes more than you'd expect: docker compose down && docker compose up -d. This doesn't delete your data — your photos and database live in the paths you set in .env, not inside the containers themselves.

If the container itself won't boot, check its resource allocation from the Proxmox GUI. An out-of-memory LXC container can look like a hung Docker daemon when it's really just been killed by the kernel for using too much RAM.

Best Practices

Put your photo library on its own mount point, not the container's root disk. Add it from the Proxmox host with pct set 200 -mp0 /path/on/host,mp=/mnt/photos, then reference that same path in your .env. This makes backups and disk expansion much simpler down the line.

Back up both the upload location and the Postgres data directory. Your photos are one half of the picture; the database holding albums, faces, and metadata is the other. Proxmox Backup Server or a simple vzdump job covering the whole container handles both at once.

Don't expose port 2283 directly to the internet. Put a reverse proxy with HTTPS in front of it if you want remote access, or use something like Tailscale to reach it without opening any ports at all.

Update deliberately rather than automatically. Run docker compose pull && docker compose up -d when you're ready, and read the release notes first — Immich moves fast, and occasional breaking changes do happen between versions.

Frequently Asked Questions

Do I need a powerful CPU to run Immich?
No. Four cores handles a typical family library comfortably. A GPU helps machine learning run faster but isn't required.

Can I run Immich in a VM instead of an LXC container?
Yes, and some people prefer it for the extra isolation. LXC uses less overhead and starts faster, which is why this guide uses it.

Is it safe to run Docker inside an unprivileged LXC container?
Yes, with nesting and keyctl enabled as shown above. This is a well-supported, common Proxmox pattern, not a hack.

How much storage will I actually need?
Depends entirely on your library. A rough rule: total the size of your existing phone backups or Google Photos storage usage, then add 30-50% headroom for growth and thumbnails.

Can multiple family members use the same Immich install?
Yes. Create additional user accounts from the admin panel, and each person gets their own library with optional shared albums.

Is Immich completely free?
Yes, it's open source under the AGPL license with no paid tier required for full functionality.

Conclusion

Getting off Google Photos always sounds harder than it turns out to be, and Immich is a big part of why that's changed. Once the container's built and the stack is running, day-to-day use disappears into the background — your phone just backs up like it always did, except now you own the result.

The setup here took maybe half an hour, most of which is waiting on downloads rather than typing. From here, the two things worth doing next are locking down remote access properly and setting up a backup job for the container. Do those two things and you've got a photo library that's genuinely yours, running on hardware you control.