Install Immich on Ubuntu Server
Why Ubuntu for Immich?
Ubuntu LTS is the most common server distribution for self-hosting. It has the widest driver support, the largest community for troubleshooting, and Docker CE publishes first-class packages for it. If you are running a dedicated server or a VPS, Ubuntu 22.04 or 24.04 LTS is the safest choice for Immich.
Updated February 2026: Verified with latest Docker images and configurations.
This guide covers the full installation from a fresh Ubuntu system to a working Immich instance with hardware acceleration and automatic startup.
Prerequisites
- Ubuntu Server 22.04 LTS or 24.04 LTS (fresh install or existing system)
- 4 GB RAM minimum, 8 GB recommended
- 2+ CPU cores
- 20 GB free disk space for the application, plus storage for your photo library
- Root or sudo access
- A domain name (optional, for remote access via HTTPS)
Platform Setup
Update the System
sudo apt update && sudo apt upgrade -y
Install Docker CE from the Official Repository
Do not use Ubuntu’s docker.io snap or apt package — it lags behind the official Docker CE releases by months and causes compatibility issues.
# Remove any old Docker packages
sudo apt remove -y docker docker-engine docker.io containerd runc 2>/dev/null
# Install prerequisites
sudo apt install -y ca-certificates curl gnupg
# Add Docker's official GPG key
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
# Add the Docker repository
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker CE and Compose plugin
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# Verify installation
docker --version
docker compose version
Add Your User to the Docker Group
This lets you run docker commands without sudo:
sudo usermod -aG docker $USER
newgrp docker
Configure UFW Firewall
Ubuntu comes with UFW (Uncomplicated Firewall). Allow SSH and the Immich port:
# Allow SSH (do this FIRST or you'll lock yourself out)
sudo ufw allow OpenSSH
# Allow Immich web UI
sudo ufw allow 2283/tcp comment "Immich"
# Enable the firewall
sudo ufw enable
# Verify rules
sudo ufw status verbose
If you are placing Immich behind a reverse proxy, allow port 80 and 443 instead of 2283, and keep 2283 restricted to localhost:
sudo ufw allow 80/tcp comment "HTTP"
sudo ufw allow 443/tcp comment "HTTPS"
# Remove the direct Immich port rule if present
sudo ufw delete allow 2283/tcp
Docker and UFW caveat: Docker manipulates iptables directly, which can bypass UFW rules. If you need strict firewall control, bind Immich to localhost only in the Docker Compose port mapping (127.0.0.1:2283:2283) and use a reverse proxy on the host.
Docker Compose Configuration
Create the project directory:
sudo mkdir -p /opt/immich && cd /opt/immich
Create a .env file:
# /opt/immich/.env
# Absolute path for uploaded photos and videos on the host.
UPLOAD_LOCATION=/opt/immich/library
# Absolute path for PostgreSQL data on the host.
# NEVER use a network share (NFS/SMB) for this -- it will corrupt the database.
DB_DATA_LOCATION=/opt/immich/postgres
# Timezone -- affects how timestamps display in the UI.
TZ=America/New_York
# Immich version -- always pin to a specific release.
IMMICH_VERSION=v2.6.1
# PostgreSQL credentials. Change DB_PASSWORD to a strong random value.
# Use only alphanumeric characters (A-Za-z0-9) to avoid shell escaping issues.
DB_PASSWORD=CHANGE_ME_TO_A_RANDOM_STRING
DB_USERNAME=postgres
DB_DATABASE_NAME=immich
Create the docker-compose.yml:
# /opt/immich/docker-compose.yml
name: immich
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION}
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- "2283:2283"
depends_on:
redis:
condition: service_healthy
database:
condition: service_healthy
restart: unless-stopped
healthcheck:
disable: false
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION}
volumes:
- model-cache:/cache
env_file:
- .env
restart: unless-stopped
healthcheck:
disable: false
redis:
container_name: immich_redis
image: docker.io/valkey/valkey:8-alpine
healthcheck:
test: valkey-cli ping || exit 1
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
database:
container_name: immich_postgres
image: docker.io/tensorchord/pgvecto-rs:pg16-v0.4.0
environment:
POSTGRES_PASSWORD: ${DB_PASSWORD}
POSTGRES_USER: ${DB_USERNAME}
POSTGRES_DB: ${DB_DATABASE_NAME}
POSTGRES_INITDB_ARGS: "--data-checksums"
volumes:
- ${DB_DATA_LOCATION}:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: pg_isready -U ${DB_USERNAME} -d ${DB_DATABASE_NAME} || exit 1
interval: 10s
timeout: 5s
retries: 5
volumes:
model-cache:
Create directories and start the stack:
sudo mkdir -p /opt/immich/library /opt/immich/postgres
cd /opt/immich
docker compose up -d
Verify all containers are running:
docker compose ps
All four containers (immich_server, immich_machine_learning, immich_redis, immich_postgres) should show status Up (healthy).
First-Time Setup
- Open
http://YOUR_SERVER_IP:2283in a browser. - Click Getting Started to create your admin account. Choose a strong password.
- Navigate to Administration (gear icon) to configure:
- Storage Template: Set a file organization pattern like
{{y}}/{{y}}-{{MM}}-{{dd}}/{{filename}}. - Machine Learning: Enabled by default. Face detection and CLIP search begin processing on first upload.
- Map: Enable map view by accepting map tile usage terms.
- Storage Template: Set a file organization pattern like
Install the Immich mobile app (iOS / Android), point it at your server URL, and enable auto-backup. For remote access, set up HTTPS through a reverse proxy first.
Platform-Specific Optimization
Systemd Service for Auto-Start
Docker containers with restart: unless-stopped restart automatically after a reboot as long as Docker itself starts. Docker CE on Ubuntu enables the systemd service by default, but verify:
sudo systemctl is-enabled docker
If it returns disabled:
sudo systemctl enable docker
For additional control, create a dedicated systemd service that brings the Immich stack up explicitly:
sudo tee /etc/systemd/system/immich.service > /dev/null <<'EOF'
[Unit]
Description=Immich Photo Management
Requires=docker.service
After=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/immich
ExecStart=/usr/bin/docker compose up -d --wait
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=120
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable immich.service
Now sudo systemctl start immich and sudo systemctl stop immich control the full stack, and it starts automatically on boot.
Intel Quick Sync (VAAPI) Hardware Acceleration
If your Ubuntu server has an Intel CPU with integrated graphics (Haswell or newer), you can offload video transcoding to the iGPU.
Check for a DRI device:
ls -la /dev/dri/
You should see renderD128. If not, load the i915 driver:
sudo modprobe i915
Install the VA-API drivers:
# Ubuntu 22.04/24.04
sudo apt install -y intel-media-va-driver-non-free vainfo
Verify VA-API works:
vainfo
You should see a list of supported profiles (H264, HEVC, etc.).
Add the DRI device to immich-server in docker-compose.yml:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION}
devices:
- /dev/dri:/dev/dri
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- "2283:2283"
depends_on:
redis:
condition: service_healthy
database:
condition: service_healthy
restart: unless-stopped
healthcheck:
disable: false
For Intel OpenVINO ML acceleration, switch the machine learning image:
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION}-openvino
devices:
- /dev/dri:/dev/dri
volumes:
- model-cache:/cache
env_file:
- .env
restart: unless-stopped
healthcheck:
disable: false
Redeploy and enable VAAPI or Quick Sync in Administration > Video Transcoding Settings within the Immich web UI.
NVIDIA GPU Setup on Ubuntu
Install the NVIDIA driver:
# Check available driver versions
ubuntu-drivers devices
# Install the recommended driver
sudo ubuntu-drivers autoinstall
# Reboot to load the driver
sudo reboot
Install the NVIDIA Container Toolkit:
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey | sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list | \
sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' | \
sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
sudo apt update
sudo apt install -y nvidia-container-toolkit
sudo nvidia-ctk runtime configure --runtime=docker
sudo systemctl restart docker
Verify GPU access from Docker:
docker run --rm --gpus all nvidia/cuda:12.3.2-base-ubuntu22.04 nvidia-smi
Add GPU access to immich-server for video transcoding:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION}
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities:
- gpu
- video
volumes:
- ${UPLOAD_LOCATION}:/data
- /etc/localtime:/etc/localtime:ro
env_file:
- .env
ports:
- "2283:2283"
depends_on:
redis:
condition: service_healthy
database:
condition: service_healthy
restart: unless-stopped
healthcheck:
disable: false
For NVIDIA CUDA ML acceleration:
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION}-cuda
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: 1
capabilities:
- gpu
volumes:
- model-cache:/cache
env_file:
- .env
restart: unless-stopped
healthcheck:
disable: false
After redeploying, enable NVENC in Administration > Video Transcoding Settings.
File Permissions
By default, Docker containers run as root, and files in your upload directory will be owned by root:root. This is fine for most setups. If you need Immich files owned by a specific user (for example, to allow a backup script running as your user to read them):
# Create a dedicated immich user
sudo useradd -r -s /usr/sbin/nologin -M immich
# Set ownership on the upload directory
sudo chown -R immich:immich /opt/immich/library
# Get the UID and GID
id immich
Then add user: "UID:GID" to the immich-server service in your Compose file, replacing UID and GID with the values from the id command. Note that not all Immich containers support running as non-root — test thoroughly.
Troubleshooting
Docker: Permission Denied
Symptom: Running docker compose up -d returns permission denied while trying to connect to the Docker daemon socket.
Fix: Your user is not in the docker group, or the group membership has not taken effect in this shell session:
sudo usermod -aG docker $USER
# Log out and back in, or:
newgrp docker
UFW Not Blocking Connections
Symptom: Ports are reachable even though UFW rules should block them.
Fix: Docker modifies iptables directly and can bypass UFW. To prevent this, bind Immich to localhost in your Compose file and use a host-level reverse proxy:
ports:
- "127.0.0.1:2283:2283"
Alternatively, configure Docker to respect UFW by editing /etc/docker/daemon.json:
{
"iptables": false
}
Then restart Docker (sudo systemctl restart docker). Be aware this disables Docker’s automatic network rules — you must manually allow container-to-container communication via UFW.
GPU Not Detected by Immich
Symptom: vainfo works on the host but Immich does not use hardware transcoding.
Fix: Verify the /dev/dri device is passed through in docker-compose.yml. Check that the render group inside the container has access:
docker exec immich_server ls -la /dev/dri/
If renderD128 is not listed, the device mapping is wrong. Ensure you have devices: - /dev/dri:/dev/dri in the immich-server service. On some Ubuntu systems, the render group GID differs between host and container — add group_add: ["render"] to the service or pass the host’s render GID:
# Find your render group GID
getent group render
Database Fails to Start After Unclean Shutdown
Symptom: immich_postgres container exits with database system was not properly shut down errors after a power loss or hard reboot.
Fix: PostgreSQL’s data checksums (enabled via POSTGRES_INITDB_ARGS) will detect corruption. If the container restarts and recovers automatically, no action is needed. If it fails repeatedly:
# Check logs
docker logs immich_postgres
# If WAL recovery fails, you may need to restore from backup
docker compose stop
# Restore from your latest pg_dump backup
Prevent this by using the systemd service defined above, which runs docker compose down on shutdown, giving PostgreSQL time to flush.
Uploads Slow Over Network
Symptom: Uploading from the mobile app over the local network is much slower than expected.
Fix: Check if your Ubuntu server has jumbo frame issues or MTU mismatches. Test with:
ping -s 8972 YOUR_SERVER_IP
If large packets fail, reduce MTU or fix your network configuration. Also verify the upload directory is on a local disk (not NFS/SMB), as network-mounted storage adds significant latency to file writes.
Resource Requirements
- RAM: ~2 GB idle, 4-8 GB during active ML processing. 8 GB recommended for libraries over 50,000 photos.
- CPU: 2 cores minimum. 4+ cores recommended for faster ML processing. Intel CPUs with Quick Sync reduce transcoding load.
- Disk: 20 GB for the application, database, and ML model cache. Photo storage depends on library size.
- GPU: Optional. Intel iGPU or NVIDIA GPU dramatically speeds up video transcoding and ML inference.
Related
Get self-hosting tips in your inbox
Get the Docker Compose configs, hardware picks, and setup shortcuts we don't put in articles. Weekly. No spam.
Comments