Leafcloud immich setup
Table of Contents
- ๐ Key Considerations Before You Start
- โ๏ธ Goal: Provision a Media Host on Leafcloud
- โ Prerequisites
- ๐ Terraform Structure
- ๐งฐ Set Up Immich
- ๐ Add HTTPS + Domain (dedyn.io)
๐ Key Considerations Before You Start
- An S3-compatible backend would have been ideal, but at this time there is no official Immich release that supports S3-compatible storage natively. Community workarounds exist but are fragile and not recommended for production.
- Youโre working with a single VM: keep services isolated but simple. Use Docker Compose. No need for Kubernetes.
- Youโre self-hosting for your family: prioritize reliability, recoverability, and ease of use.
- You are using a custom domain (e.g.
media.mangomystic.dedyn.io
) and want HTTPS enabled.
Check Your Resource Quotas First
Leafcloud applies default resource quotas that may not fit your needs (e.g., 2 vCPUs, 12GB RAM, 40GB volume limit). Before planning your infrastructure:- Request a quota increase via email or support.
- Use the OpenStack CLI to check your current limits:
openstack quota show <project_id> --volume
- You can also inspect compute limits like this:
openstack quota show <project_id> --compute
DNS Propagation Check
Once you point your domain (e.g.media.example.dedyn.io
) to your Leafcloud VM, check if DNS changes have propagated globally using:- https://www.whatsmydns.net/
Avoid Extending Storage with a Separate Volume After Immich Install
This led to numerous headaches. Immich does not gracefully recognize additional mounted volumes forUPLOAD_LOCATION
. Instead:- Define the correct storage size as root volume during Terraform provisioning.
- Set
UPLOAD_LOCATION
from the beginning.
Be Mindful of PostgreSQL Version Compatibility
Immichโs machine learning features rely on thepgvecto.rs
extension.- This requires a compatible Postgres version.
- We used Postgres 15 successfully.
- Always verify whatโs currently supported: https://github.com/immich-app/immich/discussions
Docker Images Will Take Up Space
Immichโs containers (server, ML, Postgres, Redis) together take 6โ8GB on your disk. Plan your VM root volume accordingly.Localhost in Caddy Reverse Proxy
Even though your domain points to the public IP (e.g.,45.135.59.126:2283
), Caddy runs on the same VM and should proxy tolocalhost:2283
. Thatโs because inside the VM, Caddy only needs to reach the local Immich containerโno need to route via external IP.
โ๏ธ Goal: Provision a Media Host on Leafcloud
- VM: 2 vCPUs, 8GB RAM, 200GB root volume
- OS: Ubuntu 22.04
- App: Immich self-hosted photo/video server
- Infra: Deployed via Terraform on Leafcloud (OpenStack backend)
โ Prerequisites
- Terraform v1.1+
- OpenStack project credentials (create.leaf.cloud)
- SSH key pair created and uploaded to Leafcloud
- DNS domain from https://desec.io
๐ Terraform Structure
leafcloud-terraform/
โโโ main.tf
โโโ provider.tf
โโโ leafcloud.auto.tfvars
โโโ .ssh/immich-key (your private key)
๐ง provider.tf
terraform {
required_providers {
openstack = {
source = "terraform-provider-openstack/openstack"
version = ">= 1.0.0"
}
}
}
provider "openstack" {
auth_url = "https://identity.leaf.cloud/v3"
user_name = "your@email.com"
password = "your_password"
tenant_name = "your_project"
domain_name = "Default"
region = "RegionOne"
}
๐ฆ main.tf
resource "openstack_compute_instance_v2" "media_host" {
name = "media-host"
image_name = "Ubuntu-22.04.20240522"
flavor_name = "en1.large"
key_pair = "immich-key"
security_groups = ["default"]
block_device {
uuid = "<image_id>"
source_type = "image"
destination_type = "volume"
volume_size = 200
boot_index = 0
delete_on_termination = true
}
network {
name = "external"
}
}
๐ leafcloud.auto.tfvars
auth_url = "https://identity.leaf.cloud/v3"
tenant_name = "your_project"
user_name = "your@email.com"
password = "your_password"
region = "RegionOne"
domain_name = "Default"
๐ Deploy
terraform init
terraform plan -out=tfplan
terraform apply "tfplan"
After apply:
openstack server show media-host -f value -c addresses
๐งฐ Set Up Immich
ssh -i ~/.ssh/immich-key ubuntu@<external_ip>
sudo apt update && sudo apt upgrade -y
sudo apt install -y docker.io docker-compose
sudo usermod -aG docker $USER
newgrp docker
mkdir ~/immich && cd ~/immich
docker-compose.yml
services:
immich-server:
container_name: immich_server
image: ghcr.io/immich-app/immich-server:release
restart: always
env_file:
- .env
ports:
- "2283:2283"
depends_on:
- database
- redis
volumes:
- /mnt/immich-storage/upload:/usr/src/app/upload
immich-microservices:
container_name: immich_microservices
image: ghcr.io/immich-app/immich-server:release
restart: always
env_file:
- .env
depends_on:
- database
- redis
volumes:
- /mnt/immich-storage/upload:/usr/src/app/upload
immich-machine-learning:
container_name: immich_machine_learning
image: ghcr.io/immich-app/immich-machine-learning:release
restart: always
redis:
image: redis:6.2
container_name: immich_redis
restart: always
database:
container_name: immich_postgres
image: tensorchord/pgvecto-rs:pg15-v0.2.0
restart: always
environment:
POSTGRES_PASSWORD: postgres
POSTGRES_USER: postgres
POSTGRES_DB: immich
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
.env
UPLOAD_LOCATION=/mnt/immich-storage/upload
DB_HOST=database
DB_PORT=5432
DB_USERNAME=postgres
DB_PASSWORD=postgres
DB_DATABASE_NAME=immich
REDIS_HOSTNAME=redis
๐ Add HTTPS + Domain (dedyn.io)
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install caddy
Edit /etc/caddy/Caddyfile
:
# Immich instance hosted on this VM
media.mangomystic.dedyn.io {
reverse_proxy localhost:2283
# Compress responses for faster loading
encode gzip
# Secure HTTP headers
header {
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" # Enforce HTTPS
X-Content-Type-Options "nosniff" # Prevent MIME type sniffing
X-Frame-Options "DENY" # Prevent clickjacking
Referrer-Policy "strict-origin-when-cross-origin" # Limit referrer info
}
}
sudo systemctl restart caddy
Optional: Lock Down Further (only if needed)
If your Immich is just for family:
- Allowlist IPs via Caddy (e.g., restrict to home IP)
- Add basic auth if extra protection is needed
- Use a firewall to block unused ports
But if your domain is private and not indexed publicly, and HTTPS is on โ this may be enough.
Final Checks
Run:
docker compose down
docker compose pull
docker compose up -d
Then visit:
๐ https://media.mangomystic.dedyn.io
๐ Youโre Done!
Self-hosted Immich on Leafcloud with HTTPS and infrastructure-as-code in place!