Skip to content

Plausible Analytics Plausible Analytics

Description / nameInput element
Container Registry
Container Configuration Root Path
Timezone
User ID
Group ID
Plausible Analytics /config Path

Build Status Last Commit OCI Pulls

Privacy-first web analytics: one lightweight script, no cookies, a dashboard anyone can read. Runs with PostgreSQL and ClickHouse.

Registry ghcr.io/daemonless/plausible
Daemonless daemonless/plausible
Source plausible/analytics
Website plausible.io

Version Tags

Tag Description Best For
latest Built from the latest upstream source release, on FreeBSD's Erlang. Most users — recommended.

Parts

Service Image Role
plausible ghcr.io/daemonless/plausible:latest The analytics server: dashboard, tracking script, API docs
postgres ghcr.io/daemonless/postgres:latest Sites, users and settings docs
clickhouse ghcr.io/daemonless/clickhouse:pkg-latest The events: every pageview lands here docs

Before deploying, ensure your host environment is ready. See the Quick Start Guide for host setup instructions, including the security model for host vs. container privileges.

Deployment

1. Save as .env and fill in what is empty:

# Plausible Analytics for FreeBSD (Daemonless)
# Copy to .env and edit.

# Where data lives on the host
CONFIG_LOCATION=/containers/plausible
POSTGRES_DATA_LOCATION=/containers/plausible-postgres
CLICKHOUSE_DATA_LOCATION=/containers/plausible-clickhouse

# Public URL Plausible is reached on. Must match what the browser uses:
# the tracking snippet points at it.
BASE_URL=http://localhost:8000

# Database passwords. Change both to random passwords!
# Use only characters A-Za-z0-9 (no special characters or spaces)
POSTGRES_PASSWORD=plausible
CLICKHOUSE_PASSWORD=plausible

# Sign-up after the first account: invite_only (default), true (closed) or false (open)
# DISABLE_REGISTRATION=invite_only

# Timezone (TZ identifier, e.g. Europe/Berlin)
TZ=UTC

2. Save as compose.yaml:

name: plausible

services:
  plausible:
    image: ghcr.io/daemonless/plausible:latest
    container_name: plausible
    restart: unless-stopped
    network_mode: host

    environment:
      - PUID=1000
      - PGID=1000
      - TZ=${TZ:-UTC}
      - BASE_URL=${BASE_URL:-http://localhost:8000}
      # Generated into /config/secrets.env on first start if unset.
      - SECRET_KEY_BASE=${SECRET_KEY_BASE:-}
      - TOTP_VAULT_KEY=${TOTP_VAULT_KEY:-}
      # Plausible connects to the postgres and clickhouse below with these.
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-plausible}
      - CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:-plausible}
      # Databases of your own instead:
      # - DATABASE_URL=postgres://plausible:secret@db.example.com:5432/plausible_db
      # - CLICKHOUSE_DATABASE_URL=http://default:secret@ch.example.com:8123/plausible_events_db
      # Upstream's default: after the first account, new users only by invitation.
      - DISABLE_REGISTRATION=${DISABLE_REGISTRATION:-invite_only}

    depends_on:
      - postgres
      - clickhouse

    volumes:
      - ${CONFIG_LOCATION}:/config

  postgres:
    image: ghcr.io/daemonless/postgres:latest
    container_name: plausible-postgres
    restart: unless-stopped
    network_mode: host
    # Needs SysV IPC
    annotations:
      org.freebsd.jail.allow.sysvipc: "true"

    environment:
      - POSTGRES_USER=plausible
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-plausible}
      - POSTGRES_DB=plausible_db
      - PUID=1000
      - PGID=1000
      - TZ=${TZ:-UTC}

    volumes:
      - ${POSTGRES_DATA_LOCATION}:/var/lib/postgresql/data

  clickhouse:
    image: ghcr.io/daemonless/clickhouse:pkg-latest
    container_name: plausible-clickhouse
    restart: unless-stopped
    network_mode: host
    # ClickHouse on FreeBSD needs the long stop: see its image notes.
    stop_grace_period: 60s

    environment:
      - CLICKHOUSE_PASSWORD=${CLICKHOUSE_PASSWORD:-plausible}
      - PUID=1000
      - PGID=1000
      - TZ=${TZ:-UTC}

    volumes:
      - ${CLICKHOUSE_DATA_LOCATION}:/var/db/clickhouse

Then run podman-compose up -d.

# .env

DIRECTOR_PROJECT=plausible
PUID=1000
PGID=1000
TZ=UTC
BASE_URL=http://localhost:8000
SECRET_KEY_BASE=<SECRET_KEY_BASE>
TOTP_VAULT_KEY=<TOTP_VAULT_KEY>
POSTGRES_PASSWORD=plausible
CLICKHOUSE_PASSWORD=plausible
DISABLE_REGISTRATION=invite_only
# appjail-director.yml

options:
  - alias:
  - ip4_inherit:
