Skip to main content

Self-Host Plausible Analytics 2026: Docker Setup

·OSSAlt Team
plausibleanalyticsself-hostingdockerguide
Share:

Self-Hosting Plausible Analytics in 2026: Complete Production Guide

TL;DR

Plausible Analytics is a privacy-first, cookie-free Google Analytics alternative that runs on your own server. Self-hosting costs $5–10/month on a VPS vs $9–19/month for Plausible Cloud — and you keep full data ownership. This guide walks through production deployment: Docker Compose with ClickHouse and PostgreSQL, Caddy reverse proxy with automatic SSL, SMTP email setup, Google Search Console integration, custom event tracking, and backup procedures. The entire setup takes under 30 minutes.

Key Takeaways

  • Plausible requires two databases: PostgreSQL (for site/user data) and ClickHouse (for event analytics) — both handled by the official Community Edition Docker Compose
  • Self-hosting saves $108–588/year vs Plausible Cloud depending on traffic volume
  • The official community-edition repo manages everything — clone it, configure .env, run docker compose up -d
  • SMTP setup is required for email reports and password resets — Resend, AWS SES, or any SMTP server works
  • Google Search Console integration shows organic search queries directly in Plausible — configure with OAuth credentials
  • Backups require preserving both the PostgreSQL and ClickHouse data volumes

Why Self-Host Plausible?

Plausible Cloud is a well-run service. But self-hosting offers:

  • Cost: $60–120/year server cost vs $108–588/year cloud pricing
  • Data sovereignty: Events never leave your server — no third-party sees your traffic
  • No rate limits: Cloud plans have monthly pageview caps; self-hosted is unlimited
  • Custom retention: Default 5-year retention; adjust ClickHouse configuration to keep more or less
  • Air-gapped deployments: Internal tools and intranets where data must stay on-premises

Requirements

  • VPS with 2 GB RAM (ClickHouse needs memory headroom; 1 GB works but is tight)
  • Docker and Docker Compose v2+
  • Domain name pointed at your server (analytics.yourdomain.com)
  • SMTP credentials for email (Resend free tier, AWS SES, Postmark, or any SMTP)

Recommended VPS providers:

ProviderSpecMonthly Cost
Hetzner CX222 vCPU, 4 GB RAM€4.50/mo
DigitalOcean Basic2 vCPU, 2 GB RAM$12/mo
Linode Nanode1 vCPU, 1 GB RAM$5/mo (tight)
Vultr Cloud Compute1 vCPU, 2 GB RAM$10/mo

Step 1: Server Setup

# Ubuntu 22.04/24.04 — install Docker
curl -fsSL https://get.docker.com | sh

# Add your user to docker group (avoids sudo)
sudo usermod -aG docker $USER
newgrp docker

# Verify Docker and Compose
docker --version
docker compose version

Step 2: Clone Plausible Community Edition

Plausible maintains an official community-edition repository with the production-ready Docker Compose configuration.

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

The repository contains:

  • docker-compose.yml — defines all three services (PostgreSQL, ClickHouse, Plausible)
  • clickhouse/ — ClickHouse configuration files for log levels and IPv4 settings
  • .env.example — template for all environment variables

Step 3: Configure Environment Variables

# Copy the example environment file
cp .env.example .env

# Generate a 64-character secret key
openssl rand -base64 48

# Generate a 32-character TOTP vault key
openssl rand -base64 24
# .env — complete production configuration

# Base configuration
BASE_URL=https://analytics.yourdomain.com
SECRET_KEY_BASE=your-64-char-generated-secret-from-openssl-above
TOTP_VAULT_KEY=your-32-char-generated-key-from-openssl-above

# SMTP — required for email reports and password resets
# Option A: Resend (free tier: 3,000 emails/month)
MAILER_EMAIL=analytics@yourdomain.com
SMTP_HOST_ADDR=smtp.resend.com
SMTP_HOST_PORT=587
SMTP_USER_NAME=resend
SMTP_USER_PWD=re_your_resend_api_key_here
SMTP_HOST_SSL_ENABLED=true

