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.