How to self-host Plausible analytics

By

Learn how to self-host Plausible Analytics Community Edition on a cheap DigitalOcean Droplet with Docker Compose, including BASE_URL, SECRET_KEY_BASE, and HTTPS.

~~~

I decided to self-host my website analytics.

Why?

As a life rule, I tend to not self-host my own “things” unless the benefits vastly surpass the cons.

First, what are the cons of self-hosting? The most obvious is “this is something I need to worry about from now on”. It’s great to offload this burden to someone else, especially when you happen to use just the free plan.

I’m also happy paying something, don’t get me wrong.

But sometimes the costs are unjustifiable.

Like in the case of my email newsletter. Hosted solutions are just too expensive at scale.

Also, speaking of website analytics, hosted solutions can get pricey when your pageviews count is considerable.

So I decided to self-host their analytics solution.

I decided to do this after paying a year of subscription to Plausible. I tested their hosted option for a while on some smaller sites I have, and I wanted to also host my main property, this blog, but ..the hosted plan was too expensive for it. I don’t mind paying that annual sub as I think it’s good to support the project.

I can’t think about spending like $70/m just to host them. It’s not that useful information.

I like watching analytics, but it’s almost $1k/y.

This only costs me $6 per month, using a DigitalOcean Droplet (VPS) (which is actually free, because the referral link I sometimes put when I link to them gets me plenty of free resources).

Note that I said DigitalOcean Droplet, not the DigitalOcean App Platform, mostly because App Platform starts at $5/m but does not come with a database, so you’ll need to bring your own, which DO provides under its Managed Databases offering, but it’s $15/m.

After this introduction, let’s now jump to the step-by-step explanation.

Create an account on DigitalOcean if you don’t have one yet.

Sign up for DigitalOcean with my referral link and you get $200 in credit for 60 days. This is an affiliate link: if you sign up through it and then spend $25, I get $25 in DigitalOcean credit.

Now create a new project.

In the sidebar click New Project.

Enter a name, for example “Analytics”.

Choose something in the “Tell us what it’s for” box, or you can’t create the project. Then click Create Project.

In the next panel click Skip for now.

The project is now created.

Now click “Spin up a Droplet”, which is DO’s terminology for VPS (Virtual Private Server).

This shows you the interface to create a new droplet.

Pick a region. It’s common to pick a region close to the majority of your users. As an EU person I want all data to stay in the EU for regulations reasons, so I picked an EU server.

Next you can choose an image. That’s what it’s deployed to the Droplet.

Click the Marketplace tab and search for “Docker”. Select it.

Alternatively you can also use another Linux Droplet and install Docker on it.

We’ll use Docker because Plausible is designed to be self-hosted through Docker.

It’s common for projects like this to offer a Docker installation, because they only have to manage one environment, and fewer things can go wrong for you.

If you’re new to Docker, don’t worry.

I’ll explain each step along the way.

Also, check my Docker introduction.

Now let’s go on. We need to pick a size for our droplet. Pay attention because DO really wants you to pick a fast one by default 😅 but pick “Regular” under CPU options instead.

Then click “Show all plans” to see all plans.

You should be able to pick the $4/m plan.

Right now as I’m making this tutorial it’s not available for some reason.

So I picked the $6/m plan, with 1 GB of RAM and 1 vCPU.

Now go down below.

I set up my authentication method using SSH keys, and if you already have that in place, great, use that. But I don’t want to make this tutorial too long, so we’ll just be using password auth.

Pick a password for the root user, and save it somewhere safe.

Finally, click Create Droplet.

It will take a little while to create the droplet.

Once it’s done you’ll see the IP address in the droplet page.

At this point you should create an A record in your DNS settings to point a subdomain of one of your domains to this IP address.

I won’t explain how, as it depends on what you use for your DNS. Check with your registrar.

For example on Cloudflare DNS it’s something like this:

Cloudflare DNS management interface showing A record creation for subdomain

In the droplet page click “Access console”, then Launch Droplet Console.

This opens a terminal in a new window:

Terminal window opened in browser showing command line interface for server access

We’re ready to configure our Droplet.

First, go in the /opt folder:

cd /opt

and clone the Plausible Community Edition setup. When I first wrote this post the Docker Compose files lived in the plausible/hosting repo. Since Community Edition v3 they live in plausible/community-edition, and the old repo redirects there. Clone the latest tagged release (v3.2.1 when I checked, in September 2026):

git clone -b v3.2.1 --single-branch https://github.com/plausible/community-edition plausible-ce

Docker Compose is a tool that allows you to create multiple containers for an application. Plausible needs several pieces to run: PostgreSQL, ClickHouse and the Plausible app itself.

Instead of creating multiple Dockerfiles, we get a compose.yml file that describes all of them. (If you need to write one of these for your own project, I built a free Docker Compose generator that sets up Postgres, MySQL or Redis with healthchecks.)

One thing to know: the Plausible README recommends at least 2 GB of RAM, because of ClickHouse. My instance runs on the 1 GB droplet, and it’s been fine for a blog. If you track a busy site, pick the 2 GB plan.

Then run cd plausible-ce to go into the newly created folder.