# Option B: AWS SES
# SMTP_HOST_ADDR=email-smtp.us-east-1.amazonaws.com
# SMTP_HOST_PORT=587
# SMTP_USER_NAME=your-ses-iam-access-key
# SMTP_USER_PWD=your-ses-iam-secret-key

# Optional: Google Search Console integration
# Get credentials from: console.cloud.google.com → APIs & Services → Credentials
GOOGLE_CLIENT_ID=your-oauth-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-your_client_secret

# Optional: MaxMind GeoLite2 for city-level location data (free license)
# Register at: maxmind.com/en/geolite2/signup
MAXMIND_LICENSE_KEY=your_maxmind_license_key
MAXMIND_EDITION=GeoLite2-City

# Optional: IP geolocation database path (alternative to MaxMind)
# GEONAMES_SOURCE_FILE=/path/to/cities15000.txt

Step 4: Start Plausible

# Start all services (runs in background)
docker compose up -d

# Watch startup logs
docker compose logs -f plausible

# Check all services are healthy
docker compose ps

Wait for this log line before proceeding:

plausible  | [info] Running PlausibleWeb.Endpoint with cowboy 2.x.x at 0.0.0.0:8000

Plausible runs on localhost:8000 by default.


Step 5: Reverse Proxy with Caddy (Automatic SSL)

Caddy is the simplest option for HTTPS — it automatically provisions and renews Let's Encrypt certificates.

# Install Caddy on Ubuntu
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install caddy
# /etc/caddy/Caddyfile
analytics.yourdomain.com {
    reverse_proxy localhost:8000

    # Optional: block known bot traffic at the proxy level
    @bots header_regexp User-Agent (bot|crawl|spider|slurp)
    respond @bots 403
}
sudo systemctl enable caddy
sudo systemctl restart caddy

# Check Caddy is running and certificate issued
sudo caddy validate --config /etc/caddy/Caddyfile
curl -I https://analytics.yourdomain.com

Alternative: Nginx + Certbot

