Plausible is the self-hosted analytics tool people reach for when they want something polished, privacy-respecting, and lightweight on the client side — the tracking script is under a kilobyte. The self-hostable version is called Plausible Community Edition (CE), and it's a solid Elixir/Phoenix application. But before you deploy it, you need to understand one architectural fact that shapes the whole setup, because most "deploy Plausible in 5 minutes" posts gloss over it.
The two-database reality
Plausible CE needs two data stores:
- 1PostgreSQL — for accounts, sites, goals, and general application state.
- 2ClickHouse — for the actual analytics events. Every pageview lands in ClickHouse, and every dashboard query reads from it.
This is a deliberate and good design. ClickHouse is a columnar database built for exactly this workload — we use it ourselves for PandaStack's own server-side metrics and analytics pipeline, and it's excellent. But it means self-hosting Plausible is a two-stateful-services problem, not one. If you want a single-database analytics stack, Umami (Postgres or MySQL only) is the simpler self-host; Plausible buys you a more scalable events store at the cost of running it.
Here's how I'd split it: run the Plausible app container and its PostgreSQL on PandaStack, where the database wiring is automatic, and run ClickHouse wherever suits you — more on the options below.
What Plausible CE needs
- The app image:
ghcr.io/plausible/community-edition, published by the Plausible team. Pin a versioned tag from the [releases page](https://github.com/plausible/community-edition/releases) rather than tracking latest. - Environment variables (the required core):
BASE_URL=https://plausible.yourdomain.com
SECRET_KEY_BASE=<generate with: openssl rand -base64 48>
DATABASE_URL=postgres://user:pass@host:5432/plausible_db
CLICKHOUSE_DATABASE_URL=http://user:pass@clickhouse-host:8123/plausible_events_dbBASE_URL must be the exact public URL you'll serve the dashboard from — Plausible uses it to build links and validate requests, and a mismatch produces confusing auth behavior. SECRET_KEY_BASE signs sessions; generate it once and keep it stable. Recent CE versions also use a separate key for encrypting two-factor secrets (TOTP_VAULT_KEY) — check the CE README for the exact requirements of the version you pin.
- Port: the app listens on 8000 by default (
HTTP_PORToverrides it).
The Dockerfile: migrations on startup
The CE repo's reference docker-compose runs the container with an explicit command chain: create the database if needed, migrate, then serve. Replicate that in a one-file repo so any platform build gets the same behavior:
# Dockerfile
FROM ghcr.io/plausible/community-edition:v2.1
CMD ["sh", "-c", "/entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"]This is the piece most tutorials forget. Without the db createdb && db migrate steps, the app boots against an empty Postgres, finds no schema, and crash-loops. With them, every deploy is self-healing: schema changes ship inside the image and apply before the app takes traffic. Since migrations here are forward-only and run before run, a redeploy after a version bump upgrades the schema automatically.
Setting up the PostgreSQL side on PandaStack
- 1Provision a managed PostgreSQL instance from the dashboard — 16.x is a good choice for Plausible. Backups are scheduled daily and retained per plan (7/15/30 days on Free/Pro/Premium).
- 2Connect the repo containing your Dockerfile as a container app. PandaStack builds it with rootless BuildKit in an ephemeral Kubernetes Job pod and streams the build logs live, so if the image pull or build fails you see it immediately rather than staring at a spinner.
- 3Attach the database to the app.
DATABASE_URLis injected into the container automatically — and Plausible reads exactly that variable name, so there's zero translation. Thedb createdbstep in your CMD will create the schema inside the provisioned database on first boot.
That covers half the architecture with no credential copying at all.
The ClickHouse side: your realistic options
PandaStack's managed database lineup today is PostgreSQL, MySQL, MongoDB, and Redis — no managed ClickHouse. So the events store lives outside the managed-DB flow. Your options, in rough order of operational effort:
- ClickHouse Cloud — fully managed by the ClickHouse team. Least ops, usage-based pricing; see [clickhouse.com](https://clickhouse.com/) for current terms.
- A small VM running the official
clickhouse/clickhouse-serverDocker image or package. This is what most self-hosters do. One tip from the CE repo worth stealing: it ships a trimmed ClickHouse config that reduces the retention of ClickHouse's own system log tables — on a small box those internal logs can quietly eat more disk than your actual analytics data. - An existing ClickHouse you already run for something else — Plausible just needs its own database (
plausible_events_db) and a user.
Whichever you pick, set CLICKHOUSE_DATABASE_URL in the app's environment variables. Note the scheme: it's ClickHouse's HTTP interface (port 8123), not the native TCP protocol:
CLICKHOUSE_DATABASE_URL=http://plausible:yourpassword@your-clickhouse-host:8123/plausible_events_dbMake sure the ClickHouse host is reachable from the app and locked down to it — an open 8123 on the public internet with a weak password is a query interface for strangers.
Going live
Set the app's port to 8000, add your remaining env vars (BASE_URL, SECRET_KEY_BASE), and deploy. From here, every git push to the connected repo triggers a rebuild and redeploy — upgrading Plausible later is literally bumping the image tag in the Dockerfile and pushing.
Once it's up:
- 1Create the first account through the web UI at your
BASE_URL. This first user becomes the owner. - 2Disable open registration by setting
DISABLE_REGISTRATION=truein the environment and redeploying. A public Plausible instance with registration open will accumulate strangers' accounts. - 3Add your site in the dashboard and grab the snippet:
<script defer data-domain="yourdomain.com"
src="https://plausible.yourdomain.com/js/script.js"></script>- 1Add a custom domain in the app settings — SSL is provisioned automatically. As with any analytics tool, settle the domain before you embed the snippet anywhere; the script URL is baked into every tracked page.
One free-tier caveat, stated plainly: free-tier apps on PandaStack scale to zero when idle. For an analytics collector that's receiving beacons, that means the first events after a quiet period can arrive during a cold start and get dropped. Run the Plausible app on a paid tier if complete data matters to you — the collector should stay warm.
Is self-hosting Plausible worth it?
Fair question to end on. Plausible's own cloud service exists precisely because the ClickHouse half is real operational work — disk monitoring, upgrades, backups of a second stateful system. If you just want privacy-friendly numbers with minimal ops, Umami's single-Postgres design is less to own, and Plausible Cloud is zero to own. Self-hosted Plausible CE makes sense when you want Plausible's specific product — the UI, the goal tracking, the sub-kilobyte script — with full data ownership, and you're willing to own one ClickHouse to get it.
Useful references: the [Plausible CE repo](https://github.com/plausible/community-edition), the [self-hosting docs](https://plausible.io/docs/self-hosting), and the [ClickHouse docs](https://clickhouse.com/docs).
If the Postgres-plus-container half is the part you'd rather not hand-wire, it's a ten-minute setup on https://pandastack.io.