Analytics beginner ⏱ 30-45 minutes

Plausible Analytics: Privacy-First Web Analytics for Your Homelab

Deploy Plausible Analytics, a privacy-friendly Google Analytics alternative. Self-hosted, GDPR compliant, and no cookie consent needed.

By Homelab Team • 2025-01-15
#analytics#privacy#docker#self-hosted#gdpr

Introduction

Web analytics shouldn’t require sacrificing user privacy or dealing with complex cookie consent banners. Plausible Analytics offers a refreshing alternative to Google Analytics: lightweight, privacy-focused, and remarkably straightforward.

With 19,000+ stars on GitHub and adoption by companies like Firefox, GitLab, and Tailwind CSS, Plausible has proven that ethical analytics can be both powerful and practical. Its clean interface shows you everything you need while respecting visitor privacy.

What You’ll Learn:

  • Deploy Plausible Analytics with Docker Compose in under 30 minutes
  • Configure privacy-first tracking without cookies or consent banners
  • Set up GDPR-compliant analytics that work across countries
  • Create custom event tracking for conversions and goals
  • Integrate with reverse proxies for secure access
  • Share public dashboards with stakeholders

Prerequisites:

  • Docker and Docker Compose installed (v20.10+ and v2.0+)
  • Basic understanding of DNS and web hosting
  • 1GB+ free disk space (2-5GB recommended for production)
  • Domain name with ability to add DNS records (optional but recommended)

Why Plausible Analytics?

The Privacy Problem with Traditional Analytics

Traditional analytics platforms like Google Analytics face several challenges:

  1. Privacy violations: Track users across websites, collect personal data
  2. Cookie consent: Require annoying banners that hurt conversion rates
  3. Complex interfaces: GA4’s learning curve frustrates non-technical users
  4. Data ownership: Your analytics data lives on someone else’s servers
  5. GDPR compliance: Difficult to ensure full compliance with EU regulations

How Plausible Is Different

Plausible Analytics takes a radically different approach:

Privacy by Design:

  • No cookies, no fingerprinting, no cross-site tracking
  • All data is aggregate only—never personal
  • GDPR, CCPA, and PECR compliant by default
  • No need for cookie consent banners

Simplicity:

  • One-page dashboard shows everything at a glance
  • Setup takes minutes, not hours
  • Lightweight script (less than 1KB vs GA’s 45KB+)
  • Clean, intuitive interface anyone can understand

Self-Hosted Control:

  • All data stays on your infrastructure
  • No third-party access to your analytics
  • Full ownership and control
  • Export data anytime in CSV or JSON

Key Features

FeatureDetails
Script SizeUnder 1KB (GA is 45KB+)
Cookie-FreeNo cookies, no consent banners needed
PrivacyGDPR/CCPA compliant, no personal data collection
DashboardSingle-page overview, real-time updates
Goals & EventsCustom event tracking, conversion funnels
Public SharingShare dashboards with links or embeds
Data RetentionForever (or configure custom retention)
API AccessFull REST API for custom integrations

Understanding Plausible Architecture

How Plausible Works

Website Visitor → Lightweight Script (1KB) → Plausible Server → ClickHouse Database
                                                    ↓
                                             PostgreSQL (metadata)
                                                    ↓
                                          Dashboard & API (Elixir/Phoenix)

Components:

  1. JavaScript Tracker: Ultra-light script embedded in your website
  2. Plausible Server: Elixir/Phoenix application processing events
  3. ClickHouse: Columnar database storing analytics events
  4. PostgreSQL: Stores site configurations and user accounts

What Gets Tracked:

  • Page URL and referrer source
  • Browser and device type (aggregated)
  • Country derived from IP (not stored)
  • Screen size categories

What’s NEVER Tracked:

  • IP addresses (immediately discarded)
  • Personal information
  • Cross-site behavior
  • Cookies or local storage
  • Exact geographic location

Installation: Docker Compose Method

Step 1: Create Project Directory

mkdir -p ~/plausible-analytics
cd ~/plausible-analytics

