Immich is a self-hosted photo and video backup platform with a mobile app that uploads your camera roll automatically, a timeline view, face recognition and smart search based on machine learning. In this tutorial you will install Immich on Ubuntu 24.04 using the official Docker Compose files, publish it over HTTPS with Nginx, connect the mobile app, add an existing photo folder as an external library and set up database backups.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with at least 4 CPU cores and 6 GB of RAM (8 GB recommended). The machine learning container alone can use several gigabytes while it processes a new library.
  • Local SSD storage for the PostgreSQL database and enough disk for your photos and videos. Immich adds thumbnails and transcoded videos on top of the originals.
  • A non-root user with sudo privileges.
  • Docker Engine and the Docker Compose plugin installed. See How to install Docker on Linux.
  • A domain name, referred to as your_domain (for example photos.example.com), with a DNS A record pointing to your server. The mobile app needs a stable HTTPS URL.

Step 1 - Downloading the official Compose files

Immich publishes a docker-compose.yml and an example.env with every release. Always use the pair from the same release, because the Compose file and the images are versioned together.

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

List the files to confirm the download:

ls -la /opt/immich
-rw-r--r-- 1 root root  ... .env
-rw-r--r-- 1 root root  ... docker-compose.yml

Step 2 - Configuring the environment file

Generate a database password. Immich recommends letters and numbers only, so use hex output:

openssl rand -hex 24

Open the environment file:

sudo nano /opt/immich/.env

Set the following values and leave the rest as shipped. In particular, do not change IMMICH_VERSION, which keeps the images in step with the downloaded Compose file:

# Where uploaded photos, thumbnails and database dumps are stored
UPLOAD_LOCATION=/opt/immich/library

# PostgreSQL data directory (must be local disk, not a network share)
DB_DATA_LOCATION=/opt/immich/postgres

# Your time zone, see the TZ column of the tz database
TZ=Europe/Madrid

# Paste the password generated above
DB_PASSWORD=your_db_password

Restrict the file, since it contains the database password:

sudo chmod 600 /opt/immich/.env

Step 3 - Binding Immich to localhost

The Compose file publishes the web port 2283 on all interfaces. Ports published by Docker bypass UFW, so restrict it to the loopback interface and let Nginx handle public traffic. Open the Compose file:

sudo nano /opt/immich/docker-compose.yml

Find the ports entry of the immich-server service and prefix it with 127.0.0.1:

    ports:
      - '127.0.0.1:2283:2283'

Keep a note of this edit: when you download a newer Compose file during an upgrade, apply it again.

Step 4 - Starting Immich

Start the stack. The first run pulls four images (server, machine learning, Valkey and PostgreSQL), which takes a few minutes:

cd /opt/immich
sudo docker compose up -d

Check that all containers are running and the database reports healthy:

sudo docker compose ps
NAME                      IMAGE                                            STATUS
immich_machine_learning   ghcr.io/immich-app/immich-machine-learning:...   Up 2 minutes (healthy)
immich_postgres           ghcr.io/immich-app/postgres:...                  Up 2 minutes (healthy)
immich_redis              docker.io/valkey/valkey:...                      Up 2 minutes (healthy)
immich_server             ghcr.io/immich-app/immich-server:...             Up 2 minutes (healthy)

Query the ping endpoint locally:

curl -s http://127.0.0.1:2283/api/server/ping
{"res":"pong"}

Step 5 - Configuring Nginx and HTTPS

Install Nginx and Certbot:

sudo apt update
sudo apt install nginx certbot python3-certbot-nginx

Create a server block:

sudo nano /etc/nginx/sites-available/immich
server {
    listen 80;
    listen [::]:80;
    server_name your_domain;

    # Allow large video uploads from the mobile app
    client_max_body_size 50000M;

    proxy_read_timeout 600s;
    proxy_send_timeout 600s;
    send_timeout       600s;

    location / {
        proxy_pass http://127.0.0.1:2283;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_redirect off;
    }
}

The high body size limit and timeouts prevent large videos from failing mid-upload, and the Upgrade headers enable the WebSocket connection that the web and mobile clients use for live updates.

Enable the site and reload Nginx:

sudo ln -s /etc/nginx/sites-available/immich /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Allow SSH, HTTP and HTTPS through UFW, then request a certificate:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable
sudo certbot --nginx -d your_domain

