22 min read

How to Self-Host Immich and Access Your Photo Library from Anywhere

Google Photos now limits free storage to 15GB shared across all Google services. iCloud charges monthly for anything meaningful. Both scan your photos, use them to train machine learning models, and can restrict your access at any time.

Self-Hosting · Photos · Docker · Google Photos Alternative · 2026

How to Self-Host Immich and Access Your Photo Library from Anywhere

Google Photos now limits free storage to 15GB shared across all Google services. iCloud charges monthly for anything meaningful. Both scan your photos, use them to train machine learning models, and can restrict your access at any time. Immich is a self-hosted, open-source Google Photos replacement that runs on your own hardware. It has a near-identical mobile experience, automatic backup from your phone, facial recognition, smart search, shared albums, and a polished web interface. This guide walks through a complete verified installation of Immich v2.5.6 using the official Docker Compose method, explains every configuration decision that matters, and shows how to expose your instance securely over the internet using Localtonet so you can back up photos and access your library from anywhere.

🐳 Docker Compose · Valkey · VectorChord 🤖 Facial Recognition · Smart Search · CLIP 🌐 Remote access via Localtonet ✅ Verified: Immich v2.5.6 (February 2026)

Why Immich Has Become the Self-Hosting Community's Go-To Photos App

Immich started in February 2022 as a personal project and grew to over 90,000 GitHub stars by early 2026, making it one of the fastest-growing self-hosted applications ever built. The reason is straightforward: it is the first self-hosted photo manager that actually feels like a consumer product rather than a technical experiment.

📱 Automatic Mobile Backup The iOS and Android apps run silently in the background and upload new photos and videos to your server automatically, exactly like Google Photos. No manual steps after setup.
🔍 AI-Powered Smart Search Search your library using natural language. "Beach sunset 2023", "birthday cake", "dog in snow" all work. Powered by CLIP embeddings running entirely on your hardware, with no data sent to any external service.
👤 Facial Recognition Automatically groups photos by the people in them. Works completely offline. Name the people once and Immich finds all their photos across your entire library.
📍 Map View and EXIF Data Photos with GPS metadata are plotted on an interactive map. Filter by location, zoom into a region, and see all photos taken there. Full EXIF data is displayed and searchable.
✏️ Non-Destructive Editing (v2.5+) Edit photos directly in the browser: crop, adjust brightness, contrast, saturation. All edits are stored as metadata changes, never modifying the original file. Revert to the original at any time.
🔒 Your Photos Never Leave Your Server Every photo, every video, every piece of metadata stays on your hardware. No cloud account, no subscription, no company that can raise prices, change policies, or shut down.

Immich v2.0 is now stable — the "experimental" warning is gone

For most of its history, Immich's website displayed a large warning saying the project was under heavy development and not suitable for storing irreplaceable photos. That warning was removed with the v2.0.0 stable release in October 2025. The project now follows semantic versioning, breaking changes are limited to major version releases, and the team has committed to long-term compatibility. The current version is v2.5.6, released February 10, 2026.

What Gets Installed: The Four Containers

Immich runs as four Docker containers that work together. Understanding what each one does matters when things go wrong.

ContainerImagePurpose
immich_server ghcr.io/immich-app/immich-server The main application. Serves both the web UI (SvelteKit) and the REST API (NestJS) on a single port. Handles uploads, manages albums, schedules background jobs, and serves thumbnails. This is the only container that needs to be publicly accessible.
immich_machine_learning ghcr.io/immich-app/immich-machine-learning Python FastAPI service that runs CLIP embeddings for smart search and the face detection models. Called internally by immich-server over HTTP. Downloads and caches AI models on first run (several hundred MB). Never exposed to the internet.
immich_redis docker.io/valkey/valkey:8-bookworm Job queue broker. Despite the container being named immich_redis, it actually runs Valkey, which is the open-source Redis fork that Immich migrated to. Manages background task queues: thumbnail generation, metadata extraction, ML inference jobs. Never exposed to the internet.
immich_postgres ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0 Custom PostgreSQL image with VectorChord and pgvector extensions pre-installed. Stores all metadata: asset records, albums, users, face clusters, CLIP embeddings for search, and system configuration. The actual photo and video files are stored on disk, not in the database. Never exposed to the internet.
Why a custom PostgreSQL image?