Step 2: Create docker-compose.yml

This configuration provides a production-ready Plausible deployment:

version: "3.8"

services:
  plausible:
    image: plausible/analytics:v2.0
    container_name: plausible
    restart: always
    command: sh -c "sleep 10 && /entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"
    depends_on:
      - plausible_db
      - plausible_events_db
      - mail
    ports:
      - "8000:8000"
    env_file:
      - plausible-conf.env
    networks:
      - plausible-network

  plausible_db:
    image: postgres:14-alpine
    container_name: plausible_db
    restart: always
    volumes:
      - plausible-db-data:/var/lib/postgresql/data
    environment:
      - POSTGRES_PASSWORD=postgres
    networks:
      - plausible-network

  plausible_events_db:
    image: clickhouse/clickhouse-server:23.3.7.5-alpine
    container_name: plausible_events_db
    restart: always
    volumes:
      - plausible-events-data:/var/lib/clickhouse
      - ./clickhouse/clickhouse-config.xml:/etc/clickhouse-server/config.d/logging.xml:ro
      - ./clickhouse/clickhouse-user-config.xml:/etc/clickhouse-server/users.d/logging.xml:ro
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    networks:
      - plausible-network

  mail:
    image: bytemark/smtp
    container_name: plausible_mail
    restart: always
    networks:
      - plausible-network

volumes:
  plausible-db-data:
    driver: local
  plausible-events-data:
    driver: local

networks:
  plausible-network:
    driver: bridge

Step 3: Create Configuration File

Create plausible-conf.env with your settings:

# Base URL for your Plausible instance
BASE_URL=https://analytics.yourdomain.com

# Secret key base (generate with: openssl rand -base64 64)
SECRET_KEY_BASE=REPLACE_WITH_RANDOM_64_CHAR_STRING

# Database URLs
DATABASE_URL=postgres://postgres:postgres@plausible_db:5432/plausible_db
CLICKHOUSE_DATABASE_URL=http://plausible_events_db:8123/plausible_events_db

# Admin user credentials (change these!)
[email protected]
ADMIN_USER_NAME=Admin
ADMIN_USER_PWD=changeme123

# Disable registration after creating your account
DISABLE_REGISTRATION=false

# SMTP settings for email notifications (optional)
[email protected]
SMTP_HOST_ADDR=mail
SMTP_HOST_PORT=25

# Timezone (optional, defaults to UTC)
# TZ=America/New_York

Generate SECRET_KEY_BASE:

openssl rand -base64 64

Copy the output and replace REPLACE_WITH_RANDOM_64_CHAR_STRING in the config file.

Step 4: Create ClickHouse Configuration

Create the ClickHouse config directory and files:

mkdir -p clickhouse

Create clickhouse/clickhouse-config.xml:

<clickhouse>
    <logger>
        <level>warning</level>
        <console>true</console>
    </logger>

    <!-- Stop all the unnecessary logging -->
    <query_thread_log remove="remove"/>
    <query_log remove="remove"/>
    <text_log remove="remove"/>
    <trace_log remove="remove"/>
    <metric_log remove="remove"/>
    <asynchronous_metric_log remove="remove"/>
    <session_log remove="remove"/>
    <part_log remove="remove"/>
</clickhouse>

Create clickhouse/clickhouse-user-config.xml:

<clickhouse>
    <profiles>
        <default>
            <log_queries>0</log_queries>
            <log_query_threads>0</log_query_threads>
        </default>
    </profiles>
</clickhouse>

Step 5: Start the Stack

# Start all services
docker compose up -d

# Check container status
docker compose ps

# Watch logs for initialization
docker compose logs -f plausible

Expected output:

plausible  | Running migrations...
plausible  | Migrations complete!
plausible  | Starting Plausible Analytics...
plausible  | [info] Running PlausibleWeb.Endpoint with cowboy 2.9.0 at http://0.0.0.0:8000

Step 6: Access Web Interface

Navigate to: http://YOUR_SERVER_IP:8000

