Recently, I decided to give the self-hosted photo and video solution Immich a whirl and must say, it’s pretty sweet. It kinda feels like hosting your own Google Photos instance given how similar the UIs are. And having used Google Photos for years, I’m not complaining! Anywho, there doesn’t seem to be too much info out there on Immich and Podman specifically, so thought I’d share my approach.
Approach
As I have done for most of my container infrastructure at this point, I wrote an Ansible playbook to deploy Immich via Podman. Ansible takes care of creating all of the various systemd unit files that enable me to keep things running smoothly post-deployment (and quickly deploy elsewhere if needed). I really dig it. Once the playbook - which I will share below - is executed, four containers are created and tied together via a pod:
container-immichdb.service <-- Postgres Container
container-immichml.service <-- Immich Machine Learning Container
container-immichredis.service <-- Redis Container
container-immichserver.service <-- Immich Server Container
pod-immich.service <-- Immich Pod
The Playbook
Let’s break it down into sections..
Create Immich Pod:
---
- hosts: ""
tasks:
- name: Create Immich Pod
containers.podman.podman_pod:
name: immich
hostname: immich.example.com
state: created
Create Postgres Container:
Pay attention to the container name, volume paths and Postgres variables, changing them to suit your needs. There also may be no need to wait five seconds for db init, but I put it there just in case.
- name: Run Postgres container
containers.podman.podman_container:
name: immichdb
pod: immich
image: registry.hub.docker.com/tensorchord/pgvecto-rs:pg16-v0.2.1
rm: true
state: created
volume:
- '/containers/immich/postgres:/var/lib/postgresql/data:z'
- '/etc/localtime:/etc/localtime:ro'
env:
{
'POSTGRES_USER': 'immich',
'POSTGRES_PASSWORD': 'immich123!',
'POSTGRES_DB': 'immich',
'POSTGRES_INITDB_ARGS': '--data-checksums'
}
- name: Wait for DB initialization
ansible.builtin.pause:
seconds: 5
Create Redis container:
Change the name as you please. Same detail on waiting five seconds - there’s likely no real need, but meh.
- name: Run Redis Container
containers.podman.podman_container:
name: immichredis
pod: immich
image: registry.hub.docker.com/library/redis:6.2-alpine@sha256:51d6c56749a4243096327e3fb964a48ed92254357108449cb6e23999c37773c5
rm: true
state: created
- name: Give Redis a moment
ansible.builtin.pause:
seconds: 5
Create Immich container:
I’m choosing to run the release
tag here and leveraging Podman’s auto-update ability (via the io.containers.autoupdate
label seen below). This label should keep me at the latest stable, but be aware that the Immich team calls out the fact that the app is under heavy development and updates could break things. So be sure to take regular backups. Once again, change any environment-specific stuff to suit your needs. Lastly, note the Traefik labels: Modify or remove per your needs. If you are not using Traefik, note that Immich server runs by default on port 3001. Either use that with your reverse proxy of choice or expose it via the pod for direct access.
- name: Run Immich Server Container
containers.podman.podman_container:
name: immichserver
pod: immich
image: ghcr.io/immich-app/immich-server:release
rm: false
state: created
volume:
- '/containers/immich/data:/usr/src/app/upload'
- '/etc/localtime:/etc/localtime:ro'
env:
{
'DB_HOSTNAME': 'localhost',
'DB_USERNAME': 'immich',
'DB_PASSWORD': 'immich123!',
'DB_DATABASE_NAME': 'immich',
'REDIS_HOSTNAME': 'localhost'
}
label:
{
'traefik.enable': 'true',
'traefik.http.routers.immich.rule': 'Host(`immich.example.com`)',
'traefik.http.middlewares.immich-https-redirect.redirectscheme.scheme': 'https',
'traefik.http.routers.immich.middlewares': 'immich-https-redirect',
'traefik.http.routers.immich-secure.entrypoints': 'websecure',
'traefik.http.routers.immich-secure.rule': 'Host(`immich.example.com`)',
'traefik.http.routers.immich-secure.tls': 'true',
'traefik.http.routers.immich-secure.tls.certresolver': 'le',
'traefik.http.services.immich.loadbalancer.server.port': '3001',
'io.containers.autoupdate': 'registry'
}
Create the Immich ML container:
Change name/volume as needed.
- name: Run Immich ML Container
containers.podman.podman_container:
name: immichml
pod: immich
image: ghcr.io/immich-app/immich-machine-learning:release
rm: false
state: created
volume:
- '/containers/immich/model-cache:/cache'
Create systemd Unit Files:
I have a few mounts I require for these services to start. Remove or change as needed.
- name: Create systemd unit file for immich pod and containers
containers.podman.podman_generate_systemd:
name: immich
new: true
requires:
- containers.mount
- containers-immich-data.mount
restart_policy: always
no_header: true
dest: /etc/systemd/system
Start the pod:
- name: Ensure immich pod is started and enabled
ansible.builtin.systemd:
name: pod-immich
daemon_reload: true
state: started
enabled: true
Run the playbook, being sure to specify your host to execute on via the host_var
variable and that should do it. Here’s my immich.yml file if desired. I hope this helps someone get started :)
As for importing photos from Google, I recommend using Google Takeout to pull your photo archive down and Immich-Go to upload them into your Immich server. It rocks! For things outside of Google, also note that Immich has a cli tool that allows you to bulk upload as well.
Cheers!