Immich's smart search and facial recognition features rely on vector similarity search — finding photos that are "similar" to a query by comparing high-dimensional numerical vectors. This requires special database extensions. The official Immich Postgres image bundles VectorChord (the successor to the now-deprecated pgvecto.rs) and pgvector directly into the image, so no separate extension installation is needed. Do not use a standard PostgreSQL image — smart search and face clustering will not work without these extensions.

Requirements Before You Start

RequirementMinimumRecommended
OSAny Linux with DockerUbuntu 22.04 / 24.04, Debian 12
CPUAny x86_64 or arm644+ cores. ML inference is CPU-intensive on first use.
RAM4 GB6 GB+. Machine learning container needs ~1-2 GB during inference.
StorageEnough for your photo libraryImmich stores originals + thumbnails. Budget roughly 1.5x your current photo library size.
DockerDocker Engine 25+Install from Docker's official repository, not the distro's packages.
Database storageLocal disk (SSD preferred)SSD strongly recommended. NFS is explicitly not supported for the database volume.
Do not store the database on a network share (NFS)

The Immich documentation explicitly states that network shares are not supported for DB_DATA_LOCATION. PostgreSQL requires POSIX-compliant file locking that NFS does not reliably provide, which leads to database corruption. Store the database volume on local disk. You can store UPLOAD_LOCATION (the actual photo files) on a network share or NAS if you need the storage, but the Postgres data directory must be local.

Step 1: Install Docker Engine

Install Docker Engine from Docker's official repository, not from your Linux distribution's package manager. The distribution's docker.io package is often outdated and causes known issues with Immich's compose file format.

bash — Ubuntu / Debian
# Remove any old Docker packages from the distro
sudo apt remove docker docker-engine docker.io containerd runc 2>/dev/null

# Install prerequisites
sudo apt update
sudo apt install ca-certificates curl gnupg -y

# 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 Docker's 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

# Install Docker Engine and Compose plugin
sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io docker-compose-plugin -y

# Allow your user to run docker without sudo
sudo usermod -aG docker $USER
newgrp docker

Verify the installation:

bash
docker --version && docker compose version
output
Docker version 27.5.1, build 9f9e405
Docker Compose version v2.33.1

Step 2: Download the Official Compose Files

The official installation method is to download the compose file and environment file directly from the latest Immich release. Do not manually copy compose files from blog posts or tutorials — the files change between releases and an outdated compose file is the most common cause of installation problems.

bash
mkdir ~/immich-app
cd ~/immich-app

# Download the official docker-compose.yml from the latest release
wget -O docker-compose.yml \
  https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml

# Download the example .env file
wget -O .env \
  https://github.com/immich-app/immich/releases/latest/download/example.env
Always use the release version of docker-compose.yml, not the main branch

The docker-compose.yml on the main branch of the Immich GitHub repository may reference unreleased image versions and is not compatible with the latest stable release. The URL above always points to the compose file for the latest stable release. The downloaded file includes a warning comment at the top reminding you of this.

Step 3: Configure the Environment File

Open the .env file and configure the three required variables:

bash
nano .env
.env — default contents with annotations
# Where your uploaded photos and videos are stored on the host.
# This directory will grow as you add photos. Point it to wherever
# you have storage space. Can be on a NAS or external drive.
UPLOAD_LOCATION=./library

# Where the PostgreSQL database files are stored on the host.
# MUST be on a local disk. NFS is not supported.
# SSD recommended for better performance.
DB_DATA_LOCATION=./postgres

# Set your timezone for correct timestamps. Examples:
# Europe/Istanbul, America/New_York, Asia/Tokyo
# Full list: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
TZ=Europe/Istanbul

# The Immich version to use. "v2" pins to the current major version
# and receives all 2.x.x updates. To pin to a specific version
# for controlled upgrades, use a tag like "v2.5.6".
IMMICH_VERSION=v2

# Database password. Use only A-Za-z0-9 characters to avoid
# Docker parsing issues with special characters.
# This password is only used for local container communication —
# the database is never exposed to the internet.
DB_PASSWORD=useAStrongRandomPasswordHere

# Do not change these unless you have a specific reason to.
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

The only required changes are:

VariableWhat to set
UPLOAD_LOCATIONPath where photos will be stored. Use an absolute path like /mnt/data/immich/library if you have a dedicated storage drive. For a quick start, ./library creates a folder next to the compose file.
DB_DATA_LOCATIONPath for the database. Must be local disk, not NFS. ./postgres is fine for most setups.
TZYour timezone. Affects timestamps on photos and scheduled tasks.
DB_PASSWORDChange from the default. Alphanumeric only, no special characters.

