DEV Community

DeployLark
DeployLark

Posted on Originally published at deploylark.com

Self-Host Plausible Community Edition with Docker

Plausible Community Edition is a self-hosted, cookie-free analytics stack. The same product family powers Plausible Cloud, and CE is the free, AGPL-licensed release you run yourself. On October 18, 2026, the analytics repository had about 29,500 stars, and the community-edition compose repo had about 2,900. This guide installs CE v3.2.1 from the official Compose files, pins every image tag, puts the dashboard on 127.0.0.1, and confirms a real pageview on a lab site.

The Umami Docker and PostgreSQL guide covers a lighter Postgres-only stack. Plausible CE adds ClickHouse for events and ships a different feature set from Cloud. For a broader app platform on the same VPS, see the Coolify install guide.

TL;DR:

  • Clone plausible/community-edition at tag v3.2.1, set BASE_URL and a 64-byte SECRET_KEY_BASE in .env, and pin Postgres and ClickHouse to exact tags in compose.override.yml.
  • Bind the app to 127.0.0.1:8000 and put Nginx Proxy Manager or Caddy in front with HTTPS. Keep BASE_URL equal to the public URL.
  • Open the dashboard, create the first account, add a site, paste the generated script into the site <head>, and confirm a pageview on the dashboard.
  • Expect about 2 GB of RAM for a calm ClickHouse setup. CPU needs SSE 4.2 or NEON.
  • Upgrade by checking out a newer CE tag and running docker compose up -d. Back up Postgres with pg_dump and keep the ClickHouse volumes.

Table of contents

What CE gives you, and what stays on Cloud

Feature and licence differences

The self-hosting docs compare Cloud and CE in one table. The points that matter for a home lab or a small team are:

  • You own the server: installs, upgrades, backups, capacity and uptime are yours.
  • CE releases twice a year. Cloud ships features continuously.
  • Marketing funnels, user journeys, ecommerce revenue goals, SSO and the sites API are Cloud-only.
  • Bot filtering on CE is the basic User-Agent and referrer-spam path. Cloud adds data-center IP filtering and a broader bot model.
  • Premium human support is Cloud-only. Self-hosting is community-supported only, per the self-hosting docs.
  • CE is AGPL-3.0 (or later). Cloud is a paid subscription that funds the project.

If you modify CE and offer it as a network service, AGPL requires you to share the corresponding source with users of that service. Read the licence before you ship a public SaaS on top of CE.

Hardware and CPU requirements

The CE README asks for Docker, Docker Compose, a CPU with SSE 4.2 or NEON (ClickHouse needs it), and at least 2 GB of RAM so ClickHouse and Plausible stay clear of OOM kills. The compose file mounts low-resource ClickHouse settings for small servers. On the lab host used for this guide (8 vCPUs with SSE 4.2, Debian 13), the three containers used about 710 MB of RAM after the first pageviews: about 450 MB for Plausible, 185 MB for ClickHouse and 80 MB for Postgres.

Install the official Compose stack

Clone the repo and set .env

The install path is the community-edition repository, not a hand-rolled compose file. Clone a specific tag so the ClickHouse config files match the app:

git clone -b v3.2.1 --single-branch https://github.com/plausible/community-edition plausible-ce
cd plausible-ce
Enter fullscreen mode Exit fullscreen mode

Create .env with the two required variables from the configuration wiki:

touch .env
chmod 600 .env
echo "BASE_URL=https://plausible.example.com" >> .env
echo "SECRET_KEY_BASE=$(openssl rand -base64 48)" >> .env
echo "HTTP_PORT=8000" >> .env
Enter fullscreen mode Exit fullscreen mode

BASE_URL has no default. It drives link generation and WebSocket origin checks. Set it to the URL people will type in the browser, including https:// once a reverse proxy terminates TLS. For a local evaluation, the README tip is BASE_URL=http://localhost:8000 (or any free port). The lab run used http://127.0.0.1:8000.

SECRET_KEY_BASE also has no default. It must be at least 64 bytes. openssl rand -base64 48 produces a 64-character value that meets that rule. Runtime config raises if the key is missing: SECRET_KEY_BASE configuration option is required. A short key fails later with cookie store expects conn.secret_key_base to be at least 64 bytes, which matches discussion #3516 and issue #5612.

Pin image tags and bind to localhost

Upstream compose.yml at v3.2.1 uses ghcr.io/plausible/community-edition:v3.2.1, postgres:16-alpine and clickhouse/clickhouse-server:24.12-alpine. The last two float. On October 18, 2026, those tags resolved to PostgreSQL 16.15 and ClickHouse 24.12.6.70. Put the exact tags in compose.override.yml so future docker compose pull calls stay on known builds, and publish the app only on the loopback address as the reverse-proxy wiki shows:

