📸 Breaking up with Google: Installing Immich and ImmichFrame with Docker Compose

Continuing the Breaking Up with Google series, this guide shows you how to self-host your photos with Immich and display them using ImmichFrame — all managed through Docker Compose. No more relying on Google Photos: your media stays private and under your control.

We’ll walk through setting up the server, database, Redis, machine learning container, and the ImmichFrame dashboard, all using the following Docker Compose file. This does assume you have already set up traefik on the host and are using a network called "proxy". You'll need HTTPS for immichframe to talk to your nest hubs later.


🐳 Step 1: Your Docker Compose File

Here’s the full docker-compose.yml for Immich and ImmichFrame:

name: immich

services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION:-release}
    volumes:
      - ${UPLOAD_LOCATION}:/data
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    ports:
      - '2283:2283'
    networks:
      - proxy
    depends_on:
      - redis
      - database
    restart: always
    healthcheck:
      disable: false
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.middlewares.immich-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.immich-secure.entrypoints=https"
      - "traefik.http.routers.immich-secure.rule=Host(`<immich>`)"
      - "traefik.http.routers.immich-secure.service=immich"
      - "traefik.http.routers.immich-secure.tls=true"
      - "traefik.http.routers.immich.entrypoints=http"
      - "traefik.http.routers.immich.middlewares=immich-https-redirect"
      - "traefik.http.routers.immich.rule=Host(`<immich>`)"
      - "traefik.http.services.immich.loadbalancer.server.port=2283"

  immich-machine-learning:
    container_name: immich_machine_learning
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION:-release}
    volumes:
      - model-cache:/cache
    env_file:
      - .env
    restart: always
    healthcheck:
      disable: false
    networks:
      - proxy

  redis:
    container_name: immich_redis
    image: docker.io/valkey/valkey:8-bookworm@sha256:fea8b3e67b15729d4bb70589eb03367bab9ad1ee89c876f54327fc7c6e618571
    healthcheck:
      test: redis-cli ping || exit 1
    restart: always
    networks:
      - proxy

  database:
    container_name: immich_postgres
    image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0@sha256:41eacbe83eca995561fe43814fd4891e16e39632806253848efaf04d3c8a8b84
    environment:
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_USER: ${DB_USERNAME}
      POSTGRES_DB: ${DB_DATABASE_NAME}
      POSTGRES_INITDB_ARGS: '--data-checksums'
    volumes:
      - ${DB_DATA_LOCATION}:/var/lib/postgresql/data
    shm_size: 128mb
    restart: always
    networks:
      - proxy

  immichframe:
    container_name: immichframe
    image: ghcr.io/immichframe/immichframe:latest
    restart: on-failure
    volumes:
      - /opt/images/immich/immichframe-config:/app/Config
    ports:
      - "8080:8080"
    networks:
      - proxy
    environment:
      TZ: "Europe/London"
    labels:
      - "traefik.enable=true"
      - "traefik.docker.network=proxy"
      - "traefik.http.middlewares.immichframe-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.immichframe-secure.entrypoints=https"
      - "traefik.http.routers.immichframe-secure.rule=Host(`<immichframe>`)"
      - "traefik.http.routers.immichframe-secure.service=immichframe"
      - "traefik.http.routers.immichframe-secure.tls=true"
      - "traefik.http.routers.immichframe.entrypoints=http"
      - "traefik.http.routers.immichframe.middlewares=immichframe-https-redirect"
      - "traefik.http.routers.immichframe.rule=Host(`<immichframe>`)"
      - "traefik.http.services.immichframe.loadbalancer.server.port=8080"

volumes:
  model-cache:

networks:
  proxy:
    external: true

🔧 Step 2: Deploy Immich and ImmichFrame

  1. Save this docker-compose.yml to a folder, e.g., /opt/immich
  2. Create a .env file with the following variables:
