Self-Host Plausible Analytics 2026: Docker Setup
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-editionrepo manages everything — clone it, configure.env, rundocker 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:
| Provider | Spec | Monthly Cost |
|---|---|---|
| Hetzner CX22 | 2 vCPU, 4 GB RAM | €4.50/mo |
| DigitalOcean Basic | 2 vCPU, 2 GB RAM | $12/mo |
| Linode Nanode | 1 vCPU, 1 GB RAM | $5/mo (tight) |
| Vultr Cloud Compute | 1 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
- Navigate to
https://analytics.yourdomain.com - Click Register — create your admin account
- Click Add Website — enter your domain (
yourdomain.com) - 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:
- In Plausible dashboard: Site Settings → Integrations → Google Search Console
- Click Continue with Google — authorize the OAuth connection
- Select your GSC property from the dropdown
- 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:
- Go to Google Cloud Console
- Create or select a project
- APIs & Services → Credentials → Create Credentials → OAuth 2.0 Client ID
- Application type: Web application
- Authorized redirect URIs:
https://analytics.yourdomain.com/auth/google/callback - 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
| Metric | Low Traffic | Medium Traffic | High Traffic |
|---|---|---|---|
| Sites | 1–5 | 5–20 | 20+ |
| Monthly pageviews | <1M | 1–10M | 10M+ |
| RAM | 512 MB | 1–2 GB | 4+ GB |
| CPU | 1 core | 2 cores | 4+ cores |
| Disk/month | 100–500 MB | 1–5 GB | 10+ 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.