services:
  plausible:
    name: plausible
    options:
      - container: 'args:--pull'
    oci:
      user: root
      environment:
        - PUID: !ENV '${PUID}'
        - PGID: !ENV '${PGID}'
        - TZ: !ENV '${TZ}'
        - BASE_URL: !ENV '${BASE_URL}'
        - SECRET_KEY_BASE: !ENV '${SECRET_KEY_BASE}'
        - TOTP_VAULT_KEY: !ENV '${TOTP_VAULT_KEY}'
        - POSTGRES_PASSWORD: !ENV '${POSTGRES_PASSWORD}'
        - CLICKHOUSE_PASSWORD: !ENV '${CLICKHOUSE_PASSWORD}'
        - DISABLE_REGISTRATION: !ENV '${DISABLE_REGISTRATION}'
    volumes:
      - PLAUSIBLE_CONFIG_PATH: /config
  plausible-postgres:
    name: plausible_postgres
    options:
      - from: ghcr.io/daemonless/postgres:latest
      - template: !ENV '${PWD}/postgres-template.conf'
    volumes:
      - postgres_data: /var/lib/postgresql/data
  plausible-clickhouse:
    name: plausible_clickhouse
    options:
      - from: ghcr.io/daemonless/clickhouse:pkg-latest
      - template: !ENV '${PWD}/template.conf'
    volumes:
      - clickhouse_data: /var/db/clickhouse
volumes:
  PLAUSIBLE_CONFIG_PATH:
    device: '/containers/plausible'
  postgres_data:
    device: !ENV '${POSTGRES_DATA_LOCATION}'
  clickhouse_data:
    device: !ENV '${CLICKHOUSE_DATA_LOCATION}'
1
2
3
4
5
6
7
# Makejail

ARG tag=latest

OPTION container=boot
OPTION overwrite=force
OPTION from=ghcr.io/daemonless/plausible:${tag}

Save the files above, then run appjail-director up.

Interactive Configuration

Parameters

Environment Variables

Variable Default Description
PUID 1000 User ID for the application process
PGID 1000 Group ID for the application process
TZ UTC Timezone for the container
BASE_URL http://localhost:8000 Full public URL of this instance, e.g. https://plausible.example.com. The tracking script and every link use it
SECRET_KEY_BASE <SECRET_KEY_BASE> Optional. 64+ random bytes, base64 (openssl rand -base64 48). Generated into /config/secrets.env on first start if unset
TOTP_VAULT_KEY <TOTP_VAULT_KEY> Optional. 32 random bytes, base64 (openssl rand -base64 32), encrypts 2FA secrets. Generated alongside SECRET_KEY_BASE if unset
POSTGRES_PASSWORD plausible Password for the bundled PostgreSQL (letters and digits only). Set it once, before the first start
CLICKHOUSE_PASSWORD plausible Password for the bundled ClickHouse (letters and digits only). Set it once, before the first start
DISABLE_REGISTRATION invite_only invite_only (default): after the first account, new users join by invitation. true closes sign-up entirely, false opens it to anyone
DATABASE_URL `` A PostgreSQL of your own instead of the bundled one, e.g. postgres://plausible:secret@db.example.com:5432/plausible_db. Unset, Plausible uses the stack's postgres with POSTGRES_PASSWORD
CLICKHOUSE_DATABASE_URL `` A ClickHouse of your own instead of the bundled one, e.g. http://default:secret@ch.example.com:8123/plausible_events_db. Unset, Plausible uses the stack's clickhouse with CLICKHOUSE_PASSWORD
MAILER_EMAIL `` From address for the emails Plausible sends (verification, reports)
SMTP_HOST_ADDR `` SMTP server for outgoing email; with SMTP_HOST_PORT, SMTP_USER_NAME, SMTP_USER_PWD and SMTP_HOST_SSL_ENABLED
MAXMIND_LICENSE_KEY <MAXMIND_LICENSE_KEY> Optional. A MaxMind key for the GeoLite2 city database; without it the bundled DB-IP country database is used

Volumes

Path Description
/config Secrets, the geolocation database and caches

Requirements

Plausible needs PostgreSQL and ClickHouse. Both are part of the compose file; Plausible creates its databases and runs migrations at every start. Pick POSTGRES_PASSWORD and CLICKHOUSE_PASSWORD before the first start: the databases are initialised with them.

Secrets

SECRET_KEY_BASE and TOTP_VAULT_KEY are generated into /config/secrets.env on first start, so there is nothing to prepare. Keep /config on persistent storage: a new SECRET_KEY_BASE signs everyone out and a new TOTP_VAULT_KEY locks out every 2FA user. To supply your own, set them in the environment; the environment always wins.

First run

Open BASE_URL: an instance without users goes straight to registration, and the first account becomes the owner. After that, sign-up is by invitation only (DISABLE_REGISTRATION=invite_only, the default). BASE_URL must be the address browsers use: the tracking snippet points at it.

Email

Without an SMTP server Plausible cannot send verification mail; account verification is off unless ENABLE_EMAIL_VERIFICATION=true. See the configuration wiki for every setting.

License

Plausible is licensed under the AGPL-3.0.

Implementation Details

  • Architectures: amd64
  • User: bsd (UID/GID set via PUID/PGID). Defaults to 1000:1000.
  • Base: Built on ghcr.io/daemonless/base (FreeBSD 15.1).

Need help? Join our Discord community.