You Won’t Believe What Happened When I Installed Plausible with Docker

I Installed Plausible with Docker
AI-generated image

If you're like me , running more than one websites and want's something open-source unlike Google analytics complxity, you've probably looked into Plausible Analytics.

Hosting your own analytics sounds simple– until you see it's not. I went throught every possible Docker and Self-hosting error you can think of: broken databases , 502 bad gateway errors, Docker volumes refusing to die and even accidentally deleting my entire ghost blogs.

What Is Plausible Analytics?

Plausible is a lightweight, open-source and privacy focused alternative to Google Analytics. It's really simple and cookie free.

We want to answer these questions:

  • Is Plausible is free?
  • Can I self-host it on my server with Docker and Caddy?
  • How can i monitor analytics for multiple websites from one dashboard?

If you want to self-hosting your plausible , yes it's free. In this article we'll walk throught step by step processes you should take using Docker , Caddy and custom domains.

During setup, I had to decide between using a subdomain or subdirectory for my websites — something that can affect your configuration and even SEO. I compared the two approaches in Comparison Between them if you’re facing the same decision.
Subdomain vs Subdirectory: What’s the Difference and Which One Is Better for SEO?
I have seen websites with addresses like blog.domain.com and asked myself how that’s different from something like domain.com/blog? These two url — Subdomains & Subdirectories, might seem similar , but they can have a big effect on your SEO strategy, site structure and user experience. In This Article

Comparison Between subdomain and subdirectory

What Was My Setup

  1. Run a multiple container using one docker-compose.yml.
  2. Subdomains:
  • blog.domain.com –> Ghost site
  • nomad.domain.com –> Flask app
  • plausible.domain.com –> Plausible (self-hosting)
  1. Use Caddy as reverse proxy for HTTPS and subdomain routing
  2. Setup everything from one place

My Full Stack Overview

  1. OS: Ubuntu VPS
  2. Reverse Proxy: Caddy
  3. Docker & Docker Compose
  4. Backend services:
    1. Ghost blog
    2. MySQL
    3. Plausible Analytics
    4. PostgreSQL
    5. Clickhouse

Folder Structure

This is my project structure on my server:

.
├── docker-compose.yml
├── Caddyfile
├── tourist/                 # Flask app
└── plausible-selfhost/      # Cloned from GitHub

Step-by-Step Setup

  1. Clone the Plausible Self-Hosting Repo
git clone https://github.com/plausible/hosting plausible-selfhost
cd plausible-selfhost
  1. Generate a .env file
touch .env

Add the following content and update values:

BASE_URL=https://yourdomain.com
SECRET_KEY_BASE=supersecretkey123
MAILER_EMAIL=hello@domain.com
SMTP_HOST=mail
SMTP_PORT=25
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_FROM_ADDRESS=hello@domain.com

You can generate your own secret key (+32 characters). use this:

openssl rand -base64 32
  1. Update your docker-compose.yml

Here's the full config with your Ghost,Flask and Caddy services + the new Plausible setup:

version: '3.8'

services:
  ghost:
    image: ghost:5-alpine
    container_name: ghost
    restart: unless-stopped
    depends_on:
      - mysql
    environment:
      url: https://blog.domain.com
      database__client: mysql
      database__connection__host: mysql
      database__connection__user: ghost
      database__connection__password: secret123
      database__connection__database: ghostdb
    volumes:
      - ghost_data:/var/lib/ghost/content
    expose:
      - 2368

  mysql:
    image: mysql:8
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: rootpass
      MYSQL_DATABASE: ghostdb
      MYSQL_USER: ghost
      MYSQL_PASSWORD: secret123
    volumes:
      - mysql_data:/var/lib/mysql

  tourist:
    build: ./tourist
    container_name: tourist
    restart: unless-stopped
    expose:
      - 5000
    volumes:
      - ./tourist:/app
    environment:
      FLASK_ENV: production
    command: flask run --host=0.0.0.0 --port=5000

  caddy:
    image: caddy:alpine
    container_name: caddy
    restart: unless-stopped
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - caddy_data:/data
      - caddy_config:/config
      - ./Caddyfile:/etc/caddy/Caddyfile

  plausible:
    image: plausible/analytics:latest
    container_name: plausible
    depends_on:
      - postgres
      - clickhouse
      - mail
    env_file:
      - ./plausible-selfhost/.env
    restart: unless-stopped
    expose:
      - 8000

  postgres:
    image: postgres:14
    container_name: plausible_postgres
    restart: unless-stopped
    environment:
      POSTGRES_DB: plausible
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: postgrespass
    volumes:
      - plausible_postgres_data:/var/lib/postgresql/data

  clickhouse:
    image: clickhouse/clickhouse-server:22.6-alpine
    container_name: plausible_clickhouse
    restart: unless-stopped
    ulimits:
      nofile:
        soft: 262144
        hard: 262144
    volumes:
      - plausible_clickhouse_data:/var/lib/clickhouse

  mail:
    image: bytemark/smtp
    container_name: plausible_smtp
    restart: unless-stopped

