Self-Hosting Immich for Private Photo Backups

by · Saturday. Mar 28, 2026

Hosting Immich

After Jellyfin (video) and Audiobookshelf (audio), I wanted something like Google Photos, but not Google Photos — automatic phone backups, a clean timeline, and the fun stuff like face grouping and smart search. That’s where Immich fits: a self-hosted photo/video manager built around browsing, searching, and backing up your library on your own server.

The key decision I made for this install: store all uploaded photos/videos on a separate hard drive mounted at: /srv/storage/pictures.

Immich is also the first service in this series where I really care about backups. Movies can be replaced. Audiobooks can usually be re-downloaded. Family photos and phone videos are different. I’m treating Immich as a convenient photo library, not as the only copy of my photos.

The official Immich install docs are the reference for the Docker Compose files below. Immich moves quickly, so I would trust the current docs over any old command snippet in this post if they ever disagree.

What this solves

  • Backs up phone photos to a private LAN service.
  • Keeps large photo uploads on a mounted storage drive.
  • Separates app config from user media.
  • Makes backup planning explicit before trusting the library.

1. Create the Immich app folder

This is just where I keep the compose file + env file.

sudo mkdir -p /srv/appdata/immich
sudo chown -R $USER:$USER /srv/appdata/immich
cd /srv/appdata/immich

I keep app configuration under /srv/appdata/ and large media under /srv/storage/. That split makes the server easier to reason about: config and databases are one category, user data is another. It also makes backup planning less vague because I can decide exactly which paths matter.

2. Download the official docker-compose.yml + .env

Immich’s docs have you grab these from the latest release assets.

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/example.env

Before editing anything, I quickly scan the files so I know what the stack is going to run:

less docker-compose.yml
less .env

That is not a full security audit. It is just a sanity check before I run a service that will hold personal photos. For any self-hosted app with Docker Compose, I like knowing which containers, volumes, and ports are involved before the first up -d.

3. Put the photo library on a mounted hard drive

I’m making sure the heavy stuff (photos/videos) lives on the big drive, while the database stays fast and local. I made sure the drive is mounted before starting Immich, so uploads don’t accidentally land on the root disk.

sudo mkdir -p /srv/storage/pictures/immich
sudo mkdir -p /srv/appdata/immich/postgres
sudo chown -R $USER:$USER /srv/storage/pictures/immich /srv/appdata/immich/postgres

Edit .env:

vim .env

Set these values:

# Store uploaded photos/videos on the mounted drive
UPLOAD_LOCATION=/srv/storage/pictures/immich

# Keep database data on the server disk
DB_DATA_LOCATION=/srv/appdata/immich/postgres

# Timezone
TZ=America/Los_Angeles

I also verify the mounted drive before starting Immich:

findmnt /srv/storage/pictures
df -h /srv/storage/pictures

This is a small but important guardrail. If the drive is not mounted and the directory still exists, Docker can happily write uploads to the root disk. That failure mode is quiet until the laptop’s internal drive fills up.

For a more permanent setup, /etc/fstab should mount the drive automatically on boot. I’m not covering the full disk setup here, but the Debian fstab manual page is the reference for the file format.

4. Start Immich

docker compose up -d

After the containers start, I check them before opening the browser:

docker compose ps
docker compose logs --tail=100

I’m looking for containers that are restarting, database errors, or permission errors on the upload path. If the logs are quiet and the web container is up, then I move to the first-run page.

5. First boot

On another device, I opened: http://<server-ip>:2283

Clicked Getting Started, created the first account, and that first user becomes the admin. Next, I uploaded a photo from the browser and watch it land on the hard drive path.

The upload test is deliberate. Before importing thousands of images, I want one tiny proof that the storage path is correct:

find /srv/storage/pictures/immich -type f | head

If that command shows the new upload, the main data path is working. If it does not, I stop and fix storage before letting my phone sync anything.

6. Mobile app + auto-backup

Once Immich was running on my LAN, the next thing I did was get my phone backing up automatically. Immich’s Quick Start flow is basically: install the app, connect to your server, then enable backup from the cloud/backup screen.

Then in the app I:

  • opened the Backup screen (cloud icon)
  • selected the album(s) I want backed up
  • toggled Enable backup

On iOS, I also made sure Background App Refresh is enabled for Immich (and I avoid Low Power Mode when I want uploads to happen), because iOS controls when background uploads actually run.

The first batch of photos took a while and made the fans run, but after that everything worked smoothly.

Immich’s mobile app backup docs are worth reading because phone operating systems are opinionated about background work. If backups seem slow, it may not be Immich itself. It might be iOS or Android deciding when the app is allowed to run.

7. My backup rule for Immich

Immich makes photos easy to browse, but it is not a backup strategy by itself. My rule is simple: if a file only exists inside Immich, it is not safe yet.

At minimum, the paths I care about are:

/srv/storage/pictures/immich
/srv/appdata/immich/postgres
/srv/appdata/immich/.env
/srv/appdata/immich/docker-compose.yml

The photo upload directory is obvious. The database matters too because it stores users, metadata, albums, and application state. Immich’s own backup and restore docs explain this more completely, especially the difference between backing up files and backing up the database.

For now, I’m keeping this mental model: Immich is the library, the mounted drive is the main storage, and a separate backup should exist outside this laptop. That can be another drive, another machine, or eventually an off-site backup. The important part is not pretending that RAID, Docker volumes, or a nice web UI replace backups.

8. What I would check after a reboot

Because Immich depends on both Docker and the mounted photo drive, I test a reboot before calling the setup done:

sudo reboot

After the server comes back:

findmnt /srv/storage/pictures
cd /srv/appdata/immich
docker compose ps

If the drive is mounted and the containers are healthy, then the setup is ready for normal use. If the drive is missing, I fix that before starting Immich again.