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

  1. Open http://YOUR_SERVER_IP:2283 in a browser.
  2. Click Getting Started to create your admin account. Choose a strong password.
  3. 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.

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.

Comments