Skip to content

Command Palette

Search for a command to run...

How to Deploy Plausible Analytics on a VPS

Updated Jul 2026

Self-host Plausible Community Edition on your own VPS with Docker — a lightweight, cookieless, privacy-friendly Google Analytics alternative you fully own.

Before you start
  • A VPS with 2 vCPU / ~4 GB RAM (Plausible runs Postgres + ClickHouse alongside the app)
  • A fresh Ubuntu 24.04 server with root/sudo SSH access
  • A domain you can point at the server (Plausible needs a public HTTPS URL to work)
  • An SMTP sender for invite/verification emails (optional — you can confirm the first account manually)

What Plausible is

Plausible is a lightweight, open-source web analytics tool — a privacy-first replacement for Google Analytics. It answers the questions most people actually open GA for (top pages, referrers, countries, devices, campaign UTMs, conversions) on a single clean dashboard, and it does it without cookies and without collecting personal data. No cross-site tracking, no consent banner in most jurisdictions, and a tracking script that's a fraction of GA's size.

The self-hostable edition is Plausible Community Edition (CE). Running it on your own box means the analytics data for your sites lives on hardware you control, never leaves your server, and costs the same flat VPS bill whether you track a thousand pageviews a month or a million. You trade a per-pageview SaaS subscription for owning the server.

One honest caveat before you start: Plausible CE is a three-service stack, not a single container. The app is small, but it depends on PostgreSQL and ClickHouse, and ClickHouse in particular wants real memory. That's the whole reason this guide points you at a 4 GB box rather than the cheapest VPS on the shelf — more on that below.

Server sizing — why the box needs real RAM

The data plane behind Plausible has three pieces, and you run all three on the same box:

  • the Plausible app container (the dashboard and the ingestion endpoint),
  • PostgreSQL — stores your user accounts, sites, and settings,
  • ClickHouse — the columnar database that stores every raw analytics event.

ClickHouse is the reason for the sizing. It's a serious analytical database designed to chew through large event volumes fast, and it's happiest with memory to spare. Postgres wants its share too. Stack the app on top and a 2 GB VPS ends up thrashing — ClickHouse is the first thing to get OOM-killed when memory runs short, and a killed ClickHouse means a dashboard that loads but shows no data.

So 2 vCPU / 4 GB RAM is the realistic floor. That's why we point people at a Hetzner CX22 (2 vCPU / 4 GB) as the value pick, or Kamatera when you want to dial in the exact RAM and add headroom on demand — ClickHouse rewards the extra memory. Disk grows with your event volume, but slowly for a normal-traffic site: 40 GB is comfortable to start, and you can watch it climb from the dashboard's own usage over time. If you'll be ingesting millions of events a month across many sites, size up to 8 GB and keep an eye on ClickHouse's memory rather than debugging silent data loss later.

Prepare the server

This guide assumes you've already worked through Docker & Compose on Ubuntu — Docker Engine and the Compose plugin installed, a non-root deploy user, ufw allowing SSH plus 80/443, and unattended security updates on. If you haven't, do that first; every deploy guide here builds on it.

Log in as your non-root user and confirm Docker is working without sudo:

ssh deploy@your-server-ip
docker compose version

