Skip to content

Immich in Docker Swarm

Immich is a promising self-hosted alternative to Google Photos. Its UI and features are clearly heavily inspired by Google Photos, and like Photoprism, Immich uses tensorflow-based machine learning to auto-tag your photos!

Pre-production warning

The developer makes it abundantly clear that Immich is under heavy development (although it's covered by "wife-insurance"1), features and APIs may change, and all your photos may be lost, or (worse) auto-shared with your 🐲 mother-in-law! Take due care 😉

Immich Screenshot

See my detailed review of Immich, as a Google Photos replacement, here

Immich requirements

Ingredients

Already deployed:

New:

  • DNS entry for your Immich instance, pointed to your keepalived IP

Setup data locations

First, we create a directory to hold the immich docker-compose configuration:

mkdir /var/data/config/immich

Then we setup directories to hold all the various data:

mkdir -p /var/data/immich/database-dump
mkdir -p /var/data/immich/upload
mkdir -p /var/data/runtime/immich/database

Setup Immich environment

Create /var/data/config/immich/immich.env something like the example below..

/var/data/config/immich/immich.env
###################################################################################
# Database
###################################################################################

# These are for the Immich components
DB_HOSTNAME=db
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE_NAME=immich

# These are specific to how the postgres image likes to receive its ENV vars
POSTGRES_PASSWORD=postgres
#POSTGRES_USER=postgres
POSTGRES_DB=immich

###################################################################################
# Redis
###################################################################################

REDIS_HOSTNAME=redis

# Optional Redis settings:
# REDIS_PORT=6379
# REDIS_DBINDEX=0
# REDIS_PASSWORD=
# REDIS_SOCKET=


###################################################################################
# JWT SECRET
###################################################################################

JWT_SECRET=randomstringthatissolongandpowerfulthatnoonecanguess # (1)!

###################################################################################
# MAPBOX
####################################################################################

# ENABLE_MAPBOX is either true of false -> if true, you have to provide MAPBOX_KEY
ENABLE_MAPBOX=false
MAPBOX_KEY=

###################################################################################
# WEB - Required
###################################################################################

# This is the URL of your vm/server where you host Immich, so that the web frontend
# know where can it make the request to.
# For example: If your server IP address is 10.1.11.50, the environment variable will
# be VITE_SERVER_ENDPOINT=http://10.1.11.50:2283/api
# !CAUTION! THERE IS NO FORWARD SLASH AT THE END

VITE_SERVER_ENDPOINT=https://immich.example.com/api


####################################################################################
# WEB - Optional
####################################################################################

# Custom message on the login page, should be written in HTML form.
# For example VITE_LOGIN_PAGE_MESSAGE="This is a demo instance of Immich.<br><br>Email: <i>demo@demo.de</i><br>Password: <i>demo</i>"

VITE_LOGIN_PAGE_MESSAGE=

NODE_ENV=production
  1. Yes, this has to be long. At least 20 characters.

Immich Docker Swarm config

Create a docker swarm config file in docker-compose syntax (v3), something like the example below.. example:

Fast-track with premix! 🚀

"Premix" is a git repository which includes necessary docker-compose and env files for all published recipes. This means that you can launch any recipe with just a git pull and a docker stack deploy 👍.

🚀 Update: Premix now includes an ansible playbook, enabling you to deploy an entire stack + recipes, with a single ansible command! (more here)

/var/data/config/immich/immich.yml
version: "3.2"

services:
  immich-server:
    image: altran1502/immich-server:release
    entrypoint: ["/bin/sh", "./start-server.sh"]
    volumes:
      - /var/data/immich/upload:/usr/src/app/upload
    env_file: /var/data/config/immich/immich.env
    networks:
      - internal

  immich-microservices:
    image: altran1502/immich-server:release
    entrypoint: ["/bin/sh", "./start-microservices.sh"]
    volumes:
      - /var/data/immich/upload:/usr/src/app/upload
    env_file: /var/data/config/immich/immich.env
    networks:
      - internal

  immich-machine-learning:
    image: altran1502/immich-machine-learning:release
    entrypoint: ["/bin/sh", "./entrypoint.sh"]
    volumes:
      - /var/data/immich/upload:/usr/src/app/upload
    env_file: /var/data/config/immich/immich.env
    networks:
      - internal

  immich-web:
    image: altran1502/immich-web:release
    entrypoint: ["/bin/sh", "./entrypoint.sh"]
    env_file: /var/data/config/immich/immich.env
    networks:
      - internal

  redis:
    image: redis:6.2
    networks:
      - internal

  db:
    image: postgres:14
    env_file: /var/data/config/immich/immich.env
    volumes:
      - /var/data/runtime/immich/database:/var/lib/postgresql/data
    networks:
      - internal

  db-backup:
    image: postgres:14
    env_file: /var/data/config/immich/immich-db-backup.env
    volumes:
      - /var/data/immich/database-dump:/dump
    entrypoint: |
      bash -c 'bash -s <<EOF
      trap "break;exit" SIGHUP SIGINT SIGTERM
      sleep 2m
      while /bin/true; do
        pg_dump -Fc > /dump/dump_\`date +%d-%m-%Y"_"%H_%M_%S\`.psql
        ls -tr /dump/dump_*.psql | head -n -"$$BACKUP_NUM_KEEP" | xargs -r rm
        sleep $$BACKUP_FREQUENCY
      done
      EOF'
    networks:
      - internal

  immich-proxy:
    container_name: immich_proxy
    image: altran1502/immich-proxy:release
    ports:
      - 2283:80
    deploy:
      replicas: 1
      labels:
        # traefik
        - traefik.enable=true
        - traefik.docker.network=traefik_public

        # traefikv1
        - traefik.frontend.rule=Host:immich.example.com
        - traefik.port=80

        # traefikv2
        - "traefik.http.routers.immich.rule=Host(`immich.example.com`)"
        - "traefik.http.routers.immich.entrypoints=https"
        - "traefik.http.services.immich.loadbalancer.server.port=80"        
    networks:
      - internal
      - traefik_public

networks:
  traefik_public:
    external: true
  internal:
    driver: overlay
    ipam:
      config:
        - subnet: 172.16.8.0/24

Note

Setup unique static subnets for every stack you deploy. This avoids IP/gateway conflicts which can otherwise occur when you're creating/removing stacks a lot. See my list here.

Launch Immich!

Launch the Immich stack by running

docker stack deploy immich -c /var/data/config/immich/immich.yml

Now hit the URL you defined in your config, and you should be prompted to create your first (admin) account, after which you can login (with the details you just created), and start admin-ing. Install a mobile app, connect using the same credentials, and start backing up all your photos!

Summary

What have we achieved? We have an HTTPS-protected endpoint to target with the native mobile apps, allowing us to backup photos from mobile devices and have them become searchable, shareable, and browseable via a beautiful, Google Photos-esque interface!

Summary

Created:

  • Photos can be synced from mobile device, or manually uploaded via web UI

Setup Immich in < 60s

Sponsors have access to a Premix playbook, which will set up Immich in under 60s (see below):

Chef's notes 📓


  1. "wife-insurance": When the developer's wife is a primary user of the platform, you can bet he'll be writing quality code! 👩 👨 🛏 😢 

  2. There's a friendly Discord server for Immich too! 

Tip your waiter (sponsor) 👏

Did you receive excellent service? Want to compliment the chef? (..and support development of current and future recipes!) Sponsor me on Github / Ko-Fi / Patreon, or see the contribute page for more (free or paid) ways to say thank you! 👏

Employ your chef (engage) 🤝

Is this too much of a geeky PITA? Do you just want results, stat? I do this for a living - I'm a full-time Kubernetes contractor, providing consulting and engineering expertise to businesses needing short-term, short-notice support in the cloud-native space, including AWS/Azure/GKE, Kubernetes, CI/CD and automation.

Learn more about working with me here.

Flirt with waiter (subscribe) 💌

Want to know now when this recipe gets updated, or when future recipes are added? Subscribe to the RSS feed, or leave your email address below, and we'll keep you updated.

Your comments? 💬