12 KiB
Immich Role
This Ansible role deploys Immich - a high performance self-hosted photo and video management solution - using Podman with docker-compose files.
Requirements
- Podman installed on the target system (handled by the
podmanrole dependency) - Podman compose support (
podman composecommand available) - Sufficient disk space for photos/videos at the upload location
Role Variables
See defaults/main.yml for all available variables and their default values.
Key Configuration Requirements
Required Passwords
Both passwords must be set in your inventory (min 12 characters):
immich_postgres_password- PostgreSQL database passwordimmich_valkey_password- Valkey/Redis password
Valkey ACL Configuration
Important: Immich requires a dedicated Valkey ACL user with specific permissions. This role provides the ACL configuration, but you must register it with the Valkey role.
Required Setup in Inventory:
Add the Immich user to your valkey_acl_users list in your inventory or host_vars:
# inventory/host_vars/yourserver.yml or group_vars/all.yml
valkey_acl_users:
- username: immich
password: "{{ immich_valkey_password }}"
keypattern: "immich_bull* immich_channel*"
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
ACL Breakdown:
keypattern: "immich_bull* immich_channel*"- Restricts access to BullMQ keys used by Immich&*- Allow all pub/sub channels (required for BullMQ job queues)-@dangerous- Deny dangerous commands (FLUSHDB, FLUSHALL, KEYS, etc.)+@read +@write- Allow read/write command groups+@pubsub- Allow pub/sub commands (SUBSCRIBE, PUBLISH, etc.)+select- Allow SELECT command (database switching)+auth +ping +info- Connection management commands+eval +evalsha- Lua scripting (required by BullMQ for atomic operations)
Based on: Immich GitHub Discussion #19727
Security Benefits:
- Immich cannot access keys from other services
- Cannot execute admin commands (FLUSHDB, CONFIG, etc.)
- Cannot view all keys (KEYS command denied)
- Defense-in-depth with ACL + key patterns + database numbers
External Network Configuration
Immich requires a dedicated external network to be defined in your inventory. Add this to your host_vars or group_vars:
podman_external_networks:
- name: immich
subnet: 172.20.0.0/16
gateway: 172.20.0.1
How it works:
- Define the Immich network in
podman_external_networkslist in your inventory - The
podmanrole (a dependency) creates the external network before Immich deployment - The Immich docker-compose file references this external network
- The network persists across container restarts and compose stack rebuilds
Dependencies
This role depends on:
podman- Container runtimepostgres- PostgreSQL databasevalkey- Redis-compatible cache (formerly Redis)
Note: The Valkey role must be configured with the Immich ACL user (see Valkey Configuration section above) before running this role.
Example Playbook
---
- hosts: servers
become: true
roles:
- role: podman
- role: immich
vars:
immich_postgres_password: "your-secure-postgres-password"
immich_valkey_password: "your-secure-valkey-password"
immich_upload_location: /mnt/storage/immich/upload
immich_timezone: America/New_York
Complete Example with Valkey ACL:
In inventory/host_vars/yourserver.yml:
# Podman external networks
podman_external_networks:
- name: immich
subnet: 172.20.0.0/16
gateway: 172.20.0.1
# Valkey admin password
valkey_admin_password: "your-valkey-admin-password"
# Valkey ACL users - register all service users here
valkey_acl_users:
- username: immich
password: "{{ immich_valkey_password }}"
keypattern: "immich_bull* immich_channel*"
commands: "&* -@dangerous +@read +@write +@pubsub +select +auth +ping +info +eval +evalsha"
# Add other services here as needed
# Immich passwords
immich_postgres_password: "your-secure-postgres-password"
immich_valkey_password: "your-secure-valkey-password"
In your playbook:
---
- hosts: servers
become: true
roles:
- role: valkey # Must run first to create ACL users
- role: postgres
- role: podman
- role: immich
Architecture
The role deploys Immich using Podman containers that connect to shared system services:
Immich Containers:
- immich-server - Main application server (exposed on configured port)
- immich-machine-learning - ML service for facial recognition and object detection
Shared System Services:
3. PostgreSQL - Database with vector extensions (from postgres role)
4. Valkey - Redis-compatible cache (from valkey role)
Container Networking
Both Immich containers run on a dedicated external Podman network with its own CIDR block. The network is created by the podman role as an external network, referenced in the compose file:
networks:
immich:
external: true
name: immich
The actual network configuration (subnet: 172.20.0.0/16, gateway: 172.20.0.1) is handled by the podman role based on the immich_network_* variables.
This provides:
- Network isolation: Separate subnet (defined in inventory, e.g.,
172.20.0.0/16) from other containers - Network persistence: Network survives compose stack rebuilds and container recreation
- Named bridge: Explicit interface naming for the network
- Container-to-container communication: The server reaches the ML container via service name (
immich-machine-learning:3003) using Docker/Podman internal DNS - Container-to-host communication: Both containers can reach PostgreSQL and Valkey on the host via
host.containers.internal:{{ podman_subnet_gateway }}
Key Points:
- The network must be defined in your inventory via
podman_external_networks - The network is created by the
podmanrole before Immich deployment (via role dependency) - The Immich network has its own gateway (e.g.,
172.20.0.1as defined in inventory) extra_hostsmapshost.containers.internalto the Podman default bridge gateway (e.g.,10.88.0.1), not the Immich network gateway- This allows containers to route to the host machine for PostgreSQL/Valkey access
Checking the network:
# List all Podman networks
podman network ls
# Inspect the Immich network
podman network inspect immich
Data Isolation
The role implements proper data isolation for both database backends:
- PostgreSQL: Immich gets its own database (
immich) and dedicated user (immich) with restricted privileges (NOSUPERUSER, NOCREATEDB, NOCREATEROLE) - Valkey: Immich uses a dedicated ACL user (
immich) with:- Dedicated password (independent from
valkey_admin_password) - Key pattern restriction (
immich_bull*andimmich_channel*only) - Command restrictions (no admin/dangerous operations like FLUSHDB, CONFIG)
- Database number isolation (uses DB 0 by default, configurable)
- Pub/sub channel access for BullMQ job queues
- Dedicated password (independent from
Security Benefits:
- Each service has unique credentials
- Compromised service cannot access other services' data
- Cannot accidentally delete all data (FLUSHDB/FLUSHALL denied)
- Cannot view keys from other services (KEYS command denied)
- Defense-in-depth: ACL + key patterns + command restrictions + database numbers
The compose file is deployed to {{ podman_projects_dir }}/immich/docker-compose.yml and managed via a systemd service.
Nginx Reverse Proxy with ACME/Let's Encrypt
The role includes an Nginx vhost template with native ACME support for automatic HTTPS certificate management.
Prerequisites:
- Nginx role deployed with
acme_emailconfigured - Port 80/443 accessible from internet (for ACME HTTP-01 challenge)
- DNS pointing to your server
Configuration:
# Enable Nginx reverse proxy
immich_nginx_enabled: true
immich_nginx_hostname: "blog.hello.com"
# In nginx role configuration (host_vars or group_vars)
acme_email: "admin@carabosse.cloud"
What it does:
- Deploys HTTPS vhost with automatic Let's Encrypt certificate
- HTTP → HTTPS redirect
- Proxies to Immich container on localhost
- Handles WebSocket upgrades for live photos
- Large file upload support (50GB max)
ACME automatic features:
- Certificate issuance on first deployment
- Automatic renewal
- HTTP-01 challenge handling
Post-Installation
After deployment:
- Access Immich at:
- With Nginx enabled:
https://{{ immich_nginx_hostname }} - Without Nginx:
http://<host-ip>:{{ immich_port }}
- With Nginx enabled:
- Create an admin account on first login
- Configure mobile/desktop apps to point to your server
Management
The role creates a systemd service for managing the compose stack:
# Check status
systemctl status immich
# Stop Immich
systemctl stop immich
# Start Immich
systemctl start immich
# Restart Immich
systemctl restart immich
# View logs for all containers
cd /opt/podman/immich && podman compose logs -f
# View logs for specific service
cd /opt/podman/immich && podman compose logs -f immich-server
Manual Management
You can also manage containers directly with podman compose:
cd /opt/podman/immich
# Start services
podman compose up -d
# Stop services
podman compose down
# Pull latest images
podman compose pull
# Recreate containers
podman compose up -d --force-recreate
Updating Immich
To update to a newer version:
- Update the
immich_versionvariable in your playbook or inventory - Re-run the Ansible playbook
- The systemd service will restart with the new version
Or manually:
cd /opt/podman/immich
podman compose pull
systemctl restart immich
Storage
- Upload location: Stores all photos, videos, and thumbnails
- Database location: PostgreSQL data (not suitable for network shares)
- Model cache: ML models for facial recognition
Ensure adequate disk space and regular backups of these directories.
Files Deployed
{{ podman_projects_dir }}/immich/docker-compose.yml- Compose definition/etc/systemd/system/immich.service- Systemd service unit
Security Considerations
- Set strong passwords for both
immich_postgres_passwordandimmich_valkey_password(min 12 chars) - Use Ansible Vault to encrypt passwords in production:
ansible-vault encrypt_string 'your-password' --name 'immich_postgres_password' ansible-vault encrypt_string 'your-password' --name 'immich_valkey_password' - Configure Valkey ACL properly (see Valkey Configuration section) - do not use
+@all - Consider using a reverse proxy (nginx/traefik) for HTTPS
- Restrict access via firewall rules if needed
- Keep Immich updated by changing
immich_versionand redeploying
Troubleshooting
Check service status
systemctl status immich
View compose file
cat /opt/podman/immich/docker-compose.yml
Check container status
cd /opt/podman/immich
podman compose ps
View logs
cd /opt/podman/immich
podman compose logs
Valkey ACL Issues
Error: "NOPERM No permissions to access a channel"
- The Valkey ACL is missing channel permissions
- Ensure
&*or+allchannelsis in the ACL commands - Verify ACL is properly loaded:
valkey-cli ACL LIST
Error: "NOAUTH Authentication required"
- Check
immich_valkey_passwordis set correctly - Verify the password matches in both inventory ACL config and immich vars
Error: "WRONGPASS invalid username-password pair"
- Ensure the Immich user is registered in
valkey_acl_users - Check the Valkey ACL file was deployed:
cat /etc/valkey/users.acl - Restart Valkey to reload ACL:
systemctl restart valkey
Verify Valkey ACL Configuration:
# Connect as admin
valkey-cli
AUTH default <valkey_admin_password>
# List all ACL users
ACL LIST
# Check specific user
ACL GETUSER immich
# Monitor commands (useful for debugging permissions)
MONITOR
Test Immich user credentials:
valkey-cli
AUTH immich <immich_valkey_password>
SELECT 0
PING
# Should return PONG
# Try a restricted command (should fail)
FLUSHDB
# Should return: (error) NOPERM
License
MIT
Author Information
Created for deploying Immich on NAS systems using Podman and docker-compose.