Self-host Immich: a Google Photos replacement that actually works
Immich gives you a polished mobile app, AI-powered face and object search, and full control over your photos. Setup with Docker, Postgres, and Caddy on a VPS.

Immich is the first self-hosted photo solution that doesn't feel like a compromise. The mobile apps look and behave like Google Photos. Background upload works. Search-by-face and search-by-object work. It's actively developed and stable enough to trust your photo library to.
The catch: photos and videos use a lot of disk. Plan storage carefully.
Sizing
- Compute: 4 vCPU, 8 GB RAM minimum. Machine learning (face/object detection) is the bottleneck, not the database.
- Storage: Add up your existing library, double it for headroom. 500 GB is a reasonable starting point for a single user with a phone history; multi-user families easily hit 2 TB.
- GPU: Optional. Speeds up the initial ML pass on existing libraries by 5-20x. Not required for ongoing use after the first scan.
Stack
- Docker + Compose
- Postgres 16 with pgvecto.rs (Immich requires this; the official compose handles it)
- Redis
- Immich server, microservices, ML, web
The official compose stack is the only sane way to run Immich. Don't try to install bare-metal.
DNS first
Point photos.example.com at the VPS. Wait for DNS propagation.
Install
mkdir -p /srv/immich && cd /srv/immich
wget https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
wget https://github.com/immich-app/immich/releases/latest/download/example.env -O .env
Edit .env:
UPLOAD_LOCATION=/srv/immich-data
DB_PASSWORD=use-a-real-password
TZ=Europe/London
UPLOAD_LOCATION is where photos will physically live. Mount a separate volume there if you have one.
docker compose up -d
docker compose logs -f
Caddyfile
photos.example.com {
reverse_proxy localhost:2283
request_body {
max_size 10GB
}
}
Reload Caddy. The max_size is important: video uploads will fail otherwise. (See the Caddy guide.)
First-run setup
Open https://photos.example.com. The first user becomes admin. Pick a strong password.
Mobile apps
Install "Immich" from the App Store / Play Store. On first launch, enter your server URL (https://photos.example.com) and login. Allow the photo library permissions, allow background uploads, set the destination album.
The first sync uploads everything. On a typical 50 GB phone library over fibre, plan a few hours. Immich's app doesn't pause when locked, which is essential and absent from most competitors.
ML configuration
Out of the box, Immich runs CLIP for object/scene search and a face-detection model. They're conservative defaults. To enable face recognition:
Settings -> Machine Learning Settings -> Facial Recognition: enable. The system processes the existing library in the background. On CPU-only this can take days for a large library. Be patient or schedule overnight.
For better accuracy, swap to a larger CLIP model in admin settings (ViT-L-14 is a solid step up from the default). Watch RAM usage; the bigger models need more.
Albums and sharing
- Albums: created in the app or web UI, can include other Immich users on the same instance.
- Public links: per-album, with optional password and expiry. Useful for sharing a wedding album with relatives without giving them an account.
Backups
Two pieces: the database and the upload directory.
#!/bin/bash
DEST=/srv/immich-backups
DATE=$(date +%F)
mkdir -p "$DEST"
# Database
docker exec -t immich_postgres pg_dumpall -c -U postgres | gzip > "$DEST/db-$DATE.sql.gz"
# Just the originals - thumbnails and ML data regenerate
tar czf "$DEST/originals-$DATE.tar.gz" -C /srv/immich-data library
find "$DEST" -mtime +7 -delete
Cron nightly. Sync $DEST off-site with restic or rclone to a B2/S3 bucket. Encrypt the off-site copy at rest.
If you only back up one thing, back up library/ (the originals). Thumbnails and ML metadata are regeneratable.
Operational notes
- Updates:
docker compose pull && docker compose up -d. Read release notes; database migrations occasionally need a one-shot command. - Disk fills up fast. Monitor
df -hand set up a Netdata or Uptime Kuma alert at 80% full. Adding storage to a VPS isn't instant; plan ahead. - Don't expose the DB or Redis ports in the compose stack. The default compose binds them only to the Docker network, but double-check after any edit.
Migrating from Google Photos
Google Takeout exports your library as zips with metadata in JSON sidecars. Immich's immich-go tool understands this format and imports correctly, including original timestamps. The CLI is well-documented; expect a multi-hour upload for a typical history.
That's a self-hosted photo cloud that's good enough to delete the Google Photos app. The library is yours, the search works, and you control where it lives.
Continue reading