Initial setup:

  1. You’ll be redirected to the registration page
  2. Enter your admin email and password (from plausible-conf.env)
  3. Click “Register” to create your account
  4. Verify your email (if SMTP is configured)
  5. After registration, set DISABLE_REGISTRATION=true in plausible-conf.env
  6. Restart: docker compose restart plausible

Adding Your First Website

Step 1: Create Site in Dashboard

  1. Log into Plausible dashboard
  2. Click “Add a website”
  3. Enter your domain (e.g., example.com)
  4. Select timezone for reports
  5. Click “Add snippet”

Step 2: Install Tracking Script

Copy the tracking script provided (looks like this):

<script defer data-domain="yourdomain.com" src="https://analytics.yourdomain.com/js/script.js"></script>

Installation locations:

Static HTML sites:

<!DOCTYPE html>
<html>
<head>
    <title>My Website</title>
    <!-- Add just before closing head tag -->
    <script defer data-domain="example.com" src="https://analytics.yourdomain.com/js/script.js"></script>
</head>
<body>
    <!-- Your content -->
</body>
</html>

WordPress:

  1. Install “Insert Headers and Footers” plugin
  2. Paste script in “Scripts in Header” section
  3. Save changes

Ghost: Add to Settings → Code Injection → Site Header

Hugo: Add to layouts/partials/head.html or your theme’s header partial

Next.js: Add to pages/_app.js or app/layout.js:

import Script from 'next/script'

export default function RootLayout({ children }) {
  return (
    <html>
      <head>
        <Script
          defer
          data-domain="yourdomain.com"
          src="https://analytics.yourdomain.com/js/script.js"
        />
      </head>
      <body>{children}</body>
    </html>
  )
}

Step 3: Verify Installation

Method 1: Real-time Dashboard

  1. Visit your website in a browser
  2. Open Plausible dashboard
  3. Should see “1 current visitor” within 10 seconds

Method 2: Browser DevTools

  1. Visit your website
  2. Open browser DevTools (F12)
  3. Go to Network tab
  4. Filter by “script.js”
  5. Should see successful request (200 OK)

Method 3: Using curl

# Test that the script is accessible
curl -I https://analytics.yourdomain.com/js/script.js

# Should return: HTTP/2 200

Setting Up Goals and Conversions

Goals track specific actions visitors take on your site: button clicks, form submissions, purchases, etc.

Creating Custom Events

Step 1: Define Goal in Dashboard

  1. Go to your site settings
  2. Click “Goals” section
  3. Click ”+ Add Goal”
  4. Choose “Custom Event”
  5. Name it (e.g., signup, purchase, download)
  6. Save

Step 2: Trigger Events in Your Code

Button click tracking:

<button onclick="plausible('signup')">Sign Up</button>

Form submission:

<form onsubmit="plausible('newsletter_signup'); return true;">
    <input type="email" name="email" required>
    <button type="submit">Subscribe</button>
</form>

JavaScript event:

// Track any custom event
function trackDownload(filename) {
    plausible('file_download', {
        props: {
            filename: filename
        }
    });
}

document.getElementById('download-btn').addEventListener('click', () => {
    trackDownload('user-guide.pdf');
});

React example:

import { useEffect } from 'react';

function PricingPage() {
    const handlePurchase = () => {
        window.plausible('purchase', {
            props: {
                plan: 'pro',
                price: 49
            }
        });
        // Continue with purchase logic
    };

    return (
        <button onClick={handlePurchase}>
            Buy Pro Plan
        </button>
    );
}

Pageview Goals

Track specific page visits:

  1. Go to site settings → Goals
  2. Click ”+ Add Goal”
  3. Choose “Pageview”
  4. Enter path (e.g., /thank-you, /checkout/complete)
  5. Save

Now Plausible automatically tracks when users visit that page.

Tracking Revenue

For e-commerce tracking:

// Track purchase with monetary value
plausible('purchase', {
    props: {
        amount: 99.99,
        currency: 'USD',
        product: 'Premium Subscription'
    }
});

