Immich
The path: 1. Foundation → 2. Tailscale → 3. Docker → 4. Immich
Immich is a self-hosted photo library: phone apps with automatic backup, facial recognition, search, albums, sharing. It's the killer app of self-hosting — the one that finally makes normal family members care about your server.
Prerequisites, and I mean it: a foundation (Path A or B), Tailscale serve HTTPS working — we're baking that URL into your phone today and never touching it again — and Docker + Dockge.
1. Decide where photos live, before installing
The most important decision in this guide, and it's boring on purpose: the upload library goes on your biggest disk, not the OS disk. If you did the second-disk step in guide one, you already have /mnt/photos. If not, point it at a folder you understand — and know that migrating later is a project.
mkdir -p /mnt/photos/immich-library /mnt/photos/immich-db
2. Get the files
Immich ships its own compose file. Don't copy one from a forum — pull it from the official release:
mkdir -p /opt/stacks/immich && cd /opt/stacks/immich
wget -O docker-compose.yml https://github.com/immich-app/immich/releases/latest/download/docker-compose.yml
wget -O .env https://github.com/immich-app/immich/releases/latest/download/env.example
The .env holds your answers to a handful of questions:
| Variable | Set it to | Notes |
|---|---|---|
| UPLOAD_LOCATION | /mnt/photos/immich-library | The big disk. The whole point |
| DB_DATA_LOCATION | /mnt/photos/immich-db | Database next to it |
| IMMICH_VERSION | release | Their recommended tag; Immich moves fast |
| DB_PASSWORD | long random string | openssl rand -base64 24 generates one |
| TZ | Asia/Bangkok | Yours, obviously |
Leave the database image line exactly as shipped — Immich pins a specific database build for a reason. Don't "help."
3. Launch and lock the front door
docker compose up -d
docker compose ps # all containers Up
First login: your https://server-name.your-tailnet.ts.net URL (from the Tailscale guide, via tailscale serve --bg 2283 if you haven't yet). Register the admin account, then immediately go to Administration → Settings and disable open registration. You built a photo server, not a hostel.
4. The phone apps
Install the Immich app (Android/iOS). Three settings:
- Server URL: your
https://...ts.netaddress. - Background backup: on. Photos upload themselves from now on.
- Android — battery unrestricted: Settings → Apps → Immich → Battery → Unrestricted. Not optional: "optimized" quietly murders background uploads and you'll swear the app is broken. Ask me how I know. On a de-Googled phone it's the same dance as any sandboxed app — GrapheneOS guide.
On iOS, enable background refresh and let Tailscale run on-demand. Phones upload only when Tailscale is on — feature, not bug.
First bulk upload of years of photos: Wi-Fi, plugged in, foreground. Indexing runs on your CPU and a big library can take a day or three. It's working, not stuck — watch it from the server inside tmux if you want to see the gears turn.
5. Updates and backups
Updates (monthly is fine):
cd /opt/stacks/immich
docker compose pull && docker compose up -d
Skim the release notes first — Immich is one of the fastest-moving self-hosted apps in existence, and that's the price of the features.
Backups — know what actually matters:
- The library folder (originals) and the database are sacred. Database dump plus library equals a complete restore.
- Thumbnails and ML cache regenerate. Never back them up — that's paying to store what a re-index rebuilds for free. (Mine is excluded from backups by name.)
# database dump before any update, or on a schedule
docker exec -t immich_postgres pg_dump -U postgres -d immich \
> /mnt/photos/immich-db-dump-$(date +%F).sql
Golden rule: don't wipe photos off your phone until a backup exists and you've opened it. A backup you've never restored from is a hope, not a backup.
That's it. You own your photos the way you own your Bitcoin: not your keys, not your coins — not your disk, not your memories.
Next in the series: notes that sync end-to-end encrypted through your own CouchDB, over the same Tailscale front door.
← Previous: Docker and Dockge | Next: Obsidian LiveSync (coming soon)