services:
  plausible_db:
    image: postgres:16.15-alpine
  plausible_events_db:
    image: clickhouse/clickhouse-server:24.12.6.70-alpine
  plausible:
    image: ghcr.io/plausible/community-edition:v3.2.1
    ports:
      - "127.0.0.1:8000:8000"
Enter fullscreen mode Exit fullscreen mode

The override file is the supported place for local changes. It survives git pull of a new CE tag better than editing compose.yml.

The stack looks like this once it is up:

Diagram of the Plausible Community Edition Docker Compose stack. A visitor browser sends POST /api/event to the plausible container (CE v3.2.1 on 127.0.0.1:8000). A second browser reaches the dashboard over HTTP. The plausible container uses plausible_db (PostgreSQL 16.15) and plausible_events_db (ClickHouse 24.12.6.70). An optional reverse proxy (Caddy or Nginx Proxy Manager) terminates TLS. Named volumes are db-data, event-data, event-logs and plausible-data.

Start it:

docker compose up -d
docker compose ps
curl --head http://127.0.0.1:8000
Enter fullscreen mode Exit fullscreen mode

On a warm host with images already pulled, Postgres and ClickHouse became healthy in under a minute, and /register returned HTTP 200 about 15 seconds after the app container started. An Uptime Kuma HTTP monitor on /login is enough to notice when the dashboard stops answering. The first visit is the registration page:

Plausible Community Edition registration page titled Register your Plausible CE account, with a five-step progress bar starting at Register, and a form for full name, email, password and confirm password, plus a Create my account button. Password hint says Min 12 characters.

Create that first account right away. Default registration is invite-only after the first user (DISABLE_REGISTRATION defaults to invite_only in the wiki). The first account owns the instance.

Default database credentials in the compose file are postgres / postgres. Change them for anything exposed beyond a private lab by setting POSTGRES_PASSWORD and a matching DATABASE_URL in .env (see the configuration wiki).

Put HTTPS in front

Reverse proxy layout

From CE 2.1.2 onward, Plausible can request Let's Encrypt certificates itself when HTTP_PORT=80 and HTTPS_PORT=443 are published. Behind an existing proxy, keep HTTP_PORT=8000, bind 127.0.0.1:8000, and terminate TLS on the proxy. That is the layout this guide tested.

A minimal Caddy config from the wiki is:

plausible.example.com {
    reverse_proxy localhost:8000
}
Enter fullscreen mode Exit fullscreen mode

Nginx Proxy Manager works the same way: proxy host to http://127.0.0.1:8000, force SSL, and enable WebSockets so /live/websocket stays up for the live dashboard. The same 127.0.0.1 binding pattern shows up in the Vaultwarden HTTPS guide and the Paperless-ngx Compose guide. The wiki's Nginx sample proxies that path with Upgrade and Connection headers.

Keep BASE_URL on the public hostname

Whatever you pick, BASE_URL must match the public URL. With a wrong host, the dashboard HTML can load while the LiveView WebSocket returns 403. Issue #205 shows the log line Could not check origin for Phoenix.Socket transport when the browser origin and BASE_URL disagree. After you change .env, run docker compose up -d so the container recreates. A plain docker compose restart does not reload env vars.

Add a site and confirm a pageview

Install the tracking script

After registration, the wizard asks for a domain and timezone, then shows a site-specific script. Paste it into the <head> of the site. On CE v3.2.1 the snippet looks like this shape (the pa-… filename is unique per site):

<!-- Privacy-friendly analytics by Plausible -->
<script async src="https://plausible.example.com/js/pa-xxxxxxxx.js"></script>
<script>
  window.plausible=window.plausible||function(){(plausible.q=plausible.q||[]).push(arguments)},plausible.init=plausible.init||function(i){plausible.o=i||{}};
  plausible.init()
</script>
Enter fullscreen mode Exit fullscreen mode

Plausible CE Install Plausible step showing the Script tab, a COPY box with the tracking snippet that loads a pa- script from the instance URL, optional measurements for outbound links, file downloads and form submissions, and a Verify Script installation button.

Optional checkboxes turn on outbound-link, file-download and form-submission measurements. The new script updates those choices from the dashboard. Legacy snippets that used data-domain still work. The script update guide covers migration.

The script's event endpoint is taken from BASE_URL (it posts to {BASE_URL}/api/event). Sites that only allowlist their own analytics host in Content-Security-Policy need that origin in connect-src, or an endpoint override in plausible.init as shown in discussion #6126.