Step 4: Storage Type Optimization (SSD vs HDD)

Immich's custom PostgreSQL image has two different performance configurations built in — one optimized for SSDs and one for HDDs. The difference is in how PostgreSQL handles I/O concurrency: SSDs benefit from parallel I/O, while HDDs perform better with sequential access patterns.

By default, Immich assumes SSD storage. If your database is on a spinning disk or a slow NAS-attached volume, add this to the database section of your docker-compose.yml:

docker-compose.yml — database service, add under environment:
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
      DB_STORAGE_TYPE: 'HDD'   # add this line if your DB is on HDD

Step 5: Start the Containers

bash
docker compose up -d
output
[+] Running 5/5
 ✔ Network immich_default                    Created
 ✔ Container immich_redis                    Started
 ✔ Container immich_postgres                 Started
 ✔ Container immich_machine_learning         Started
 ✔ Container immich_server                   Started

Wait about 60 to 90 seconds for all containers to finish initializing. The first startup takes longer than subsequent ones because the database schema is being created and initial migrations are running. Check that everything is healthy:

bash
docker compose ps
output
NAME                       STATUS
immich_postgres            running (healthy)
immich_redis               running (healthy)
immich_machine_learning    running (healthy)
immich_server              running (healthy)

All four containers should show running (healthy). If immich_server shows running (health: starting), wait another minute and check again — it performs a health check on startup that takes time to pass. If any container shows exited, check its logs:

bash
docker compose logs immich_server --tail=50

Step 6: Create Your Admin Account

Open your browser and navigate to http://localhost:2283 (or the server's local IP address on port 2283 if you are accessing it from another device on your network).

Immich runs on port 2283, not 8080

Many older tutorials show Immich on port 8080. That was changed in an earlier version. The current port is 2283. If you navigate to port 8080 and get no response, try port 2283.

On first access, you are taken directly to the account creation screen. Enter a name, email, and strong password for your admin account. This first account automatically receives administrator privileges.

After logging in, visit the Administration panel (top right menu) to review the default settings before adding any content:

SettingWhere to find itWhat to consider
Storage templateAdministration → Storage TemplateControls how files are organized on disk. Default is flat. You can set a template like {{y}}/{{MM}}/{{dd}} to organize by date.
Thumbnail qualityAdministration → ImageHigher quality thumbnails look better but use more disk space and take longer to generate.
Machine learning modelsAdministration → Machine LearningCLIP model for smart search, recognition model for faces. Defaults work well. Larger models are more accurate but slower and use more RAM.
Video transcodingAdministration → Video TranscodingTranscoding makes videos compatible with all browsers. Requires significant CPU. Can be disabled if you only use modern formats (H.264 MP4).

Step 7: Set Up the Mobile App for Automatic Backup

Install the Immich app on your phone (available for iOS and Android). When you open it for the first time, it will ask for a server URL. For local network access, enter your server's local IP address and port: http://192.168.1.45:2283. After setting up remote access with Localtonet, you will update this to your public HTTPS URL.

After logging in, go to the app settings and enable Background Backup. The app will upload your existing photo library in the background and continue uploading new photos automatically as you take them, on Wi-Fi only by default (configurable to also use mobile data).

Free Up Space (new in v2.5.0)

Once your photos are backed up to Immich, you can use the Free Up Space feature in the mobile app to delete local copies from your phone. This removes files that have been successfully backed up (and are not in the Immich trash) from your device's gallery, reclaiming storage space without losing any photos. To complete the reclaim, you need to manually empty your phone's system trash folder after using this feature.

Step 8: Expose Immich to the Internet with Localtonet

For automatic mobile backup to work when you are away from home, and for remote access to your photo library from any device, Immich needs a public HTTPS URL. Localtonet provides this without port forwarding, a static IP, or a domain name.

Why Cloudflare Tunnel does not work well for Immich

Cloudflare Tunnel is a popular free tunneling option, but it enforces a 100MB upload size limit on free and Pro plans. Immich is primarily a photo and video backup service. Modern smartphone cameras produce video files well above 100MB, and RAW photos from mirrorless cameras can reach 50-80MB each. Uploading a short 4K video or a burst of RAW files will fail with an HTTP 413 error behind Cloudflare Tunnel. Localtonet has no upload size limit, making it the correct choice for an Immich tunnel.

Install and authenticate Localtonet

Download Localtonet for your platform from localtonet.com, install it on the same machine running Immich, and authenticate with your token from the Localtonet dashboard.