Configure revenue goal:

  1. Create custom event goal named purchase
  2. In goal settings, enable “Track revenue”
  3. Plausible will sum the amount prop for revenue reports

Reverse Proxy Configuration

For production deployments, run Plausible behind a reverse proxy with SSL.

Nginx Configuration

# /etc/nginx/sites-available/plausible
server {
    listen 443 ssl http2;
    server_name analytics.yourdomain.com;

    # SSL certificates (Let's Encrypt recommended)
    ssl_certificate /etc/letsencrypt/live/analytics.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/analytics.yourdomain.com/privkey.pem;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # Plausible proxy
    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;
        proxy_buffering off;
    }

    # Optional: Increase body size for imports
    client_max_body_size 10M;
}

# Redirect HTTP to HTTPS
server {
    listen 80;
    server_name analytics.yourdomain.com;
    return 301 https://$server_name$request_uri;
}

Enable configuration:

sudo ln -s /etc/nginx/sites-available/plausible /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Caddy Configuration

# /etc/caddy/Caddyfile
analytics.yourdomain.com {
    reverse_proxy localhost:8000 {
        header_up X-Real-IP {remote_host}
    }

    # Security headers
    header {
        X-Frame-Options "SAMEORIGIN"
        X-Content-Type-Options "nosniff"
        Referrer-Policy "strict-origin-when-cross-origin"
    }
}

Reload Caddy:

sudo systemctl reload caddy

Traefik Configuration

Add labels to your docker-compose.yml:

services:
  plausible:
    image: plausible/analytics:v2.0
    container_name: plausible
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.plausible.rule=Host(`analytics.yourdomain.com`)"
      - "traefik.http.routers.plausible.entrypoints=websecure"
      - "traefik.http.routers.plausible.tls.certresolver=letsencrypt"
      - "traefik.http.services.plausible.loadbalancer.server.port=8000"
    networks:
      - traefik-proxy
      - plausible-network

networks:
  traefik-proxy:
    external: true
  plausible-network:
    driver: bridge

GDPR Compliance and Data Privacy

Why Plausible Is GDPR Compliant

Plausible is designed to be compliant with GDPR, CCPA, and PECR by default:

No Personal Data Collection:

  • IP addresses are not stored (used only for country detection, then discarded)
  • No cross-site or cross-device tracking
  • No fingerprinting techniques
  • No cookies or persistent identifiers

Data Processing:

  • All data is aggregate and anonymized
  • No way to identify individual visitors
  • Complies with “Privacy by Design” principle

Data Rights:

  • No personal data means no data subject requests
  • Easy data export and deletion
  • Full transparency in data processing

According to GDPR guidelines, Plausible does not require cookie consent banners because:

  1. It doesn’t use cookies
  2. It doesn’t collect personal data
  3. It doesn’t track users across sites
  4. Data processing is legitimate interest (website analytics)

Legal basis: Article 6(1)(f) GDPR - Legitimate interests

Privacy Policy Language

Add this to your privacy policy:

Analytics

We use Plausible Analytics, a privacy-friendly web analytics tool, to understand
how visitors use our website. Plausible does not use cookies and does not collect
personal data. All analytics data is aggregated and anonymized.

The following information is collected:
- Page URL and referrer
- Browser type and device category (mobile, desktop, tablet)
- Country derived from IP address (IP is not stored)
- Screen size category

Data is processed on our own server at [your location] and is never shared with
third parties. You can read more about Plausible's privacy approach at:
https://plausible.io/data-policy

Data Retention Settings

Configure data retention in plausible-conf.env:

# Keep data forever (default)
# No configuration needed

# Or set custom retention (in months)
# DATA_RETENTION_MONTHS=12

Sharing Dashboards

