Skip to content

Deploy to Hetzner Cloud

Hetzner Cloud is the cheapest credible EU-sovereign option. Data centres: Falkenstein (DE), Nuremberg (DE), Helsinki (FI). GDPR- friendly by default; ISO 27001 certified.

OSS v0.1 is not one-click

The instructions below are a manual flow. Commercial edition ships a tested Terraform module that does all of this in one terraform apply. See strategy/04_ONE_CLICK_EU_DEPLOY.md for the commercial design.

Step 1 — provision a server

Use the Hetzner Cloud Console or the hcloud CLI.

Recommended:

  • Image: Ubuntu 24.04 LTS
  • Type: CCX13 (dedicated vCPU, 2 vCPU, 8 GB RAM, 80 GB NVMe) — €14.39/mo, plenty for a single-tenant demo or small production.
  • Location: Falkenstein or Helsinki (whichever is closer to your users).
  • Firewall: allow 22 (from your IP), 80, 443.

With hcloud:

hcloud server create \
  --type ccx13 \
  --image ubuntu-24.04 \
  --name lex-custis-prod \
  --location fsn1 \
  --ssh-key your-ssh-key-name

Takes ~30 seconds.

Step 2 — bootstrap the server

SSH in:

ssh root@<public-ip>

Install Docker + Compose v2:

curl -fsSL https://get.docker.com | sh
apt-get install -y docker-compose-plugin git python3 curl

Create a non-root user:

adduser --gecos "" --disabled-password lexcustis
usermod -aG docker lexcustis
su - lexcustis

Step 3 — clone + install

git clone https://github.com/vbalagovic/lex-custis.git lex-custis
cd lex-custis
./install.sh                    # Mistral path
# or
./install.sh --with-ollama      # self-hosted, requires a GPU for 7B+ models

Takes ~10 minutes. When it's done, the stack is running on localhost:3000 (frontend) and localhost:8081 (backend). You still need to put TLS in front.

Step 4 — TLS with Caddy

The repo includes a Caddyfile that auto-provisions Let's Encrypt certs when $DOMAIN is set. Add to your .env:

DOMAIN=lexcustis.yourdomain.eu

Update DNS so lexcustis.yourdomain.eu points at your Hetzner server's public IP.

Run Caddy:

docker run -d --name caddy --restart unless-stopped \
  --network lex-custis_default \
  -p 80:80 -p 443:443 \
  -v ./Caddyfile:/etc/caddy/Caddyfile:ro \
  -v caddy_data:/data \
  caddy:2-alpine

First request on :443 triggers the cert provisioning. docker logs caddy -f to watch.

Step 5 — firewall

In the Hetzner Cloud Console, attach a firewall that allows:

  • 22/tcp from your SSH source IPs only.
  • 80/tcp from the world (for HTTP-01 challenge).
  • 443/tcp from the world.
  • Outbound: everything (Docker pulls, Mistral API calls, etc.).

Step 6 — backups

Install the Hetzner Storage Box or use a Hetzner Object Storage bucket in the same region:

# Nightly at 03:00 Europe/Berlin
0 3 * * * cd /home/lexcustis/lex-custis && docker compose exec -T postgres pg_dump -U lexcustis lexcustis | gzip | sshfs ...

Commercial edition automates this + Qdrant snapshots.

Step 7 — monitoring

Minimum: UptimeRobot on https://lexcustis.yourdomain.eu/health. If the check fails for 3 consecutive 60-second windows, alert a pager.

Hetzner-specific tips

  • Use the private network feature if you expand to multi-server: backend on one, DB on another.
  • The Load Balancer product can terminate TLS and forward to N backend servers — useful for >100 concurrent users.
  • Hetzner's Snapshots feature ≠ Postgres backups — it's VM-level. Cheap, useful for rollback, but still do app-level backups.
  • Backups feature in Hetzner Cloud Console does daily VM backups automatically for +20% of server cost. Turn it on for belt-and- braces.

Common pitfalls

  • GPU models. Hetzner has dedicated GPU line (GEX44, €430/mo) if you want to self-host 13B+ Ollama models. For 7B and smaller, a CCX33 CPU box is fine.
  • Inbound rate limit. Hetzner rate-limits inbound bandwidth at 1 Gbps — irrelevant for a chat app, worth knowing for bulk-import flows.
  • Falkenstein is the most popular DC; Helsinki has more headroom. If you see slot unavailability at provisioning time, try Helsinki.

When to graduate off Hetzner

At ~200 concurrent users you start wanting:

  • Dedicated Postgres (RDS-style managed)
  • Dedicated Qdrant cluster
  • Multiple backend replicas with a load balancer

At that point either scale up on Hetzner Cloud (bigger dedicated instances, managed Postgres) or move to Scaleway/OVH managed services — or contact us for the commercial edition where we operate the whole stack for you on EU-sovereign infra.