Managing photos with self-hosted Immich
- Published on
- ·5 min read
Why self-host your photos?
Family photos are probably the most valuable digital thing you own. Yet most of us throw them at Google Photos or iCloud without a second thought. Here's the catch: you're hostage. Google cuts free storage? Happens. Google compresses your photos? Yeah, they do that. Worse - they use your photos to train AI. Your kids' photos. Tell me that doesn't feel wrong.
Self-host on your NAS and you own it. No cloud storage limits. Share with family without middlemen. You're in control.
Immich: the Google Photos alternative we needed
Immich is open-source and replicates the core Google Photos experience: automatic phone backup, facial recognition, smart search, shared albums, smooth timeline. The project is actively developed - each release actually brings new features, not just bug fixes.
What sets it apart from competitors like Photoprism and LibrePhotos? The mobile apps. iOS and Android clients that do automatic background backup just like Google Photos. That's what convinced my family to switch - the UX is nearly identical, so zero friction.
Architecture and deployment
Immich is several services: main server, PostgreSQL, and a machine learning service for facial recognition and semantic search. Everything on an isolated photos_net Docker network.
# docker-compose.yml - Immich
services:
immich-server:
image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
container_name: immich-server
environment:
- DB_HOSTNAME=immich-postgres
- DB_USERNAME=immich
- DB_PASSWORD=${DB_PASSWORD}
- DB_DATABASE_NAME=immich
- IMMICH_MACHINE_LEARNING_URL=http://immich-ml:3003
volumes:
- ${UPLOAD_LOCATION}:/usr/src/app/upload
- /mnt/data/photos/external:/usr/src/app/external
- /etc/localtime:/etc/localtime:ro
networks:
- photos_net
depends_on:
- immich-postgres
- immich-ml
restart: unless-stopped
immich-ml:
image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
container_name: immich-ml
volumes:
- /mnt/data/apps/photos/ml-cache:/cache
networks:
- photos_net
restart: unless-stopped
immich-postgres:
image: docker.io/tensorchord/pgvecto-rs:pg16-v0.2.0
container_name: immich-postgres
environment:
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_USER=immich
- POSTGRES_DB=immich
- POSTGRES_INITDB_ARGS='--data-checksums'
volumes:
- /mnt/data/apps/photos/postgres:/var/lib/postgresql/data
networks:
- photos_net
restart: unless-stopped
networks:
photos_net:
name: photos_net
The .env file holds the sensitive bits:
# .env - Immich configuration
IMMICH_VERSION=release
DB_PASSWORD=a_strong_password_here
UPLOAD_LOCATION=/mnt/data/photos/upload
Web UI on port 2283. In production it goes through Caddy with automatic TLS.
Storage and external libraries
Directory layout
/mnt/data/
├── apps/photos/
│ ├── postgres/ # PostgreSQL database
│ └── ml-cache/ # ML model cache
└── photos/
├── upload/ # Photos uploaded via app
└── external/ # Imported external libraries
Migrating from a previous NAS
The feature that saved me hours: external libraries. When migrating from my old Synology, I had 50,000 photos organized in folders (Family/Year/Event). Re-uploading everything through the mobile app? Nightmare. Instead, I just mounted the folder in the container and declared it as an external library in Immich.
Immich scanned everything, indexed it, extracted EXIF metadata, ran facial recognition and auto-tagging. Photos appeared in the timeline as if I'd uploaded them natively. Best part: original files stay in place, nothing duplicated.
AI features (genuinely impressive)
This is where Immich really shines.
- Facial recognition: automatically detects and groups faces. Name someone once, all their photos are found. Search "Jules skiing" - boom, there they are.
- Semantic search (CLIP): search in natural language. "Beach sunset" returns matching photos even without tags. The CLIP model understands image content.
- Object detection: photos auto-tagged with what's in them (car, dog, mountain, etc.).
Performance on Intel N95
ML runs on CPU, no GPU. On the N95 (4 cores), initial indexing is slow - expect roughly 2-3 seconds per photo across all models. 50,000 photos? Several days of background processing. But that's one-time. After that? Only new photos get processed, nearly instant.
The ML model cache in /mnt/data/apps/photos/ml-cache prevents re-downloading 2GB of models on every container restart. First launch takes time. After that? Fast startup.
Mobile app and automatic backup
The Immich app (iOS and Android) beats Google Photos hands down:
- Automatic background backup, Wi-Fi or mobile data
- Timeline works the same way
- Album sharing with other users
- Search with the same AI capabilities as the web UI
Setup is straightforward: enter server URL, log in via OIDC (Authelia integration), enable backup. Photos land in /mnt/data/photos/upload, each user gets their own folder.
SSO and authentication
Immich natively supports OIDC. On my setup, Authelia is the identity provider. Each family member has their own account, private photo space, and albums. Sharing happens through shared albums - exactly like Google Photos.
Backup strategy
Photos live on a RAID array with Btrfs. First layer of protection. But RAID isn't backup.
- Daily PostgreSQL dump: cron runs
pg_dump, stores encrypted dump on separate volume - Btrfs snapshots of photo directories: automatic snapshots with retention (7 days of daily snapshots, 4 weekly)
Photos are immutable by nature. A Btrfs snapshot is sufficient to protect them. PostgreSQL needs a proper dump for consistency.
The takeaway
Immich transformed how my family manages photos. The experience is close enough to Google Photos that switching felt seamless. Full control over data. External library support made migration painless. AI features (facial recognition, semantic search) work remarkably well even on modest CPU like the N95. It's the self-hosted app that delivers the most everyday value.
Debian NAS from scratch series — This article is part of a complete series on building a Debian NAS.
Previous: Building a complete media center with Jellyfin and the *arr stack | Next: NAS monitoring: watching disks, services and logs at a glance