Now we need to create a .env file with the configuration. Two settings are required. BASE_URL is the final analytics URL, for example https://plausible.your-domain.com. SECRET_KEY_BASE is a random string, at least 64 bytes long, and openssl can generate it for you:

touch .env
echo "BASE_URL=https://plausible.your-domain.com" >> .env
echo "SECRET_KEY_BASE=$(openssl rand -base64 48)" >> .env

Note: use a subdomain and not a subfolder. Plausible won’t work as a subfolder.

Plausible listens on port 8000 as HTTP. Since Community Edition 2.1.2 it can also listen on ports 80 and 443 and get a Let’s Encrypt certificate on its own, and that’s what the official quickstart does now: add HTTP_PORT=80 and HTTPS_PORT=443 to .env, expose the two ports, done.

That option did not exist when I set up my instance, so I put Nginx in front of Plausible instead. It’s also what you want if the same server hosts other sites, since only one thing can own ports 80 and 443. The rest of the post goes through the Nginx route.

Add the port to .env:

echo "HTTP_PORT=8000" >> .env

and create a compose.override.yml file. The compose.yml file that comes with the repo doesn’t expose any port, and we don’t want to edit it, or the next git pull will conflict. Compose merges the override file on top of it:

cat > compose.override.yml << 'EOF'
services:
  plausible:
    ports:
      - 127.0.0.1:8000:8000
EOF

The 127.0.0.1 part is important. We make sure port 8000 only works for localhost connections, not for connections coming from the outside, and we’ll later set up Nginx to act as a reverse proxy on port 80.

Now run:

docker compose up -d

The Docker marketplace image ships the Compose plugin, so docker compose (with a space) works out of the box. The old standalone docker-compose command is not maintained anymore.

Docker pulls the PostgreSQL, ClickHouse and Plausible images, then creates and starts the containers. After a bit of downloading and processing, things should be up and running.

Wait a couple seconds to make sure things are up, then try accessing the server using

curl http://localhost:8000

and you should get a response back like this:

Successful curl response from localhost:8000 showing HTML content from Plausible

If you try too soon, you might get an error like curl: (56) Recv failure: Connection reset by peer, retry a little later.

Now it’s time to install Nginx!

Run the following commands:

apt update
apt install nginx

Then run:

ufw allow "Nginx Full"

Now run this to create the plausible.conf file in /etc/nginx/sites-available/

nano /etc/nginx/sites-available/plausible.conf

I use nano, but any editor works. Type this content, swapping YOUR_DOMAIN_HERE with your domain, like plausible.flaviocopes.com:

server {
    listen       80;
    listen       [::]:80;
    server_name  YOUR_DOMAIN_HERE;

    access_log  /var/log/nginx/plausible.access.log;
    error_log   /var/log/nginx/plausible.error.log;

    location / {
      proxy_pass http://127.0.0.1:8000;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /live/websocket {
      proxy_pass http://127.0.0.1:8000;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection "Upgrade";
    }
}

Using this configuration we listen on port 80 and we proxy all the requests to localhost:8000 where our Plausible instance is listening. The second location block passes the WebSocket connection the dashboard uses for live updates. It comes from the reverse proxy page of the Plausible wiki.

Now enable the site using:

ln -s /etc/nginx/sites-available/plausible.conf /etc/nginx/sites-enabled/

Test the Nginx config with

nginx -t

Nginx configuration test showing syntax is ok and test is successful

Then restart Nginx with

systemctl reload nginx

At this point if you open in your browser the URL you set, your should see Plausible working.

If it’s not working yet, you might have to wait for DNS to propagate (did you set up the domain DNS to point to the Droplet IP, right?). Also try a different browser, as DNS is often cached.

Note that we told Nginx to listen on that specific domain, so if you access the IP address you will see the default Nginx page, this is normal:

Default Nginx welcome page shown when accessing server by IP address

Note the “Not secure” words in the address bar however.. we need to set up secure connections.

Run:

apt install certbot python3-certbot-nginx

and:

certbot --nginx -d YOUR_DOMAIN_HERE

and follow the instructions.

Reload the page, your should have an automatic secure connection now!

Great!

Now you can enter your details and create your user account, then add your site details, the domain and its timezone.

Plausible then shows you how to install the tracking script on your site, and you’re ready to go.

When I first set this up, anyone who found the URL could create an account on my instance, and I had to close registration by hand. Community Edition fixed that: after the first user signs up, the registration page is closed by default, and new people can only join through an invite you send from the site settings.

If you want to change this, add a DISABLE_REGISTRATION line to .env. The default is invite_only. Set it to true to block registration completely, invites included, or to false to let anyone register:

DISABLE_REGISTRATION=true

Then recreate the containers so the change applies:

docker compose down --remove-orphans
docker compose up -d

(docker compose restart doesn’t pick up .env changes, in case you try)

That’s it.

I hope this tutorial can be helpful in setting up Plausible.

Years later that same instance needed a Docker, Postgres, ClickHouse, and Plausible upgrade, and Codex did it: Updating self-hosted Plausible Analytics using AI.

It’s a great tool. If you want a peek under the hood, I also wrote about how Plausible Analytics is built.

And don’t forget to become a sponsor of the project.

Want me to talk about your product? You can sponsor this site.

~~~

Related posts about tutorial: