Hosting Immich Locally with Docker and Caddy

In this post, I document the steps I followed to set up Immich – a self-hosted photo and video backup solution – on my local server using Docker and Caddy. I also configured it to be accessible over HTTPS with a custom subdomain.

🔧 Requirements

  • Docker and Docker Compose installed
  • A registered domain (e.g. wallaceat.me)
  • Caddy running in a container for reverse proxy and HTTPS

📁 Directory Structure

/home/wallace/server/immich
├── docker-compose.yml
├── stack.env
└── ...

📝 docker-compose.yml

Here’s the Docker Compose file I used:

version: "3.8"
services:
  immich-server:
    container_name: immich_server
    image: ghcr.io/immich-app/immich-server:release
    volumes:
      - /mnt/immich:/usr/src/app/upload:rw
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - stack.env
    ports:
      - '2283:2283'
    depends_on:
      - redis
      - database
    restart: always
    networks:
      - immich-network

  immich-machine-learning:
    image: ghcr.io/immich-app/immich-machine-learning:release
    volumes:
      - model-cache:/cache
    env_file:
      - stack.env
    restart: always
    networks:
      - immich-network

  redis:
    image: redis:6.2-alpine
    restart: always
    networks:
      - local

  database:
    image: tensorchord/pgvecto-rs:pg14-v0.2.0
    environment:
      POSTGRES_PASSWORD: your_password
      POSTGRES_USER: your_user
      POSTGRES_DB: your_db
    volumes:
      - /mnt/postgres:/var/lib/postgresql/data
    restart: always
    networks:
      - local

volumes:
  model-cache:

networks:
  local

🌐 Caddy Configuration

My Caddy server is already running in a separate container and serves multiple services. Here’s the config snippet I added to the Caddyfile to expose Immich:

photos.wallaceat.me {
  reverse_proxy immich_server:2283
}

If using an external Caddy container, ensure it’s in the same Docker network or resolve to the host’s IP and port.

🌍 Domain and HTTPS

I pointed the subdomain subdomain.wallaceat.me to my server’s IP using an A record on my DNS provider (Dynu). Caddy automatically issued a Let’s Encrypt certificate.

🛠 Common Issues

  • Port conflicts: Ensure no other services are using port 80 or 443
  • Redis timeout: Check if the Redis container is running and available on the Docker network
  • Permissions: Mount points like /mnt/immich must be accessible by Docker

📌 Tips

  • Automate DNS updates using Dynu’s dynamic IP update client if your IP is dynamic
  • Consider automating deployment via GitHub Actions or Drone CI in the future

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *