Skip to content
$_ setuptracking
Recipes

Plausible CE on Hetzner: 20-Minute Self-Host Setup with Caddy (€4.51/mo)

7 min read
Isometric illustration of laptop with code editor sitting on stack of servers

Goal: a fully working Plausible Community Edition install on a Hetzner Cloud €4.51/mo VPS, behind Caddy auto-HTTPS, in under 20 minutes. EU-hosted by default. Zero cookies. Real DNS hostname like analytics.your-site.com.

This is the cheapest serious self-host setup you can buy in 2026 — €4.51/mo gets you a CX22 Hetzner instance (2 vCPU / 4 GB RAM / 40 GB SSD / 20 TB traffic) in Falkenstein or Helsinki, which is plenty for any site under ~5M monthly events. Plausible CE itself is AGPL-3, so you own everything end-to-end.

What you’ll need before you start

  • A domain you control. We’ll use your-site.com in the examples — replace with yours throughout.
  • DNS access (where you can add an A record).
  • 5 minutes’ patience for DNS propagation.
  • An SSH client (Terminal on Mac/Linux, or PowerShell with OpenSSH on Windows).
  • A Hetzner account. hetzner.com/cloud — sign up takes ~3 min, no affiliate link, just the plain domain.

Total cost: €4.51/mo for the VPS, €0 for everything else. Tested on a fresh CX22 in Falkenstein, Ubuntu 24.04 LTS, on 2026-05-02.

Step 1 — Provision the Hetzner CX22 instance

In the Hetzner Cloud Console:

  1. Create a new project (call it analytics).
  2. Add Server → Location: Falkenstein (fsn1) or Helsinki (hel1) — both EU, same price.
  3. Image: Ubuntu 24.04.
  4. Type: Shared vCPU → CX22 (€4.51/mo).
  5. SSH keys: paste your public key (cat ~/.ssh/id_ed25519.pub on Mac/Linux). Don’t use password auth.
  6. Name: plausible-1.

Click Create. Hetzner gives you the public IPv4 in ~30 seconds. Note it down — we’ll call it $IP from here on.

Step 2 — Point DNS at the server

In your DNS provider (Cloudflare, Vercel, your registrar, doesn’t matter), add:

Type: A
Name: analytics       (or @ if you want plausible at the apex)
Value: $IP            (your Hetzner IPv4)
TTL:   60
Proxy: OFF            (Cloudflare-specific — we want Caddy to handle TLS, not CF)

If you proxy through Cloudflare instead, Caddy can’t issue Let’s Encrypt certs the easy way. Either turn the proxy off, or skip Caddy and use the CF Origin certificate — out of scope here.

Read:  Umami on Vercel + Neon: Free Self-Hosted Analytics in 5 Minutes

Verify propagation: dig +short analytics.your-site.com should return $IP within a minute. If it doesn’t, wait, then try again. DNS lies sometimes.

Step 3 — Bootstrap the server

SSH in:

ssh root@$IP

Update and install Docker + Compose:

apt update && apt upgrade -y
apt install -y curl ca-certificates gnupg ufw

# Docker official install
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
  https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" \
  | tee /etc/apt/sources.list.d/docker.list > /dev/null

apt update
apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# Firewall: only 22, 80, 443 open
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

Confirm Docker works: docker run --rm hello-world should print the welcome message.

Step 4 — Clone Plausible Community Edition

mkdir -p /opt/plausible && cd /opt/plausible
git clone https://github.com/plausible/community-edition.git .

The repo ships with a docker-compose.yml that wires up four services: Plausible itself (Elixir/Phoenix), PostgreSQL (for app data — users, sites, settings), ClickHouse (for the actual event store), and the Plausible event ingester.

You don’t need to touch the compose file for a basic install. Edit .env instead:

cp .env.example .env
nano .env

Set these three values minimum:

BASE_URL=https://analytics.your-site.com
SECRET_KEY_BASE=$(openssl rand -base64 64 | tr -d '\n')
TOTP_VAULT_KEY=$(openssl rand -base64 32 | tr -d '\n')

(Yes, you actually run the openssl commands and paste the output. Each must be a unique long string. If you ever lose SECRET_KEY_BASE, all sessions invalidate; if you lose TOTP_VAULT_KEY, 2FA breaks for users that enabled it.)

Optional but recommended for a personal install:

DISABLE_REGISTRATION=invite_only
SMTP_HOST=...      # if you want email reports + invites; skip for now
[email protected]

Step 5 — Boot the stack

docker compose up -d

First boot pulls ~600 MB of images and runs DB migrations. Takes 1–2 minutes. Watch progress:

docker compose logs -f plausible

You’re looking for the line Running PlausibleWeb.Endpoint with cowboy 2.x.x at :::8000 (http). When you see it, hit Ctrl+C to detach (the container keeps running) and verify locally:

curl -I http://127.0.0.1:8000

Should return HTTP/1.1 302 Found with a redirect to /login. Plausible is alive on port 8000 — but only on localhost. We’ll expose it via Caddy with HTTPS next.

Read:  Matomo on Hetzner: Self-Host with MariaDB & Caddy (€8.21/mo, 30 min)

Step 6 — Caddy in front (auto-HTTPS, zero config)

Caddy is the simplest reverse proxy in the world for this kind of job. It handles Let’s Encrypt automatically, redirects HTTP→HTTPS, and the entire config is 4 lines.

apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' \
  | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' \
  | tee /etc/apt/sources.list.d/caddy-stable.list

apt update
apt install -y caddy

Edit /etc/caddy/Caddyfile:

analytics.your-site.com {
    reverse_proxy 127.0.0.1:8000
}

That’s it. Reload:

systemctl reload caddy

Caddy will hit Let’s Encrypt, fetch a certificate, install it, and start serving. Watch the log to confirm:

journalctl -u caddy -f

Look for certificate obtained successfully. From this point onward, https://analytics.your-site.com will load Plausible’s login page — and HTTP will auto-redirect to HTTPS.

Step 7 — First-run setup

Open https://analytics.your-site.com in a browser. Plausible’s setup wizard appears:

  1. Create the first admin account (email + password).
  2. Email confirmation is bypassed in self-hosted unless you set SMTP_* in .env.
  3. Click “Add a website” → enter your-site.com → choose timezone.
  4. Plausible gives you the tracking snippet — paste it before </head> on the site you want to track.

Within ~30 seconds of pasting the snippet, your first pageview should appear in the dashboard.

What just happened (mechanism, not recipe)

You now have a four-container stack on a Hetzner CX22:

  • Plausible (Elixir) — serves the dashboard UI, accepts events at /api/event, talks to Postgres for app data and ClickHouse for events.
  • PostgreSQL — stores users, sites, settings, scheduled reports. Tiny, ~50 MB.
  • ClickHouse — columnar store for the actual pageview/event data. This is what makes Plausible fast at scale; aggregations across 10M events take milliseconds.
  • Caddy (host-level, not in compose) — reverse-proxies analytics.your-site.com127.0.0.1:8000 and auto-renews TLS every 60 days from Let’s Encrypt.

Cookies set on visitors: zero. Plausible identifies unique visitors via daily-rotated salted hash of IP + User-Agent + domain — the salt rotates at 00:00 UTC, so the same visitor on two consecutive days appears as two different anonymous IDs. This is what makes it consent-banner-free under GDPR’s legitimate-interest basis.

Backup, updates, monitoring (10 more minutes)

Backup. The state lives in two Docker volumes: plausible_db-data (Postgres) and plausible_event-data (ClickHouse). Snapshot them weekly with a single cron line:

echo '0 3 * * 0 cd /opt/plausible && docker compose exec -T plausible_db pg_dump -U postgres plausible_db | gzip > /backups/pg-$(date +\%F).sql.gz' | crontab -

(Adjust path. Push to S3/B2/Hetzner Storage Box as needed. ClickHouse backups are larger — see CE backup docs.)

Read:  PostHog on Hetzner: Self-Host Product Analytics with Docker (€16.41/mo, 45 min)

Updates. Plausible CE releases roughly every 4–6 weeks. To upgrade:

cd /opt/plausible
git pull
docker compose pull
docker compose up -d

Migrations run on startup. Total downtime: ~30 seconds.

Monitoring. Add Hetzner’s free metrics in the Cloud Console (CPU/RAM/disk graphs) — sufficient for a personal install. For real production, plug in UptimeRobot on https://analytics.your-site.com/api/health.

What this costs you over 3 years

Line item Year 1 3-year total
Hetzner CX22 €54.12 €162.36
Domain (subdomain — no extra cost) €0 €0
Backup storage (Hetzner Storage Box 100GB) €38.40 €115.20
Total €92.52 €277.56

For comparison, Plausible Cloud at the same traffic tier (~10k pageviews/mo) is €108/year — basically equivalent if you don’t already have a Hetzner account. Self-hosting wins decisively above ~50k events/mo where Plausible Cloud jumps to €228/year.

When to NOT use this setup

  • You expect >5M events/month. CX22 will start swapping under sustained load. Move to CX32 (€8.21/mo) or shard ClickHouse out.
  • You need goals, funnels, ecommerce, or session replay. Plausible CE doesn’t have these — that’s by design (it’s the minimalist tool). Try Matomo on Hetzner or PostHog instead.
  • Your team isn’t comfortable with SSH + Docker. Use Plausible Cloud (managed) or Umami on Vercel — simpler ops at the cost of monthly fees or vendor lock.

Troubleshooting (the three things that actually break)

Caddy can’t get a cert. 9 times out of 10 it’s DNS not pointing at the server yet. dig analytics.your-site.com, confirm. If DNS is fine, check port 80 isn’t blocked: ufw status. Caddy needs port 80 reachable for the HTTP-01 challenge.

Plausible container restart loop. Almost always missing or invalid SECRET_KEY_BASE / TOTP_VAULT_KEY. Check .env, regenerate, docker compose down && docker compose up -d.

No events arriving. Inspect the snippet on the tracked site (DevTools → Network → filter “event”). Common causes: ad-blocker on your end, snippet not actually injected, or BASE_URL mismatch (the snippet hard-codes the script URL from BASE_URL). Confirm the script returns 200 from your tracked site’s network panel.

What to do next

If this stack fits, the next things worth doing:

  • Run the Stack Picker to confirm Plausible is actually the right pick for your traffic + features — or pivot to Matomo, Umami, PostHog, Rybbit if not.
  • Run the TCO calculator to compare 3-year self-host vs Plausible Cloud at your actual pageview volume.
  • Set up a second site in the same Plausible install — Plausible CE has no per-site limit, you can add 50 sites to one VPS without paying anything more.
  • Bookmark this page for the next time you redeploy. The recipe is versioned — if Plausible CE changes the install steps, this page gets a “Updated” stamp at the top.

Found this useful?

Try the Stack Picker to get a personal recommendation, or browse the install recipe library.