Lumio / self-hosted
Guide

Install Lumio on Hetzner Cloud.

Step-by-step guide: from ordering the server to your first gallery. Hetzner is almost always the right choice for European studios — cheap, fast, data centers in Germany.

Step 1 — Order a server

In the Hetzner Cloud Console:

  • Location: Falkenstein or Nuremberg (for GDPR peace of mind)
  • Image: Ubuntu 22.04 LTS
  • Type: CX22 (4 vCPU / 8 GB / 40 GB) for ~€5/month — enough for a solo studio
  • Add an SSH key (upload your local public key)
  • Firewall: none for now — we configure ufw directly on the server

Step 2 — Secure the server

Log in via SSH (ssh root@YOUR_IP), then:

# Update the system
apt update && apt upgrade -y

# Create a non-root user
adduser lumio
usermod -aG sudo lumio
mkdir -p /home/lumio/.ssh
cp ~/.ssh/authorized_keys /home/lumio/.ssh/
chown -R lumio:lumio /home/lumio/.ssh
chmod 700 /home/lumio/.ssh
chmod 600 /home/lumio/.ssh/authorized_keys

# Harden SSH (password login off, root login off)
sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication no/' /etc/ssh/sshd_config
systemctl restart sshd

# Firewall
ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw --force enable

# fail2ban
apt install -y fail2ban
systemctl enable --now fail2ban

From now on, log in only as the lumio user.

Step 3 — Install Docker

# Docker Engine + Compose plugin
curl -fsSL https://get.docker.com | sh
sudo usermod -aG docker $USER
# Log in again so the group takes effect
exit
ssh lumio@YOUR_IP

# Test
docker run --rm hello-world

Step 4 — Set up DNS

At your domain registrar create two A records — one for your main domain and a wildcard for subdomains (tenant galleries):

photos.yourstudio.com      A    YOUR_HETZNER_IP
*.photos.yourstudio.com    A    YOUR_HETZNER_IP

Wait about 5–30 minutes for the DNS change to propagate. Test: dig photos.yourstudio.com.

Step 5 — Deploy Lumio

cd /opt
sudo mkdir lumio && sudo chown lumio:lumio lumio
cd lumio

git clone https://github.com/markusthiel/lumio.git .
cp .env.example .env

# Edit .env — the most important variables:
# BASE_DOMAIN=photos.yourstudio.com
# POSTGRES_PASSWORD=... (secure password)
# MINIO_ROOT_PASSWORD=... (secure password)
# JWT_SECRET=... (at least 64 random characters)
nano .env

# Start the stack
docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d

# First start takes 2-3 minutes, then:
docker compose ps      # all services running?
docker compose logs -f # follow logs

Step 6 — Create an admin and log in

docker compose exec api npm run create-admin \
  -- --email=you@studio.com --password=...

Studio login at https://photos.yourstudio.com. Caddy automatically obtains a Let's Encrypt certificate — on the first request this can take a few seconds.

Common questions about the Hetzner setup

Which Hetzner server is enough for a solo studio? +

A CX22 (4 vCPU, 8 GB RAM, 40 GB NVMe) for about €5/month is enough for a solo studio with moderate gallery volume. You book object storage separately — Hetzner Object Storage costs €5.99/month for 1 TB. So under €12 a month in total, with your own data sovereignty. For larger studios or video-heavy workflows, rather a CX32 or CCX13 with dedicated vCPUs.

Is Hetzner worth it versus AWS / GCP for photo galleries? +

For European studios almost always yes. Hetzner is 5–10× cheaper than AWS/GCP for comparable specs, data centers are in Germany (Falkenstein, Nuremberg) and Finland, no Schrems II drama. AWS/GCP have advantages if you're already stuck in that ecosystem or need specific managed services (Aurora, BigQuery) — both typically irrelevant for photo galleries.

How do I secure the server against attacks? +

Three points minimum: (1) SSH login by key only, password login disabled, fail2ban against brute force; (2) ufw firewall with only the necessary ports open (22, 80, 443); (3) automatic security updates via unattended-upgrades. In the Lumio setup everything is behind Caddy, which terminates HTTPS — direct external access to API/frontend isn't needed.

What does the setup really cost in ongoing operation? +

For a typical solo-studio setup: CX22 €5/month + 1 TB object storage €6/month + domain €1/month = about €12/month. Plus your effort for updates and monitoring (~30 min/month with a well-configured setup). Compared to Lumio Cloud Studio (€39/month) you save ~€27/month — over 12 months that's ~€300 a year. Whether that's worth it against your hourly rate is your call.

How do I back up the Postgres database? +

Simplest option: a daily pg_dump via cron, uploaded to a second object storage bucket (ideally at a different provider than the primary — e.g. primary on Hetzner, backup on Backblaze B2). More complex setups use pgbackrest or Barman for incremental backups and point-in-time recovery. A guide with concrete scripts follows in the docs.

Ready to get started?

Source code on Forgejo, issues and discussions there too. If anything in the guide is unclear, reach out — we're happy to improve the docs.