Plausible: Privacy Analytics That Don't Suck
Tried Umami first - too basic. Looked at Matomo - too heavy. Plausible hit the sweet spot: clean UI, actually useful insights, and my users' data stays on my server. Cost me $6/month to host vs Google's "free" product that sells your data.
Problem
Got Plausible running fine, but the "forgot password" feature did nothing.
Users (including me) couldn't reset passwords. Checked the logs - no error messages,
just silence when trying to send emails.
Error: {:error, :send_failed} in Plausible logs.
What I Tried
Attempt 1: Verified SMTP credentials worked with telnet - they did.
Attempt 2: Changed SMTP ports (587, 465, 25) - none worked.
Attempt 3: Tried Gmail SMTP - also failed (less secure app issue).
Actual Fix
Plausible requires specific SMTP environment variables, and the mailer adapter wasn't configured. Needed to set SMTP_HOST_ADDR, SMTP_HOST_PORT, and use a proper mailer service. Also, some providers (like Mailgun) require TLS which needs explicit configuration.
# In docker-compose.yml, add to plausible service:
environment:
# SMTP Configuration
MAILER_EMAIL: noreply@yourdomain.com
SMTP_HOST_ADDR: smtp.mailgun.org
SMTP_HOST_PORT: 587
SMTP_USER_NAME: postmaster@yourdomain.com
SMTP_USER_PASSWORD: ${SMTP_PASSWORD}
SMTP_RETRIES: 2
SMTP_TLS: "true" # Critical for Mailgun and similar
# Or for Gmail (requires App Password now):
environment:
MAILER_EMAIL: your-email@gmail.com
SMTP_HOST_ADDR: smtp.gmail.com
SMTP_HOST_PORT: 587
SMTP_USER_NAME: your-email@gmail.com
SMTP_USER_PASSWORD: ${GMAIL_APP_PASSWORD} # Generate in Google Account settings
SMTP_TLS: "true"
# Test email sending from within container:
docker exec plausible bash
/app/plausible/bin/plausible exec release_tasks
# Then trigger a password reset to test
Problem
After running Plausible for 2 weeks with moderate traffic (~5k daily pageviews), the server started swapping. Checking htop, ClickHouse was consuming 2.5GB RAM on a 2GB VPS. The container got OOM killed and took analytics down.
What I Tried
Attempt 1: Added 4GB swap - kept swapping, performance degraded.
Attempt 2: Reduced ClickHouse memory limit in docker-compose - database queries started failing.
Attempt 3: Restarted ClickHouse - memory usage slowly climbed back up.
Actual Fix
ClickHouse is designed to use lots of RAM for performance. The solution: 1) Enable ClickHouse's memory profiler to understand what's consuming memory, 2) Configure appropriate memory limits that leave room for the OS and other containers, and 3) Set up log cleanup to prevent unbounded growth.
# Add ClickHouse configuration with memory limits
services:
clickhouse:
image: clickhouse/clickhouse-server:23
container_name: plausible_clickhouse
networks:
- plausible-network
volumes:
- ./data/clickhouse:/var/lib/clickhouse
- ./clickhouse/config.xml:/etc/clickhouse-server/config.d/custom.xml:ro
ulimits:
nofile:
soft: 262144
hard: 262144
# Create clickhouse/config.xml:
#
# 1500000000
# 1000000000
# 1000000000
# warning
#
# warning
# 100M
# 3
#
#
# For low-traffic sites, you can also disable some ClickHouse features:
# ClickHouse config → disable unnecessary table functions
Problem
Added Plausible tracking to multiple domains on different servers. Worked fine on the main domain, but other sites showed CORS errors in console: "Cross-origin request blocked: same-origin policy". Tracking events weren't being recorded from those sites.
What I Tried
Attempt 1: Added CORS headers in my site's Nginx config - didn't help (wrong place).
Attempt 2: Used the same tracking script on all sites - CORS errors persisted.
Attempt 3: Tried accessing Plausible via IP instead of domain - browser blocked mixed content.
Actual Fix
The CORS configuration needs to be on the Plausible server itself, not the sites being tracked. Plausible has a BASE_URL configuration that needs to match your domain. Also needed to configure the reverse proxy to send proper CORS headers for the /api/event endpoint.
# Nginx reverse proxy config for Plausible
server {
listen 443 ssl http2;
server_name analytics.yourdomain.com;
# Basic proxy setup
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;
}
# Specific CORS handling for event tracking
location /api/event {
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;
# CORS headers for cross-site tracking
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'POST, OPTIONS' always;
add_header 'Access-Control-Allow-Headers' 'Content-Type' always;
if ($request_method = 'OPTIONS') {
return 204;
}
}
}
# In Plausible docker-compose.yml:
environment:
BASE_URL: https://analytics.yourdomain.com
# Important: must match the domain accessing it
What I Learned
- Lesson 1: SMTP configuration is tricky - use Mailgun or similar service for reliable email delivery.
- Lesson 2: ClickHouse needs significant RAM - budget at least 2GB RAM for small deployments, 4GB+ for production.
- Lesson 3: CORS issues happen when tracking across domains - configure headers on the Plausible server, not client sites.
- Overall: Plausible is more resource-intensive than Umami but provides better analytics. The trade-off is worth it if you need detailed insights, but not if you just want basic pageview counts.
Production Setup
Complete production setup with all services properly configured.
# docker-compose.yml for Plausible Hosting
version: "3.8"
services:
plausible_db:
image: postgres:15-alpine
container_name: plausible_db
volumes:
- ./data/postgres:/var/lib/postgresql/data
environment:
- POSTGRES_DB=plausible
- POSTGRES_USER=plausible
- POSTGRES_PASSWORD=${DB_PASSWORD}
networks:
- plausible-network
restart: always
plausible_events_db:
image: clickhouse/clickhouse-server:23
container_name: plausible_events_db
volumes:
- ./data/clickhouse:/var/lib/clickhouse
- ./clickhouse/config.xml:/etc/clickhouse-server/config.d/custom.xml:ro
networks:
- plausible-network
ulimits:
nofile:
soft: 262144
hard: 262144
restart: always
plausible:
image: plausible/analytics:latest
container_name: plausible
command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh db init-admin && /entrypoint.sh run"
depends_on:
- plausible_db
- plausible_events_db
ports:
- 127.0.0.1:8000:8000
environment:
- BASE_URL=https://analytics.yourdomain.com
- SECRET_KEY_BASE=${SECRET_KEY_BASE}
- DATABASE_URL=postgresql://plausible:${DB_PASSWORD}@plausible_db:5432/plausible
- CLICKHOUSE_DATABASE_URL=clickhouse://plausible_events_db:8123/plausible_events_db
- SERVER_PORT=8000
- DISABLE_REGISTRATION=true
- ADMIN_USER_EMAIL=${ADMIN_EMAIL}
- ADMIN_USER_NAME=admin
- ADMIN_USER_PWD=${ADMIN_PASSWORD}
networks:
- plausible-network
restart: always
networks:
plausible-network:
# Generate secrets in .env file
cat > .env << EOF
# Generate with: openssl rand -base64 64 | tr -d '\n'
SECRET_KEY_BASE=your_generated_secret_key_here
# Generate with: openssl rand -base64 32
DB_PASSWORD=your_generated_db_password_here
ADMIN_EMAIL=your@email.com
ADMIN_PASSWORD=your_secure_admin_password
EOF
# ClickHouse config file (clickhouse/config.xml)
cat > clickhouse/config.xml << EOF
1500000000
warning
100M
3
EOF
# Start everything
docker-compose up -d
# Wait for initialization, then access:
# https://analytics.yourdomain.com
Monitoring & Debugging
Essential commands for maintaining Plausible:
Essential Commands
# View logs
docker-compose logs -f plausible
# Check database sizes
docker exec plausible_events_db clickhouse-client --query "SELECT formatReadableSize(sum(bytes)) FROM system.parts WHERE active"
# Count events in database
docker exec plausible_events_db clickhouse-client --query "SELECT COUNT(*) FROM plausible_events_db.events"
# Backup PostgreSQL database
docker exec plausible_db pg_dump -U plausible plausible | gzip > backup_$(date +%Y%m%d).sql.gz
# Restart services
docker-compose restart plausible
# Update to latest version
docker-compose pull
docker-compose up -d
Red Flags to Watch For
- ClickHouse using >2GB RAM - consider upgrading server or tuning memory limits
- Events not appearing within 5 minutes - check database connectivity
- SMTP failures in logs - verify email credentials and TLS settings
- Disk growing >100MB/day - configure log rotation and data retention