Plausible only needs to be reachable on 80/443 (through the reverse proxy you'll add for HTTPS) and 22 for SSH — all of which the base guide already opened. The app's internal port 8000 stays private; you never expose it directly.

Install Plausible CE

Plausible CE ships as an official repository containing a ready-to-run docker-compose.yml — Postgres and ClickHouse are wired in for you. Clone it and step inside:

git clone https://github.com/plausible/community-edition plausible-ce
cd plausible-ce

The compose file expects a plausible-conf.env file next to it holding your configuration. Create it now. The two values you must set are BASE_URL and SECRET_KEY_BASE:

# generate a strong secret for signing sessions
openssl rand -base64 48

Then write plausible-conf.env:

BASE_URL=https://analytics.example.com
SECRET_KEY_BASE=<paste the openssl output here>
TOTP_VAULT_KEY=<paste a second openssl rand -base64 48 here>

What each one does:

  • BASE_URL is the single most important setting. It must be the public HTTPS URL where the instance will live (https://analytics.example.com). It's baked into the tracking script you'll embed and into every link the dashboard generates — get it wrong, or leave it as http:// or an IP, and the tracking snippet and dashboard links break.
  • SECRET_KEY_BASE signs user sessions. Generate it with openssl rand -base64 48, keep it stable (changing it logs everyone out), and keep it secret — treat this file like any other credential.
  • TOTP_VAULT_KEY encrypts the two-factor-auth secrets Plausible stores for accounts that enable 2FA. Current CE releases expect it; generate it the same way as SECRET_KEY_BASE. It must also stay stable and secret.

Now bring the stack up:

docker compose up -d

Compose pulls the Plausible, PostgreSQL, and ClickHouse images, runs the initial database migrations, and starts all three in the background. Give it a minute on first boot — ClickHouse takes a moment to initialize. Confirm every service came up healthy:

docker compose ps
docker compose logs --tail 40 plausible

The app now listens on port 8000 inside the Docker network. Don't publish that port to the internet — HTTPS goes in front of it next.

HTTPS + domain

Plausible requires a public HTTPS URL to function, and it must match the BASE_URL you set. Put a reverse proxy in front of the app's port 8000 to terminate TLS.

First, DNS: create an A record for your analytics hostname (say analytics.example.com) pointing at the server's public IP. Wait for it to resolve before requesting a certificate.

The simplest path is Caddy, which fetches and renews Let's Encrypt certificates automatically — see Automatic HTTPS with Caddy for the full setup. The reverse-proxy target is the Plausible container on port 8000. A minimal Caddyfile reads:

analytics.example.com {
    reverse_proxy plausible:8000
}

If Caddy runs in its own container, put it on the same Docker network as the Plausible stack so plausible:8000 resolves; if it runs on the host, proxy to 127.0.0.1:8000 and publish only that port to localhost in the compose file. Either way, the public URL Caddy serves — https://analytics.example.com — must be exactly what you put in BASE_URL. Load that URL in a browser; you should land on Plausible's registration screen over HTTPS.

First-run setup

With the dashboard reachable over HTTPS, do the initial setup.

Create the first account immediately. On a fresh CE install, registration is open, so the first person to register becomes the owner. Do it the moment HTTPS is live, before anything else is reachable. If you configured SMTP (next section), Plausible emails a verification link; if you didn't, you can confirm the account manually — see the email section below.

Once you're in:

  1. Add a site. Enter the domain you want to track (e.g. example.com) and pick its timezone.
  2. Copy the tracking snippet. Plausible shows a <script> tag pointing at your instance. Paste it into the <head> of the site you're measuring:
<script defer data-domain="example.com"
        src="https://analytics.example.com/js/script.js"></script>

The data-domain must match the site you added, and the src points at your own instance's BASE_URL — that's what keeps the data on your server. Deploy the change to your site, load a page, and within a few seconds the first visit shows up on the dashboard as a live "current visitor."

Email / SMTP (optional)

Plausible uses email for two things: verifying new self-registered accounts and sending team invites. It works fine without email for a single-owner setup, but wiring SMTP makes multi-user and invite flows smooth.

Add your provider's credentials to plausible-conf.env, then recreate the app:

MAILER_EMAIL=analytics@example.com
SMTP_HOST_ADDR=smtp.youprovider.com
SMTP_HOST_PORT=587
SMTP_USER_NAME=<smtp username>
SMTP_USER_PWD=<smtp password>
SMTP_HOST_SSL_ENABLED=false
docker compose up -d

Any transactional-email provider works — a dedicated SMTP relay is far more reliable than sending straight from the box, which usually lands in spam. If you'd rather not set up email at all, confirm the first account manually by marking the user activated directly in Postgres:

docker compose exec plausible_db psql -U postgres -d plausible_db \
  -c "UPDATE users SET email_verified = true;"

Run that once, right after creating the owner account, and you're in without ever sending an email.

Backups

Two databases hold your data, and you must back up both:

  • PostgreSQL — accounts, sites, and settings. Lose it and you lose the map to your setup.
  • ClickHouse — the actual analytics events. This is the one people forget, and it's the irreplaceable half: your entire history of pageviews lives here, not in Postgres.

Dump both on a schedule and ship the dumps off-box (an S3-compatible bucket) — a backup that only lives on the same server dies with the server:

# PostgreSQL: accounts, sites, settings
docker compose exec -T plausible_db \
  pg_dump -U postgres plausible_db | gzip > pg_$(date +%F).sql.gz

# ClickHouse: the raw analytics events (the half people forget)
docker compose exec -T plausible_events_db \
  clickhouse-client --query "BACKUP DATABASE plausible_events TO Disk('backups', 'ch_$(date +%F).zip')"

Wrap those in a small cron job, sync the output to off-box storage, and — at least once — test a restore into a throwaway stack. An untested backup is a guess. Service names (plausible_db, plausible_events_db) come from the CE compose file; confirm them with docker compose ps if the project renames them.

Upgrades

Plausible CE moves at a deliberate pace, but upgrades can carry database migrations — especially on the ClickHouse side — so treat them with a little care rather than pulling blind.

The habits that keep upgrades boring:

  • Pin the image version. The compose file references a Plausible image tag; point it at a specific release rather than a floating latest so you control exactly when you move.
  • Read the CE release notes first. They call out required migration steps and any breaking config changes. This is the one to actually read, not skim.
  • Back up both databases before upgrading (the step above), so a failed migration is recoverable.

Then upgrade deliberately:

docker compose pull
docker compose up -d

pull fetches the new images; up -d recreates only the changed containers and runs any pending migrations on startup. Watch docker compose logs -f plausible through the first boot to confirm migrations applied cleanly before you walk away.

Troubleshooting

Dashboard loads but shows no data. The most common launch problem, and almost always one of three things: the tracking <script> isn't actually in the page's <head> (view source and confirm), the data-domain/src don't match your site and BASE_URL, or an ad-blocker is eating the request during your own testing. Plausible's cookieless script is a frequent blocklist target — test in a private window with blockers off, or use the proxy/custom-path options Plausible documents for production. Also re-check that BASE_URL is the exact public HTTPS host; a mismatch there silently breaks ingestion.

ClickHouse gets OOM-killed / the box grinds. Memory. On an undersized VPS, ClickHouse is the first thing the kernel kills under pressure, and a dead ClickHouse means events aren't stored even though the dashboard still loads. Check docker compose ps for a restarting plausible_events_db, and dmesg | grep -i kill for OOM evidence. The fix is the 4 GB (or larger) sizing above — this is the single best argument for not running Plausible on a 2 GB box.

Verification or invite emails never arrive. SMTP misconfiguration, or mail from the server landing in spam. Confirm the SMTP_* values in plausible-conf.env, that you recreated the container after setting them (docker compose up -d), and check docker compose logs plausible for mailer errors. If you just need the first account, skip email entirely and use the manual email_verified = true update shown above.

Certificate never issues. The reverse proxy needs DNS pointing at the server and port 80 reachable so the ACME challenge can complete. Confirm the A record resolves (dig analytics.example.com) and that nothing else is bound to port 80.

Verification + next steps

You're done when you can: load the Plausible dashboard over HTTPS on your own domain, see all three containers healthy in docker compose ps, register the owner account, add a site, and watch a real visit appear live after pasting the tracking snippet into your site's <head> — with scheduled dumps of both Postgres and ClickHouse landing in off-box storage.

From here, add more sites, invite teammates, and set up goals and funnels. The one thing that makes or breaks a Plausible instance is the memory underneath ClickHouse — it's a real analytical database, not a toy. A Hetzner CX22 is the value pick at 2 vCPU / 4 GB; reach for Kamatera when you want to size RAM precisely and give ClickHouse the headroom it likes as your event volume grows. See Best VPS for self-hosting for the ranked picks.

Next steps

We use analytics cookies (Google Analytics, PostHog) to see which guides are useful. No ad networks, no cross-site tracking. See our privacy policy.