Share your analytics publicly without requiring login:

  1. Go to site settings
  2. Enable “Public Dashboard”
  3. Copy the generated link (e.g., https://analytics.yourdomain.com/share/example.com?auth=TOKEN)
  4. Share the link with stakeholders

Privacy: Public dashboards show analytics but hide:

  • Real-time visitor count
  • Geographic details beyond country
  • Specific source URLs (only domains shown)

Embed Dashboards

Embed your public dashboard in any website:

<iframe
    plausible-embed
    src="https://analytics.yourdomain.com/share/example.com?auth=TOKEN&embed=true&theme=light"
    scrolling="no"
    frameborder="0"
    loading="lazy"
    style="width: 1px; min-width: 100%; height: 1600px;">
</iframe>

<script async src="https://analytics.yourdomain.com/js/embed.host.js"></script>

Dark theme: Change theme=light to theme=dark


Advanced Configuration

Proxy Tracking Script

To avoid ad blockers, serve the Plausible script from your own domain:

Nginx proxy:

location /js/script.js {
    proxy_pass https://analytics.yourdomain.com/js/script.js;
    proxy_set_header Host $host;
}

location /api/event {
    proxy_pass https://analytics.yourdomain.com/api/event;
    proxy_buffering on;
}

Update tracking code:

<script defer data-domain="yourdomain.com" src="/js/script.js"></script>

Exclude Specific Pages

Exclude admin pages or internal paths from tracking:

<script
    defer
    data-domain="yourdomain.com"
    data-exclude="/admin/*,/api/*"
    src="https://analytics.yourdomain.com/js/script.js">
</script>

Track Local Development

By default, Plausible ignores localhost. To enable:

<script
    defer
    data-domain="yourdomain.com"
    src="https://analytics.yourdomain.com/js/script.local.js">
</script>

Note: Use script.local.js instead of script.js

Hash-Based Routing (SPAs)

For single-page apps with hash-based routing (e.g., example.com/#/page):

<script
    defer
    data-domain="yourdomain.com"
    src="https://analytics.yourdomain.com/js/script.hash.js">
</script>

Troubleshooting Common Issues

Issue 1: No Data Showing in Dashboard

Diagnosis:

# Check container logs
docker compose logs plausible

# Check if script loads
curl -I https://analytics.yourdomain.com/js/script.js

# Test event API
curl -X POST https://analytics.yourdomain.com/api/event   -H "Content-Type: application/json"   -d '{"domain":"yourdomain.com","name":"pageview","url":"https://yourdomain.com/test"}'

Solutions:

  1. Verify BASE_URL in plausible-conf.env matches your access URL
  2. Check browser console for CORS errors
  3. Ensure tracking script data-domain matches site domain in Plausible
  4. Disable browser ad blockers for testing
  5. Check your website domain is correctly added in Plausible

Issue 2: Database Migration Errors

Symptoms: Plausible container crashes on startup with migration errors

Solution:

# Reset and reinitialize databases
docker compose down -v
docker volume rm plausible-analytics_plausible-db-data
docker volume rm plausible-analytics_plausible-events-data
docker compose up -d

Warning: This deletes all data. Only use for fresh installations.

Issue 3: ClickHouse Memory Issues

Symptoms: plausible_events_db container using excessive memory

Solution: Limit ClickHouse memory usage in docker-compose.yml:

plausible_events_db:
  image: clickhouse/clickhouse-server:23.3.7.5-alpine
  environment:
    - CLICKHOUSE_DB=plausible_events_db
    - MAX_MEMORY_USAGE=2000000000  # 2GB limit
  deploy:
    resources:
      limits:
        memory: 2G

Issue 4: Email Notifications Not Sending

Symptoms: Weekly/monthly reports not arriving

External SMTP configuration: Update plausible-conf.env:

[email protected]
SMTP_HOST_ADDR=smtp.gmail.com
SMTP_HOST_PORT=587
[email protected]
SMTP_USER_PWD=your-app-password
SMTP_HOST_SSL_ENABLED=true
SMTP_RETRIES=2

Test SMTP:

docker compose restart plausible
docker compose logs plausible | grep -i smtp

Issue 5: High Disk Usage

Check storage:

docker system df -v | grep plausible

Solution: Configure data retention in plausible-conf.env:

# Retain data for 12 months
DATA_RETENTION_MONTHS=12

Manual cleanup:

# Access ClickHouse
docker exec -it plausible_events_db clickhouse-client

# Check table sizes
SELECT
    table,
    formatReadableSize(sum(bytes)) as size
FROM system.parts
WHERE database = 'plausible_events_db'
GROUP BY table;

# Delete old data (older than 12 months)
ALTER TABLE plausible_events_db.events
DELETE WHERE timestamp < now() - INTERVAL 12 MONTH;

Backup and Recovery

Backup Script

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

BACKUP_DIR="/backups/plausible"
DATE=$(date +%Y%m%d-%H%M%S)

mkdir -p "$BACKUP_DIR"

# Stop Plausible (keeps databases running)
cd ~/plausible-analytics
docker compose stop plausible

# Backup PostgreSQL
docker exec plausible_db pg_dump -U postgres plausible_db | gzip > "$BACKUP_DIR/postgres-$DATE.sql.gz"

# Backup ClickHouse
docker exec plausible_events_db clickhouse-client --query="BACKUP DATABASE plausible_events_db TO Disk('backups', '$DATE.zip')"
docker cp plausible_events_db:/var/lib/clickhouse/backups/$DATE.zip "$BACKUP_DIR/clickhouse-$DATE.zip"

# Backup config
cp plausible-conf.env "$BACKUP_DIR/plausible-conf-$DATE.env"

# Restart Plausible
docker compose start plausible

# Cleanup old backups (keep 30 days)
find "$BACKUP_DIR" -type f -mtime +30 -delete

echo "Backup completed: $DATE"

Automate with cron:

# Daily backup at 3 AM
0 3 * * * /root/backup-plausible.sh >> /var/log/plausible-backup.log 2>&1

Restore from Backup

# Stop services
docker compose down

# Restore PostgreSQL
gunzip -c /backups/plausible/postgres-YYYYMMDD-HHMMSS.sql.gz |   docker exec -i plausible_db psql -U postgres plausible_db

# Restore ClickHouse
docker cp /backups/plausible/clickhouse-YYYYMMDD-HHMMSS.zip plausible_events_db:/tmp/
docker exec plausible_events_db clickhouse-client --query="RESTORE DATABASE plausible_events_db FROM Disk('backups', 'YYYYMMDD-HHMMSS.zip')"

# Restart services
docker compose up -d

Maintenance Best Practices

Update Schedule

Monthly:

# Pull latest images
cd ~/plausible-analytics
docker compose pull

# Backup before update
./backup-plausible.sh

# Update containers
docker compose up -d

# Verify functionality
docker compose logs -f plausible

Monitoring:

# Check resource usage
docker stats plausible plausible_db plausible_events_db

# Check disk usage
du -sh ~/plausible-analytics/

Performance Optimization

For high-traffic sites (100k+ pageviews/month):

  1. Increase PostgreSQL resources:
plausible_db:
  environment:
    - POSTGRES_SHARED_BUFFERS=256MB
    - POSTGRES_EFFECTIVE_CACHE_SIZE=1GB
  1. Tune ClickHouse:
plausible_events_db:
  environment:
    - MAX_CONCURRENT_QUERIES=50
    - MAX_MEMORY_USAGE=4000000000  # 4GB
  1. Consider dedicated server for ClickHouse

Conclusion

You now have a production-ready, privacy-first analytics system that:

  • Respects user privacy without sacrificing insights
  • Requires no cookie consent banners
  • Keeps all data under your control
  • Provides beautiful, actionable dashboards
  • Complies with GDPR, CCPA, and PECR

Next Steps:

  1. Add tracking to all your websites
  2. Set up goals for key conversions
  3. Configure email reports for weekly summaries
  4. Share public dashboards with stakeholders
  5. Explore the API for custom integrations

Remember: Start simple with basic pageview tracking, then gradually add custom events as you identify what metrics truly matter for your websites.


Additional Resources

Official:

Community:

Integrations:


Last Updated: January 2025 Version Covered: Plausible Analytics v2.0+