UPLOAD_LOCATION=/opt/images/immich/uploads
DB_PASSWORD=your_db_password
DB_USERNAME=immich
DB_DATABASE_NAME=immich_db
DB_DATA_LOCATION=/opt/images/immich/db
IMMICH_VERSION=release
  1. Create a file called Settings.yaml in your Immichframe configuration directory (mine is: /opt/images/immich/immichframe-config) and paste the following inside it.
General:
  PhotoDateFormat: 'dd/MM/yyyy'  # string
  ImageLocationFormat: 'City,State,Country'
  # Imperial or metric system (Fahrenheit or Celsius)
  UnitSystem: 'metric'  # 'imperial' | 'metric'
  # 2 digit ISO code, sets the language of the weather description.
  Language: 'en'  # string
  # Image interval in seconds. How long an image is displayed in the frame.
  Interval: 45
  # Duration in seconds.
  TransitionDuration: 2  # float
  # Displays the current time.
  ShowClock: true  # boolean
  # Time format
  ClockFormat: 'hh:mm'  # string
  # Date format for the clock
  ClockDateFormat: 'eee, MMM d' # string
  # Allow two portrait images to be displayed next to each other
  Layout: 'splitview'  # single | splitview

# multiple accounts permitted
Accounts:
  - # The URL of your Immich server e.g. `http://photos.yourdomain.com` / `http://192.168.0.100:2283`.
    ImmichServerUrl: 'http://immich-server:2283'  # string, required, no default
    # Read more about how to obtain an Immich API key: https://immich.app/docs/features/command-line-interface#obtain-the-api-key
    ApiKey: 'KatpLGHvFLorrYsTNp3yQNvl4YuY37alla1rN61d8'  # string, required, no default
    ## UUID of album(s) - e.g. ['00000000-0000-0000-0000-000000000001']
    #Albums:  # string[]
    #  - 73aef035-490f-4a8f-9162-08aceba6ba35
    ## UUID of excluded album(s)
    #ExcludedAlbums:  # string[]
    #  - UUID
    ## UUID of People
    People:  # string[]
    ##  - UUID
  1. Start the containers:

docker-compose up -d

  1. Confirm they’re running:

docker ps

You should see all five containers: immich_server, immich_machine_learning, immich_redis, immich_postgres, and immichframe.


🌐 Step 3: Access Immich and ImmichFrame

  • Immich Dashboard: https://<immich>
  • ImmichFrame Display: https://<immichframe>. These assume your Traefik reverse proxy is running and configured to use the external proxy network.

✅ Step 4: Optional Enhancements

  • Enable GPU acceleration in the ML container (immich-machine-learning)
  • Backup PostgreSQL and media volumes regularly
  • Integrate ImmichFrame with Home Assistant for automated photo displays
  • Adjust Traefik labels for custom domains or HTTPS certificates

🔒 Why Self-Hosting Matters

  • Photos stay on your own server
  • Full privacy and control
  • Avoid Google Photos entirely
  • Seamlessly integrate into your smart home setup

✨ Next in the Breaking Up with Google Series

Learn how to automate ImmichFrame on Nest Hubs using Home Assistant - turning your smart displays into a private, automated photo frame.


About the author

Tim Wilkes is a UK-based security architect with over 15 years of experience in electronics, Linux, and Unix systems administration. Since 2021, he's been designing secure systems for a telecom company while indulging his passions for programming, automation, and 3D printing. Tim shares his projects, tinkering adventures, and tech insights here - partly as a personal log, and partly in the hopes that others will find them useful.

Want to connect or follow along?

LinkedIn: [phpsytems]
Twitter / X: [@timmehwimmy]
Mastodon: [@timmehwimmy@infosec.exchange]


If you've found a post helpful, consider supporting the blog - it's a part-time passion that your support helps keep alive.

⚠️ Disclaimer

This post may contain affiliate links. If you choose to purchase through them, I may earn a small commission at no extra cost to you. I only recommend items and services I’ve personally read or used and found valuable.

As an Amazon Associate I earn from qualifying purchases.