Create an HTTP tunnel to port 2283

In the Localtonet dashboard, create a new tunnel:

SettingValue
ProtocolHTTP
Local IP127.0.0.1
Local Port2283
Subdomaine.g. myphotosmyphotos.localto.net

Update the .env with your public URL

Immich needs to know its own public URL to generate correct links in shared album emails and API responses. Add this line to your .env file:

.env — add this line
IMMICH_SERVER_URL=https://myphotos.localto.net

Restart the server container to apply the change:

bash
docker compose restart immich_server

Navigate to https://myphotos.localto.net from any device. You should see the Immich login page over HTTPS. Localtonet provides the TLS certificate automatically.

Update the mobile app to use the public URL

In the Immich mobile app, go to Settings and update the server URL from the local IP address to your Localtonet public URL: https://myphotos.localto.net. The app will now back up photos over the internet when you are away from home, and continue to use the same URL when on your local network.

Backup and Restore

Immich v2.5.0 introduced a built-in database backup system. The server automatically creates database backups on a configurable schedule (default: daily). You can manage backups from the web UI at Administration → Maintenance, or configure the backup path in your .env file.

There are two things to back up:

WhatWhereContains
Photo and video files Your UPLOAD_LOCATION directory All original files, processed thumbnails, and video transcodes. This is your irreplaceable data. Back it up with rsync, rclone, or any backup tool that supports the filesystem.
Database Automatic backups in UPLOAD_LOCATION/backups/ All metadata: albums, faces, person names, shared links, favorites, descriptions, storage paths. Without this, your photos are unsorted files with no organization.

To trigger a manual database backup from the command line:

bash
docker compose exec immich_postgres pg_dumpall \
  --clean --if-exists --username=postgres | \
  gzip > ~/immich-db-backup-$(date +%Y%m%d).sql.gz

To restore from a backup (on a fresh installation with the same configuration):

bash
# Stop immich_server and immich_machine_learning first
docker compose stop immich_server immich_machine_learning

# Restore database
gunzip < ~/immich-db-backup-20260210.sql.gz | \
  docker compose exec -T immich_postgres psql \
  --username=postgres

# Restart everything
docker compose start

Keeping Immich Up to Date

Immich releases updates frequently. Since IMMICH_VERSION=v2 does not auto-update (it pins to the major version, not "latest"), you need to run an update manually:

bash
# Make a database backup first
docker compose exec immich_postgres pg_dumpall \
  --clean --if-exists --username=postgres | \
  gzip > ~/immich-backup-before-update.sql.gz

# Pull new images
docker compose pull

# Restart with new images (runs database migrations automatically)
docker compose up -d
Always read the release notes before major updates

Immich occasionally ships migrations that take significant time on large libraries (100,000+ assets). The server logs will show progress on operations like "Reindexing clip_index" and "Reindexing face_index". These are normal and should be allowed to complete. Do not stop the containers during a migration. Set IMMICH_VERSION to a specific version tag (e.g. v2.5.6) if you need to control exactly when updates apply.

Hardware Acceleration for Faster Thumbnails and ML

By default, Immich uses the CPU for all video transcoding and machine learning inference. On hardware with a compatible GPU or NPU, enabling acceleration dramatically reduces the time it takes to process a large library import.

HardwareAcceleration typeUse case
NVIDIA GPUCUDA (for ML) + NVENC (for video)Fastest option. Smart search and face detection run on GPU. Video transcoding uses hardware encoder.
Intel iGPU/ArcOpenVINO (ML) + QuickSync (video)Good option for Intel NUC or mini PC setups. Significantly faster than CPU for transcoding.
AMD GPUROCm (ML) + VAAPI (video)ML acceleration available on recent AMD GPUs. Video hardware transcoding via VAAPI.
Raspberry Pi 5ARM NNARM Neural Network acceleration. Reduces ML time compared to pure CPU on Pi hardware.

To enable hardware acceleration, uncomment the relevant extends section in docker-compose.yml and set the service type. For example, for NVIDIA GPU transcoding:

docker-compose.yml — immich-server service
  immich-server:
    extends:
      file: hwaccel.transcoding.yml
      service: nvenc   # set to: nvenc, quicksync, rkmpp, vaapi, or vaapi-wsl

The hwaccel.transcoding.yml and hwaccel.ml.yml files are downloaded alongside the main compose file. Full documentation is at docs.immich.app/features/ml-hardware-acceleration.