Verify the public endpoint from your own computer:

curl -s https://your_domain/api/server/ping
{"res":"pong"}

Step 6 - Creating the admin account and setting the server URL

Open https://your_domain in your browser and click Getting Started. The first account you register becomes the administrator. Use a real email address and a strong password.

After logging in, open Administration > Settings > Server Settings and set External domain to https://your_domain. Immich uses it to build shared links; without it, links point to the internal address.

Under Administration > Users you can create accounts for other people. Each user gets their own timeline and storage quota.

Step 7 - Backing up your phone with the mobile app

Install the Immich app from the App Store, Google Play or F-Droid, then:

  1. Enter https://your_domain as the server endpoint.
  2. Log in with your Immich account.
  3. Tap the cloud icon in the top bar to open the backup screen.
  4. Select the albums to back up (for example your camera roll) and exclude albums such as screenshots.
  5. Enable backup. In the app settings you can restrict uploads to Wi-Fi and enable background backup so new photos upload without opening the app.

On the server, the uploads appear in the timeline within seconds, and the machine learning jobs pick them up for face detection and smart search. Open Administration > Jobs to watch the queues; the first backup of a large camera roll keeps them busy for a while.

Step 8 - Adding an existing photo folder as an external library

If you already have photos on the server, for example on a mounted disk at /mnt/photos, Immich can index them in place without copying them. Mount the folder read-only into the server container by adding a line under the volumes of the immich-server service in /opt/immich/docker-compose.yml:

      - /mnt/photos:/mnt/photos:ro

Recreate the containers to apply the new mount:

cd /opt/immich
sudo docker compose up -d

In the web interface, open Administration > External Libraries, click Create Library, choose the owner, add /mnt/photos as an import path and click Scan. The photos appear in the owner's timeline once the scan and thumbnail jobs finish. Immich rescans external libraries periodically; you can change the schedule in the library settings.

Step 9 - Backing up the database

The database holds users, albums, faces and the link between every asset and its file. Losing it means rebuilding everything, so back it up together with the photos.

Immich runs a scheduled database dump job by default and writes the dumps to backups inside UPLOAD_LOCATION. Check that it is enabled under Administration > Settings > Backup Settings, and confirm the files exist:

sudo ls -lh /opt/immich/library/backups

You can also take a manual dump at any time, for example before an upgrade:

cd /opt/immich
sudo docker compose exec -T database pg_dumpall --clean --if-exists --username=postgres \
  | gzip | sudo tee /opt/immich/manual-dump-$(date +%F).sql.gz > /dev/null

For off-server backups, copy all of /opt/immich/library except thumbs and encoded-video, which Immich can regenerate. Do not copy /opt/immich/postgres while the database is running; the dumps are what you restore from.

Step 10 - Upgrading Immich

Read the release notes for breaking changes first and take a manual dump. Then download the Compose file of the new release:

cd /opt/immich
sudo wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml

Reapply the 127.0.0.1 port binding from Step 3 (and the external library mount from Step 8, if you added one). If the release notes mention new variables in example.env, add them to your .env. Then pull the images and recreate the containers:

sudo docker compose pull
sudo docker compose up -d

Troubleshooting

The mobile app cannot connect. Open https://your_domain/api/server/ping in the phone's browser. If it does not return {"res":"pong"}, check DNS, the certificate and sudo docker compose logs --tail 50 immich-server.

Large videos fail to upload. Make sure client_max_body_size and the timeouts from Step 5 are in the active server block, and that no other proxy (for example a CDN) in front of Nginx limits the request size.

Smart search or face detection does not return results. Check sudo docker compose logs --tail 50 immich-machine-learning. If the container restarts, it is usually out of memory: check free -h and add RAM or swap. Then rerun the Smart Search and Face Detection jobs from Administration > Jobs.

Disk fills up faster than expected. Check which folder grows with sudo du -sh /opt/immich/library/*. Transcoded videos and thumbnails can add a significant percentage on top of the originals.

Conclusion

Immich now runs on Ubuntu 24.04 behind Nginx with HTTPS, receives photos from your phone automatically, indexes an existing folder in place and dumps its database every day. As next steps, set storage quotas per user, share albums with other users or public links, and copy the library and dumps off the server with restic following the 3-2-1 backup strategy.