# /etc/nginx/sites-available/plausible
server {
    listen 80;
    server_name analytics.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name analytics.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;

    location / {
        proxy_pass http://localhost:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}
# Get SSL certificate
sudo certbot --nginx -d analytics.yourdomain.com
sudo systemctl reload nginx

Step 6: DNS Configuration

Add an A record pointing your subdomain to your server's IP:

Type: A
Name: analytics
Value: YOUR_SERVER_IP
TTL: 300 (or auto)

Propagation typically takes 1–5 minutes. Verify with:

dig +short analytics.yourdomain.com
# Should return your server IP

Step 7: Create Account and Add Site

  1. Navigate to https://analytics.yourdomain.com
  2. Click Register — create your admin account
  3. Click Add Website — enter your domain (yourdomain.com)
  4. Copy the tracking snippet

Step 8: Add Tracking Script to Your Site

<!-- Basic tracking — cookieless, under 1KB, no consent banner needed -->
<script defer data-domain="yourdomain.com"
  src="https://analytics.yourdomain.com/js/script.js"></script>

<!-- Enhanced tracking with outbound links, file downloads, and custom events -->
<script defer data-domain="yourdomain.com"
  src="https://analytics.yourdomain.com/js/script.tagged-events.outbound-links.file-downloads.js">
</script>

Custom Event Tracking

// Method 1: Data attributes (no JS required)
<button data-analytics='"Signup", {"props": {"plan": "pro"}}'>
  Start Free Trial
</button>

// Method 2: JavaScript API
window.plausible('Purchase', {
  props: { plan: 'pro', billing: 'annual' },
  revenue: { currency: 'USD', amount: 99.00 }
});

// Method 3: Tagged events (use class="plausible-event-name=EventName")
<a href="/pricing" class="plausible-event-name=Pricing+Click">
  View Pricing
</a>

Step 9: Google Search Console Integration

If you set GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET in your .env:

  1. In Plausible dashboard: Site Settings → Integrations → Google Search Console
  2. Click Continue with Google — authorize the OAuth connection
  3. Select your GSC property from the dropdown
  4. Click Save

The Search tab in your Plausible dashboard now shows organic search queries with impressions, clicks, and average position — without leaving Plausible.

Getting OAuth credentials:

  1. Go to Google Cloud Console
  2. Create or select a project
  3. APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID
  4. Application type: Web application
  5. Authorized redirect URIs: https://analytics.yourdomain.com/auth/google/callback
  6. Copy Client ID and Client Secret to .env

Step 10: Production Hardening

Backups

Plausible uses two databases. Back up both:

#!/bin/bash
# /opt/plausible-backup.sh

BACKUP_DIR="/opt/backups/plausible/$(date +%Y-%m-%d)"
mkdir -p "$BACKUP_DIR"

cd /opt/plausible-ce

# Backup PostgreSQL (site/user configuration)
docker compose exec -T plausible_db pg_dump -U postgres plausible > "$BACKUP_DIR/postgres.sql"

# Backup ClickHouse (analytics events — the big one)
docker compose exec -T plausible_events_db clickhouse-client \
  --query "BACKUP DATABASE plausible TO File('/var/lib/clickhouse/backup/plausible')"
docker cp plausible-ce-plausible_events_db-1:/var/lib/clickhouse/backup/plausible "$BACKUP_DIR/clickhouse/"

echo "Backup complete: $BACKUP_DIR"
# Add to crontab — daily backup at 2am
echo "0 2 * * * /opt/plausible-backup.sh >> /var/log/plausible-backup.log 2>&1" | crontab -

Updates

cd /opt/plausible-ce

# Pull latest Plausible CE version
git pull

# Pull new Docker images
docker compose pull

# Restart with new images (zero-downtime if using rolling restart)
docker compose up -d

Monitoring

Add Uptime Kuma (open source uptime monitoring) to alert when Plausible goes down:

# Add to your Uptime Kuma configuration
- name: "Plausible Analytics"
  url: "https://analytics.yourdomain.com"
  interval: 60
  maxretries: 3
  notificationId: 1  # Your Slack/email notification

Firewall

# Allow only necessary ports
sudo ufw allow 22    # SSH
sudo ufw allow 80    # HTTP (Caddy redirect)
sudo ufw allow 443   # HTTPS
sudo ufw enable

# Plausible (8000) should NOT be publicly exposed — Caddy proxies it

Resource Usage

MetricLow TrafficMedium TrafficHigh Traffic
Sites1–55–2020+
Monthly pageviews<1M1–10M10M+
RAM512 MB1–2 GB4+ GB
CPU1 core2 cores4+ cores
Disk/month100–500 MB1–5 GB10+ GB
VPS cost/month$5–6$12–20$40+

ClickHouse compression is excellent — 10 million events typically use less than 1 GB on disk.


Troubleshooting Common Issues

Plausible not receiving events:

# Check Plausible is running on port 8000
docker compose ps
curl -I http://localhost:8000

# Check Caddy is forwarding correctly
curl -I https://analytics.yourdomain.com

# Check tracking script loads (open DevTools → Network tab → filter "script.js")

ClickHouse health check fails:

# Check ClickHouse logs
docker compose logs plausible_events_db

# ClickHouse needs elevated nofile limits — verify with:
docker compose exec plausible_events_db ulimit -n
# Should return 262144

Email not working:

# Test SMTP from Plausible container
docker compose exec plausible bin/plausible eval "Plausible.Mailer.deliver_now(Plausible.Email.welcome_email(%{email: \"test@example.com\", name: \"Test\"}))"

Related: Matomo vs Plausible: Full-Featured vs Lightweight · Plausible vs Umami vs Matomo: The Three-Way Comparison · How to Migrate from Google Analytics to Umami

See open source alternatives to Plausible on OSSAlt.

The SaaS-to-Self-Hosted Migration Guide (Free PDF)

Step-by-step: infrastructure setup, data migration, backups, and security for 15+ common SaaS replacements. Used by 300+ developers.

Join 300+ self-hosters. Unsubscribe in one click.