Confirm the first pageview

Open the site in a normal browser and watch the CE dashboard for the domain. The lab run added example.test, served a static page with the generated snippet, and recorded pageviews. After four accepted events, the dashboard showed 4 unique visitors, 4 total pageviews and the / path under Top pages:

Plausible CE dashboard for example.test showing 4 current visitors, Unique visitors 4, Total visits 4, Total pageviews 4, Views per visit 1, Bounce rate 100 percent, a today graph with a spike to 4, Top sources Direct / None, and Top pages /.

Two checks that catch false starts:

  1. The script ignores localhost and 127.0.0.1 by design. The browser console prints Ignoring Event: localhost. That matches community-edition issue #92 and the troubleshooting docs. Test on a real hostname (a /etc/hosts entry is enough in a lab).
  2. A 202 from POST /api/event is not always a stored event. The troubleshooting docs say to look for x-plausible-dropped: 1 when a visit is filtered. The lab posts that used a normal browser User-Agent and an X-Forwarded-For address returned 202 without that header, and ClickHouse held four rows in plausible_events_db.events_v2.

Upgrades and backups

Upgrades

The upgrade wiki recommends pinning a full patch tag such as v3.2.1. Security fixes are not backported, so plan to move forward. v3.2.1 itself is a security release that removes an exposed /storybook endpoint (CVE-2026-8467). General steps for a patch or minor bump:

cd plausible-ce
git fetch origin v3.2.1:v3.2.1
git checkout v3.2.1
# keep your .env and compose.override.yml
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

Read the release notes before every major bump. Some releases change ClickHouse config files in the community-edition repo, which is why the checkout step matters.

Backups

Postgres holds users and site settings. ClickHouse holds events. The Upgrade PostgreSQL wiki shows a pg_dump path that doubles as a backup:

docker compose stop plausible
docker compose exec plausible_db sh -c "pg_dump -U postgres plausible_db > plausible_db.bak"
docker compose cp plausible_db:plausible_db.bak ./plausible_db.bak
docker compose start plausible
Enter fullscreen mode Exit fullscreen mode

Keep copies of the ClickHouse volumes (event-data, and event-logs if you care about server logs) with your volume backup tool. There is no separate CE wiki page that ships a one-line ClickHouse dump. Restoring across major ClickHouse versions needs extra care, so keep the image tag pin in compose.override.yml.

Also back up .env. Losing SECRET_KEY_BASE logs everyone out of the dashboard. Site data in Postgres and ClickHouse stays, as ruslandoga noted when rotating the key.

CE can also send account activation, password resets and optional reports when SMTP or a mail provider adapter is configured. The configuration wiki lists SMTP_HOST_ADDR, MAILER_EMAIL and the Postmark, Mailgun, Mandrill and SendGrid variables. Without SMTP, local accounts still work when ENABLE_EMAIL_VERIFICATION stays at its default false. Those providers were not exercised in the lab run.

FAQ

Setup and resources

How much RAM does Plausible CE need?

The CE README recommends at least 2 GB so ClickHouse and Plausible avoid OOM kills. The lab stack with the bundled low-resource ClickHouse config used about 710 MB of RAM after the first pageviews. Builds and heavy queries need more headroom than idle stats.

Why does the dashboard load but live data stay empty?

BASE_URL usually does not match the URL in the browser. The HTML can still load while /live/websocket fails origin checks. Set BASE_URL to the public URL, then recreate the container with docker compose up -d. See issue #205.

Why is SECRET_KEY_BASE required, and how long must it be?

The configuration wiki requires it and asks for at least 64 bytes. Generate one with openssl rand -base64 48. A missing key stops startup. A short key breaks cookie sessions with cookie store expects conn.secret_key_base to be at least 64 bytes.

Tracking and HTTPS

Why does the browser say "Ignoring Event: localhost"?

The tracker refuses localhost and loopback hosts so local development noise stays out of production stats. Use a real hostname for the test site, or a hosts-file entry that is not localhost. Maintainer replies in issue #92 call out the same console warning.

Which Cloud features are missing from CE?

Per the self-hosting docs: marketing funnels, user journeys, ecommerce revenue goals, SSO and the sites API. Bot filtering is also simpler on CE, and there is no premium support channel.

How do you put HTTPS in front of CE?

Either publish ports 80 and 443 and let CE request certificates, or bind 127.0.0.1:8000 and terminate TLS on Caddy or Nginx Proxy Manager. Keep BASE_URL on the https:// URL in both cases. The reverse-proxy wiki has sample configs.

Originally published on DeployLark: Self-Host Plausible Community Edition with Docker

Top comments (0)