Skip to content

Immich

Taking videos at 4k, 60 fps? Worried about the ever accumulating storage on your photo service drive? There is now a legitimate, self hosted alternative!

Here's how we install Immich.

Prerequisites

Get Docker installed. Setup is quite fast, see my documentation.

Cloning Docker Container

After you have Docker installed it's quite simple (see the official documentation). Create a folder for where the docker container will go. I'm creating a folder within a docker folder where other Docker apps can also be stored. Then we'll also download the config files, which will enable Docker Compose to spin up the container.

mkdir ./docker/immich-app
cd ./docker/immich-app
wget 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
wget https://github.com/immich-app/immich/releases/latest/download/hwaccel.yml

Change the environmental variables

sudo nano .env

Here you can customize the upload location of photos on your server as well as the database password.

Abstract
# You can find documentation for all the supported env variables
# at https://immich.app/docs/install/environment-variables

# The location where your uploaded files are stored
UPLOAD_LOCATION=./library

# The Immich version to use. You can pin this to a specific version like "v1.71.0"
IMMICH_VERSION=release

# Connection secret for postgres. You should change it to a random password
DB_PASSWORD=postgres

# The values below this line do not need to be changed
##############################
DB_HOSTNAME=immich_postgres
DB_USERNAME=postgres
DB_DATABASE_NAME=immich

REDIS_HOSTNAME=immich_redis

Change the password

Add your own password to the .env file here DB_PASSWORD=postgres

Start the Container

docker compose up -d

You should now see the container running in Docker Desktop if you happened to install that as well.

Immich in Docker Desktop

Open the App

Immich runs on port 2283. If you are on Ubuntu Desktop you can access the website via

localhost:2283

Otherwise, if you are access it from another computer on the network, make sure you allow the port on the firewall.

sudo ufw allow 2283

Nginx Reverse Proxy

Let's set up Immich to be accessible from the world wide web!

Prerequisites

If you haven't already installed nginx or certbot, please follow my documentation before continuing to get those installed.

Set up an nginx server block

Let's configure a server block for photos.mydomain.com. Of course, you can replace mydomain with a domain that you own.

Let's make sure we're in the nginx site-available directory

cd /etc/nginx/sites-available

Create a server block for photos.<mydomain>.com.

sudo nano photos.mydomain.com

Paste the following configuration

server {
    listen 80;
    listen [::]:80;

    # replace with your domain or subdomain
    server_name photos.mydomain.com

    # https://github.com/immich-app/immich/blob/main/nginx/templates/default.conf.template#L28
    client_max_body_size 50000M;

    location / {
        proxy_pass http://localhost:2283;
        proxy_set_header Host              $http_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;

        # http://nginx.org/en/docs/http/websocket.html
        proxy_http_version 1.1;
        proxy_set_header   Upgrade    $http_upgrade;
        proxy_set_header   Connection "upgrade";
        proxy_redirect off;
    }
}

Replace the domain

Just remember to replace photos.mydomain.com with the actual domain you want routed to Immich.

Save this file.

Enable the site.

sudo ln -s /etc/nginx/sites-available/photos.mydomain.com /etc/nginx/sites-enabled/

Let's just test our nginx config real quick to make sure there were no syntax issues.

sudo nginx -t

Configure DNS Routing

Before we set up certbot, we need to configure routing for the domain or subdomain that we want to route to our home server.

For AWS users, simply go to Hosted Zones, choose the domain that you want to create a record for, and hit Create Record. In our example here, were are using a subdomain photos. The record will be a CNAME, which allows use to forward traffic to the DDNS we created with No-IP. Hit save. Now we have a CNAME routing for photos.mydomain.com which will forward traffic to our DDNS aka our home server.

Configure SSL (HTTPS)

Let's install our SSL certificates so that we make sure our site always uses HTTPS.

sudo certbot --nginx -d photos.mydomain.com

We should get a Congratulations! message from certbot.

Let's test our configuration once again.

sudo nginx -t

And now restart the nginx service.

sudo systemctl restart nginx

Testing

Excellent! We can now hit our Immich server from the world wide web!

Immich Https

Immich Behind a CloudFlare Tunnel with Google OAuth

Step 1: Set up Google as an Auth Provider in CloudFlare

Go to Settings -> Authentication -> Login Methods

Hit the Add new button.

CloudFlare will provide instructions for setting up Google as an identity provider. Follow these instructions to create an App ID and Client secret. You will enter these values into the Google identity provider App ID and Client secret.

Step 2: Set up a public hostname for your tunnel.

