Self-Host Immich (Google Photos Replacement)
Deploy Immich — a full Google Photos replacement — on your own server using Docker Compose, with machine learning, mobile auto-backup, and album sharing.
Before you start
- ▸A Linux server with at least 4 GB RAM and sufficient disk for your photo library
- ▸Docker Engine 24+ and the docker-compose-plugin installed
- ▸A domain name or static local IP if you want remote or HTTPS access
- ▸Basic familiarity with editing files and running commands as root or via sudo
Immich is a self-hosted photo and video backup platform that covers virtually everything Google Photos offers: automatic mobile uploads, facial recognition, object tagging, shared albums, and a polished web UI. It is actively developed, uses PostgreSQL with the pgvecto.rs extension for vector search, and ships all components as Docker images. This guide walks through a production-ready deployment using Docker Compose, including the machine learning service, reverse proxy hints, and mobile client setup.
Prerequisites and Architecture
Immich runs as several cooperating containers:
- immich-server — main API and web UI
- immich-machine-learning — facial recognition and CLIP-based smart search
- postgres — database with pgvecto.rs extension pre-bundled in Immich's image
- redis — job queue and session cache
You need a host with at least 4 GB RAM (8 GB recommended if you want machine learning to be responsive), Docker Engine 24+ and the Compose plugin, and a dedicated data volume or directory with plenty of storage. Hardware transcoding (Intel QSV, NVIDIA CUDA, VAAPI) is optional but speeds up video processing significantly.
Installing Docker and Compose
Debian / Ubuntu
sudo apt update
sudo apt install -y ca-certificates curl gnupg
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
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
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Fedora / RHEL / Rocky
sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo systemctl enable --now docker
Arch Linux
sudo pacman -S docker docker-compose
sudo systemctl enable --now docker
Add your user to the docker group so you can run Compose without sudo (re-login after):
sudo usermod -aG docker $USER
Creating the Project Directory and Environment File
Immich's upstream repository provides official docker-compose.yml and .env templates. Always pull the latest release versions of these files rather than copying an outdated snippet.
mkdir -p ~/immich && cd ~/immich
curl -fsSL https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml -o docker-compose.yml
curl -fsSL https://github.com/immich-app/immich/releases/latest/download/example.env -o .env
Open .env and set the critical variables:
nano .env
The fields you must change:
- UPLOAD_LOCATION — absolute path where photos are stored, e.g.
/mnt/photos/immich - DB_DATA_LOCATION — path for PostgreSQL data files, e.g.
/mnt/photos/immich-db - DB_PASSWORD — set a strong random password; this is used internally between containers
- IMMICH_VERSION — pin to a specific release tag (e.g.
v1.106.4) for reproducible deployments; omit or setreleasefor rolling updates
UPLOAD_LOCATION=/mnt/photos/immich
DB_DATA_LOCATION=/mnt/photos/immich-db
DB_PASSWORD=replace_with_a_strong_password
IMMICH_VERSION=release
Create the directories and make sure they are owned by your user (Docker will bind-mount them):
mkdir -p /mnt/photos/immich /mnt/photos/immich-db
Reviewing the Compose File
The official docker-compose.yml already wires all four containers together. Key points to verify before starting:
- The
immich-serverservice exposes port 2283 on the host by default. If that conflicts, change the left side of the port mapping:"3000:2283". - The
immich-machine-learningservice has a dedicated model cache volume (model-cache). On first startup it downloads several hundred megabytes of CLIP and facial recognition models. - If you have an NVIDIA GPU, uncomment the
deploy.resources.reservations.devicesblock underimmich-machine-learningand ensure the NVIDIA Container Toolkit is installed on the host.
Starting Immich
cd ~/immich
docker compose up -d
Watch the logs during first boot — PostgreSQL initializes the schema and machine learning downloads its models:
docker compose logs -f immich-server
Startup is complete when you see a line similar to:
# Example output (yours will vary):
# immich-server | Immich Server is listening on 0.0.0.0:2283
Open a browser to http://<server-ip>:2283 and complete the first-run admin setup.
Putting Immich Behind a Reverse Proxy
Exposing port 2283 directly is fine for LAN use, but for remote access you should terminate TLS at a reverse proxy. Caddy is the simplest option:
# /etc/caddy/Caddyfile snippet
photos.example.com {
reverse_proxy localhost:2283
}
For NGINX Proxy Manager or Traefik, set the upstream to http://immich-server:2283 if the proxy is in the same Docker network, or http://127.0.0.1:2283 if it runs on the host. Immich requires WebSocket support — ensure your proxy passes Upgrade and Connection headers.
Immich also has a large file upload path (/api/assets). Set your proxy's client body size limit to at least 50000M (NGINX: client_max_body_size 50000M;).
Configuring the Machine Learning Service
Machine learning is enabled by default. In the Immich web UI navigate to Administration → Machine Learning to adjust:
- Smart Search — CLIP model for natural-language photo search (e.g. "dog at the beach")
- Facial Recognition — groups faces and lets you name people
- Min detection score — lower it to catch more faces at the cost of false positives
The machine learning container is CPU-only by default. If the ML container is consuming too much CPU during bulk ingestion, you can throttle job concurrency under Administration → Jobs.
Mobile Auto-Backup
Install the Immich app (immich.app links to both App Store and Play Store). During setup:
- Enter your server URL (e.g.
https://photos.example.comorhttp://192.168.1.10:2283). - Log in with the credentials you created during first-run setup.
- Go to Backup in the app and enable Background backup.
- Select which albums or folders to include (on Android, the app can access DCIM and other media folders; on iOS it requests Photos library access).
- Enable Require Wi-Fi and Require charging to avoid mobile data usage.
Backup progress appears in real time on both the mobile app and the web UI under your account's timeline.
Shared Albums and External Sharing
Immich supports two sharing models:
- Shared albums — invite other Immich users by email; they can view and optionally upload.
- Public share links — generate a password-optional link for anyone, including non-users. Set an expiry date for temporary shares.
To create a shared album, select photos in the web UI, click the share icon, and choose Create shared album. Add collaborators by their Immich account email. Collaborators see the album under their Sharing tab and can add their own assets if you grant upload permission.
Keeping Immich Updated
Because Immich is under active development, database migrations run automatically on startup. Always back up the database before upgrading:
docker exec -t immich_postgres pg_dumpall -U postgres > immich_backup_$(date +%F).sql
cd ~/immich
docker compose pull
docker compose up -d
Verification
# All containers should show "Up" status
docker compose ps
# Check Immich server health endpoint
curl -s http://localhost:2283/api/server/ping
# Expected: {"res":"pong"}
Troubleshooting
Containers keep restarting
Run docker compose logs immich-server and look for database connection errors. The most common cause is a mismatch between DB_PASSWORD in .env and what PostgreSQL was initialized with. If you changed the password after first boot, remove the DB data directory and let Postgres reinitialize (you will lose existing data).
Machine learning is extremely slow
The ML container defaults to CPU inference. With no GPU, facial indexing of a large library takes hours — this is normal. Reduce the job worker count under Administration → Jobs → Smart Search concurrency to avoid starving other services. If you have a supported GPU, enable it in the Compose file as noted above.
Photos not appearing after upload
Immich processes uploads asynchronously through job queues. Check Administration → Jobs in the web UI. If jobs are stuck, restart the server container: docker compose restart immich-server.
Mobile app cannot connect
Verify the URL has no trailing slash and includes the scheme (https:// or http://). If using a self-signed certificate, you must install the CA on the mobile device or the app will refuse the connection.
Frequently asked questions
- Can I migrate my existing Google Photos library to Immich?
- Yes. Use Google Takeout to export your library as a zip archive, then use the immich-go CLI tool (a popular third-party utility) to import the Takeout folder while preserving EXIF dates and album structure.
- Does the machine learning service require a GPU?
- No, it runs on CPU by default. A GPU (NVIDIA CUDA, Intel OpenVINO, or ARM NN) dramatically speeds up bulk indexing but is entirely optional for personal use.
- How do I store photos on an external drive or NAS NFS mount?
- Set UPLOAD_LOCATION in .env to the mounted path (e.g. /mnt/nas/immich). Make sure the mount is present before Docker starts; add a systemd mount unit and set docker.service to require it.
- Is my data safe if I lose the server?
- The photo files live in UPLOAD_LOCATION as plain files organized by date, so they are readable without Immich. Back up that directory plus a pg_dumpall database dump to recover fully, including albums, faces, and metadata.
- Can multiple family members have separate accounts?
- Yes. The admin creates additional user accounts under Administration → Users. Each user gets a private timeline and storage quota. Shared albums let users share specific photos across accounts.
Related guides
Configure Prometheus Alertmanager
Configure Prometheus Alertmanager with routing trees, receivers, inhibition rules, grouping, Go templates, and PagerDuty/Slack on-call integrations.
Build an Intranet Server on Linux
Set up a complete small-office intranet on one Linux box: Nginx web server, dnsmasq local DNS, Samba file sharing, and a Wiki.js team wiki.
Build an nftables Firewall Script
Build a complete nftables firewall from scratch: tables, chains, sets, default-deny input policy, service allowlisting, and persistent systemd configuration.
Caddy as a Reverse Proxy
Set up Caddy as a reverse proxy with automatic HTTPS, load balancing, WebSocket passthrough, reusable snippets, and header control — no certbot required.