Immich is a self-hosted photo and video backup server that gives you Google Photos-style features on your own hardware. To self-host Immich with Docker Compose you download the official compose file and .env from an Immich release, point the upload and database paths at your VPS disk, then run docker compose up -d.
This guide walks through the whole path: preparing the host, starting the stack, putting HTTPS in front of it, connecting the mobile app, and backing up the library so a dead disk does not cost you every photo you own.
Before you start
You need:
- A Linux VPS with root access and at least 2 GB RAM. Immich runs PostgreSQL, Redis, the server and a machine-learning container, so give it room.
- Docker Engine and the Compose plugin. If you have not installed them yet, follow Install Docker and Docker Compose on a Linux VPS.
- A domain name pointing at your server's IP if you want HTTPS and mobile app uploads outside your home network. Replace
immich.example.comand203.0.113.10below with your own values. - Disk space for the photo library. Uploads grow fast, so plan for a separate volume or a large partition.
Log in to your server and confirm Docker works:
docker --version
docker compose versionNote: You manage power, reinstall and the browser console for this VPS in your VPS provider's control panel. If you lock yourself out over SSH, the console there still gets you in.
Self-host Immich with Docker Compose
1. Create the project directory
Keep Immich in its own directory so the compose file, .env, library and database sit together.
sudo mkdir -p /opt/immich
sudo chown deploy:deploy /opt/immich
cd /opt/immich2. Download the official compose file and env template
Immich ships a compose file and an example env file with every release. Always take them from the release you intend to run, because service names and images change between versions. Open the Immich releases page, copy the latest tag, and substitute it for vX.Y.Z:
curl -L -O https://github.com/immich-app/immich/releases/download/vX.Y.Z/docker-compose.yml
curl -L -o .env https://github.com/immich-app/immich/releases/download/vX.Y.Z/example.envCheck the compose file before you start anything:
grep -n "ports:" -A 2 docker-compose.yml
grep -n "image:" docker-compose.ymlThe server container publishes port 2283 by default. If you plan to run a reverse proxy on the same host, you can leave it as is and point the proxy at 127.0.0.1:2283.
3. Edit the.env file
Open .env and set the paths and credentials. A minimal version looks like this:
UPLOAD_LOCATION=./library
DB_DATA_LOCATION=./postgres
TZ=Etc/UTC
IMMICH_VERSION=release
DB_PASSWORD=replace-with-a-long-random-password
DB_USERNAME=postgres
DB_DATABASE_NAME=immichGenerate a strong database password rather than typing one:
openssl rand -base64 32Warning: Changing UPLOAD_LOCATION or DB_DATA_LOCATION after the first start makes Immich look at empty directories and appear to lose your photos. Decide the paths now, and if you must move them later, stop the stack and move the data first.
If your library lives on a separate disk, mount it and point UPLOAD_LOCATION at the mount point, for example /mnt/photos/immich. Make sure the deploy user can write there.
4. Start the stack
docker compose up -d
docker compose psYou should see four containers: immich_server, immich_machine_learning, immich_redis and immich_postgres. The database takes a few seconds to initialise on the first run. Watch the server until it reports it is listening:
docker compose logs -f immich-serverPress Ctrl+C to stop following the log. Then open http://203.0.113.10:2283 in a browser and create the first administrator account. That first account becomes the admin, so create it immediately.
5. Add HTTPS
The mobile app and browsers both expect a real certificate. The simplest route is a reverse proxy container. Follow Nginx Proxy Manager in Docker: HTTPS for many self-hosted apps and create a proxy host for immich.example.com forwarding to your server's IP on port 2283.
Two settings matter for photo uploads:
- Enable Websockets Support so the upload progress and job queues work.
- Raise the maximum body size. Nginx defaults to 1 MB, which fails on any real photo. Set
client_max_body_size 50000m;in the advanced tab.
If you prefer plain Nginx, the equivalent block is:
server {
listen 443 ssl;
server_name immich.example.com;
client_max_body_size 50000m;
location / {
proxy_pass http://127.0.0.1:2283;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}6. Connect the mobile app
Install the Immich app from the App Store or Play Store, then enter your server URL as https://immich.example.com — no port number when you use HTTPS on 443. Log in with the admin account you created, and turn on automatic backup for the camera roll.
Tip: Let the first backup finish on Wi-Fi before you add a second device. The initial upload is the heaviest job the server will ever run.
Back up the Immich library
Immich stores two things you must protect: the upload folder (your original files and generated thumbnails) and the PostgreSQL database (albums, users, faces, metadata). Backing up only one of them gives you a broken restore.
Dump the database:
cd /opt/immich
docker compose exec -T database pg_dumpall --clean --if-exists --username=postgres > immich-db-$(date +%F).sqlThen copy the upload folder and the dump off the server. restic handles both in one snapshot and encrypts everything before it leaves the machine — see Encrypted off-site backups with restic: init, snapshot, restore and Sync VPS backups to Backblaze B2 or Google Drive with rclone.
Warning: Never restore a database dump into a running stack. Stop the containers with docker compose down, restore the upload folder and the dump, then start again. Restoring over live data can corrupt the database and leave you with an unusable library.
Check it works
docker compose psshows all four containers as running, anddatabaseas healthy.https://immich.example.comloads over a valid certificate with no browser warning.- Upload one photo from the web interface and confirm it appears in the timeline.
- Upload one photo from the mobile app and confirm the same photo shows up on the web.
- Run the database dump command above and confirm the
.sqlfile is not empty.
Troubleshooting
immich_serverrestarts in a loop with a database connection error — the server started before PostgreSQL finished initialising. Checkdocker compose logs databasefor errors, thendocker compose restart immich-server. If it keeps happening, add a healthcheck-based dependency as described in Docker Compose depends_on and healthchecks.- Mobile app says it cannot reach the server — usually a certificate or URL problem. Use the full
https://URL, confirm the certificate is trusted by the phone, and check that port 443 is open in your firewall. A self-signed certificate will be rejected by the app. - Uploads fail with HTTP 413 or stop at a few percent — the reverse proxy body limit is too small. Set
client_max_body_size 50000m;and reload the proxy. permission deniedwhen writing to the upload folder — the container user cannot write to the mounted path. Fix ownership on the host withsudo chown -R 1000:1000 /mnt/photos/immichand restart the stack.- The machine-learning container eats all the RAM — it loads models for face and object detection. Either give the VPS more memory or disable the container in the compose file if you do not need smart search.
Next steps
- Put a proper proxy and certificate in front of the stack with Nginx Proxy Manager in Docker: HTTPS for many self-hosted apps.
- Automate the database dump with Schedule restic backups with systemd timers on a Linux VPS.
- Learn how volumes behave before you move data around in Back up Docker volumes with restic and restore them.