Navigate to your tunnel in Networks -> Tunnels. Click on your tunnel and hit the Edit button.

Click on Public Hostname at the top. Add the desired subdomain and the local IP:port that the Immich app is running on. You can now access this via the hostname URL in the browser.

Step 3: Set up a CloudFlare SaaS Application

Go to Access -> Applications.

Hit Add an application. Choose SaaS.

Give the application a name, i.e. Immich.

The logo can be this URL.

https://raw.githubusercontent.com/immich-app/immich/main/design/immich-logo.png

The scopes should be openid, email, profile.

The redirect URLs should be

http://local_IP:2283/auth/login
http://local_IP:2283/user-settings
https://immich.yourdomain.com/auth/login
https://immich.yourdomain.com/user-settings
http://local_IP:2283/api/oauth/mobile-redirect
https://immich.yourdomain.com/api/oauth/mobile-redirect

Note the Client ID, Client Secrent, and the Issuer URL. Copy these as we'll need them for the Immich Admin configuration.

Create a Policy called Immich Access Policy.

Action is Allow.

In the Configure Rules, set the Selector to Emails. Add any emails that should have the ability to sign into the application.

Save the policy.

For Authentication, set your Auth provider i.e. Google.

In Immich go to Administration -> Settings -> OAuth.

Paste in the Issuer URL, Client ID, and Client Secret.

Scroll down to the Mobile Redirect URI Override. You need to toggle this on because CloudFlare does not allow us to use the app.immich:; URL. Set this to be https://immich.yourdomain.com/api/oauth/mobile-redirect.

Verify that you can sign in correctly on both web and mobile.

Deactivate password sign in.

Congrats, you are now tunneling into your Immich instance using CloudFlare tunnel, and authenticating using OAuth with Google with CloudFlare as the issuer.

Keep in mind that CloudFlare Tunnel does have a file size upload limit of 100 MB. Any large videos you have WILL NOT SYNC. The only way you will sync these large videos would be to connect through a VPN.

Backup

Immich is backed up by the restic → OneDrive layer described on Backups. Media originals, uploads, and Immich's own daily DB dumps; thumbs and encoded-video excluded as regenerable caches. Daily at 04:00 UTC, after the 02:00 UTC DB dump.

Why two engines when one would do

This comes up constantly, so here is the reasoning. Kopia and restic are deliberately different engines because they do different jobs, and sharing one engine between the on-site and off-site copy means a single engine bug or corruption takes out both.

Kopia is the LAN copy on the external drive: deduplicated, browsable, fast to restore a single file. You can walk the snapshot tree and pull one photo without restoring anything else.

restic is the off-site copy on OneDrive. Because the repo lives on OneDrive rather than on the VM, a brand-new machine can restore everything. restic streams — it fetches only the packs it needs and decrypts in RAM, so a restore does not need twice the disk. A fresh machine needs three things: the backup repo, the password from the password manager, and OneDrive access. Roughly 3 hours for ~332 GiB at ~30 MB/s.

Two engines, two jobs, two failure domains.

Run the restore test first

After the first full backup, run the end-to-end test — restic check plus a subset restore of the DB dumps plus sha256 verify against the live originals. It's cheap, no 337 GB restore, and it proves the chain works before you actually need it.

Repo: Wildium/immich-backup (private, config and scripts only, secrets gitignored).

Deployment options

Docker compose is the canonical path above, but Immich runs fine through several other routes:

The standard install — docker-compose.yml + .env as described above. Most flexible, works with the reverse proxy, and is what I run.

Install the Immich Distribution snap; it runs on port 80 by default. Persisted data lives in /var/snap/immich-distribution/common (the pgsql and upload folders). Change the port with:

sudo snap set immich haproxy-http-bind="*:2283"
sudo snap restart immich-distribution

Both home-OS platforms have one-click Immich app stores — see the CasaOS and Umbrel walkthroughs. Convenient for a dedicated NAS box, but you give up compose-level control.

Frameo (digital frames)

Gift a digital frame without handing photos to a third-party cloud: Frameo pulls from your own Immich server via ImmichFrame.

  1. Set up the Frameo normally, skipping its image-upload step.
  2. Settings → About → enable the beta program and ADB access; accept the computer's fingerprint prompt (a USB-A→USB-C cable worked where C-to-C didn't).
  3. Confirm the device: adb devices should show <id> device — if unauthorized, toggle the ADB access off and on.
  4. Install ImmichFrame per its docs and point it at your Immich instance.

Full walkthrough: Frameo with ImmichFrame.

See also

Comments