Common Problems and Solutions

ProblemLikely CauseFix
immich_postgres stuck at "health: starting" Database initialization in progress or VectorChord index rebuild taking time Wait up to 5 minutes for large libraries. Check docker compose logs immich_postgres --tail=20. If logs show "Reindexing" operations, this is normal — do not stop the container.
Smart search returns no results Machine learning container has not processed assets yet, or CLIP model failed to load Check docker compose logs immich_machine_learning for errors. Trigger a reprocess from Administration → Jobs → Smart Search → Run All.
Mobile app cannot connect after updating server URL App cached old URL or SSL certificate not trusted Force-close the app, clear its cache, reopen and re-enter the URL. Localtonet certificates are from a trusted CA and should be accepted by default.
Upload fails on large video files If using Cloudflare Tunnel: 100MB request limit enforced by Cloudflare proxy Switch to a Localtonet tunnel. Localtonet does not enforce upload size limits.
"unknown shorthand flag: 'd'" error on docker compose up Running the old docker-compose binary from the distro instead of the Docker Compose plugin Follow the Docker Engine install instructions in Step 1 to install from Docker's official repository. The correct command is docker compose (space, not hyphen).
Thumbnails not generating for new uploads Job queue backlog or Redis/Valkey connection issue Check all 4 containers are healthy. From Administration → Jobs, check if thumbnail jobs are queued or failing. Restart immich_redis if it shows as unhealthy.
Faces detected but not grouped / named faces reset Face clustering runs on a schedule, not immediately after upload Trigger manually from Administration → Jobs → Face Detection → Run All, then Administration → Jobs → Facial Recognition → Run All.

Frequently Asked Questions

Can I import my existing Google Photos library into Immich?

Yes. Download your Google Photos library using Google Takeout (takeout.google.com). Takeout exports your photos as ZIP files with accompanying JSON metadata files. The recommended tool for importing this into Immich is immich-go, a third-party CLI tool that reads the Takeout ZIP files, correctly parses the JSON metadata (including original capture dates), and uploads everything to your Immich instance via the API. Direct drag-and-drop upload of Takeout ZIPs to Immich does not preserve the original dates because the JSON metadata files need to be read and matched to each photo, which immich-go handles automatically.

How much RAM does Immich actually use?

At idle with no active processing, the full stack uses roughly 500 to 800 MB: immich-server around 200-300 MB, immich-machine-learning around 100-150 MB at idle (more when a model is loaded), Postgres around 100-200 MB, and Valkey under 50 MB. During active ML inference (smart search indexing or face detection on new uploads), the machine-learning container can use 1-2 GB temporarily while models are loaded into memory. For a comfortable experience including background processing, 4 GB total system RAM is the practical minimum and 6-8 GB is recommended for a library larger than a few thousand photos.

Does Immich modify my original photos?

No. Immich stores your original files exactly as uploaded and never modifies them. Thumbnails, transcoded videos, and CLIP embeddings are generated separately and stored alongside the originals. The non-destructive editing feature added in v2.5.0 works the same way: edits are stored as metadata in the database, not applied to the original file. The original is always available for download regardless of any edits made in the Immich interface.

Can multiple family members use the same Immich instance?

Yes. Immich has full multi-user support. Each user has their own private library. You can share specific albums with other users, and there is a "Partner sharing" feature where two users can choose to view each other's libraries. Each user has their own storage quota (configurable by the admin). The admin account can create new user accounts from Administration → Users.

What happens to my photos if Immich is unavailable?

Your photos remain safely on disk in UPLOAD_LOCATION. Immich stores files in their original format in a structured directory. If the Immich application is unavailable, you can still access all your original files directly through the filesystem. The database backs up metadata, but even without the database, your original photo files are intact and can be browsed normally.

Your Photos. Your Server. Accessible from Anywhere.

Immich gives you a Google Photos experience that runs entirely on your own hardware. Localtonet gives it a stable public HTTPS address with no upload limits, no port forwarding, and no domain required. Set up the tunnel in two minutes and start backing up your phone today.

Create a Localtonet HTTP Tunnel →

Localtonet is a secure multi-protocol tunneling and proxy platform designed to expose localhost, devices, private services, and AI agents to the public internet supporting HTTP/HTTPS tunnels, TCP/UDP forwarding, mobile proxy infrastructure, file server publishing, latency-optimized game connectivity, and developer-ready AI agent endpoint exposure from a single unified control plane.

support