volumes:
  ghost_data:
  mysql_data:
  caddy_data:
  caddy_config:
  plausible_postgres_data:
  plausible_clickhouse_data:
  1. Caddyfile (with Plausible)
blog.domain.com www.blog.domain.com {
    reverse_proxy ghost:2368
}

nomad.domain.com www.nomad.domain.com {
    reverse_proxy tourist:5000
}

plausible.domain.com {
    reverse_proxy plausible:8000
}
  1. Run Everything

From directory containing docker-compose.yml, run:

docker-compose up -d

Now visit https://plausible.domain.com and create your admin account.

  1. Add Tracking to Your Site

Add this script in to <head> section of your HTML (Don't forget to replace your Domain):

<script defer data-domain="blog.domain.com" src="https://plausible.domain.com/js/plausible.js"></script>
  • You can add more domains inside the Plausible dashbord later.

But I Got Lots Of Errors

  1. First I ran:
docker ps
  1. When I tried to cleaning everything with:
docker compose down -v
  1. But I got this:
! Volume arash_plausible_postgres_data    Resource is still in use
! Volume arash_plausible_clickhouse_data  Resource is still in use
! Network arash_default                   Resource is still in use
  1. How I Fix it:
docker ps -a
docker rm -f <container_name_or_id>  # remove all orphaned or stuck containers
docker volume rm <volume_name>
docker network rm <network_name>

Huge Warning: docker-compose down -v Deletes All Data

I didn't realize that this command deletes all Docker Volumes, including database data, when i ran:

docker compose down -v

and suddenly deleted all my Ghost Blog content (posts, media, settings).

Lesson Learned:

Never run docker compose down -v unless you've backed up your volumes.

Backup Ghost Volumes before doing it:

docker run --rm -v ghost_data:/data -v $(pwd):/backup alpine tar czf /backup/ghost_backup.tar.gz -C /data .

Error: 502 Bad Gateway from Caddy

curl -I https://plausible.domain.com

#Returned

HTTP/2 502

Fix:

Caddy couldn't reach plausible:8000 because the container was crashing, make sure plausible container is running:

docker ps | grep plausible

verify caddy can connect to it:

docker exec -it caddy wget http://plausible:8000

Error: plausible Container Crashes Due to Database Errors

Postgrex.Error: password authentication failed for user "postgres"

Fix:

Make sure your docker-compose.yml and .env file match:

.env:

DATABASE_URL=postgres://postgres:postgres@plausible_postgres:5432/plausible

docker-compose.yml:

POSTGRES_PASSWORD: postgres

They must match, Restart again:

docker-compose down
docker-compose up -d --build

Error: Database Doesn’t Exist

FATAL 3D000 (invalid_catalog_name) database "plausible" does not exist

Plausible tries to auto-create the DB, but fails if volumes are messed up.

Fix:

Be sure that container is running with this command:

command: sh -c "/entrypoint.sh db createdb && /entrypoint.sh db migrate && /entrypoint.sh run"

You can manually create the DB inside Postgres:

docker exec -it plausible_postgres psql -U postgres
CREATE DATABASE plausible;
\q

Error: Domain Already Registered in Plausible

This domain cannot be registered. Perhaps one of your colleagues registered it?

Fix:

Log in with original admin user, or manually delete the domain from postgres:

docker exec -it plausible_postgres psql -U postgres
\c plausible
DELETE FROM sites WHERE domain = 'nomad.domain.com';

Key Lessons I Learned

  1. docker compose down -v —> wipe out everything
  2. Always Back up named volumes before cleanup
  3. Use one docker-compose.yml , don't mix multiple stacks
  4. Caddy's reverse proxy setup is clean and automatic if containers are healthy
  5. Plausible is picky about domain registration, one domain = one owner
Related Article

✅ Update: I finally got it right!

After nuking my blog the first time, I took a deep breath, tried again, and successfully added Plausible Analytics without deleting the universe.

Want to see how I did it step by step (with screenshots)?

How I Got Real Analytics with Plausible Without Crashing My Site
Yep, this time I didn’t nuke the server. After nearly deleting my entire blog the first time I tried self-hosting Plausible, you’d think I’d be too scared to touch it again. But no. I went back in — this time to actually add the tracking script to my

Plausible Web Analytics Dashbord

Final Thoughts

If you're planning to self-host tools lie Plausible , Ghost , Flask app and more , you'll face into problems. This aticle gives you not just the fixes but the things to avoid breaking.

With Docker , Caddy and a few minutes, I had my analytics dashboard running flawlessly

If you’re building indie projects or freelance websites, this is a powerful free tool you can run forever.

You can fuel my next data science deep dive by buying me a coffee ☕!