How to Self Host Plausible Analytics
I was in the process of setting up a few projects recently and wanted to try out using Plausible analytics rather the standard Google Analytics and I got a bit stuck configuring https
access so thought I would document the process here.
There’s a great get started guide that Plausible provide by they kinda skim over the setup of a reverse proxy and SSL certificate so you can access the app over https
so I’m hoping to fill in the blanks here.
Things you’ll need #
You’ll need a server and a domain name pointed at said server (I did a video covering this a while back) and also git
and docker
available.
Login to server and get the Plausible code #
So the first thing I did was to actually get the Plausible code onto the server and, straight out of there installation guide, this is just a case of cloning the repository.
ssh user@domain
cd ~
git clone https://github.com/plausible/hosting plausible
I put this in the user’s home directory so I can easily find the app code later on.
Next, the installation guide asked me to update a bit of config before spinning up the Docker containers.
One bit of the information is a secret key which they also provide a handy command for.
openssl rand -base64 64 | tr -d '\n' ; echo
I then set these bits of info in the plausible-conf.env
file.
nano plausible-conf.env
BASE_URL=https://<your-domain>
SECRET_KEY_BASE=<from-above>
I set the BASE_URL
to the https
version of the domain I’m going to be hosting the analytics on and I discovered later on that this can’t be a path e.g. https://<your-domain>/stats
or something as the login pages etc. don’t get updated.
Spin up the Docker containers and test it out #
As Plausible is setup in containers the next step is to get them all up and running and as there is a docker-compose
file, this can be done with one command.
sudo docker-compose up -d
The default config in the docker-compose
file is to expose Plausible globally on port 8000
.
So as my firewall allows it, I was able to see the Plausible login page at http://domain.com:8000/
I didn’t setup any accounts yet though, first I wanted to setup the reverse proxy with nginx and secure the login with an SSL cert.
Create a basic nginx config #
So the next thing I wanted to do was to ‘hide’ the exposed port for Plausible and put this behind nginx and then ultimately set up the secure connection.
This is the bit that the installation instructions fall a bit short on which, to be fair, it’s not really their job to tell you how to configure proxies and servers etc!
Anyway, I decided to get nginx up and running in a separate Docker container but first, I created a simple nginx config to handle traffic on port 80
and pass requests to Plausible.
server {
listen 80;
server_name <your-domain-name>;
resolver 127.0.0.11;
location ~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
proxy_pass http://plausible:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
So adding the proxy_pass
will ensure any requests are passed to Plausible and I also setup a separate location
for the certbot certificates which we’ll sort out in a moment.
It’s important to set the X-Forwarded-For
header to ensure the right IP address of a visitor is passed to Plausible for the Geolocation lookups.
If you add the Docker DNS resolver you can simply reference the Plausible app container by name (http://plausible:8000
) which can be updated in the docker-compose
file.
## Updating docker-compose with nginx container
I did play around with using a separate Docker image and docker-compose
for the nginx stuff but then I thought, they’re all linked together so might as well use the existing docker-compose
file that came with the Plausible code.
So I added an nginx image that references the config file created above and networked the containers together.
The docker-compose
file then looked a bit like this:
version: "3.3"
services:
# Adding the nginx image here
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./certbot/www:/var/www/certbot
- ./certbot/conf:/etc/letsencrypt
networks:
- plausible
mail:
image: bytemark/smtp
restart: always
networks:
- plausible # Network containers
plausible_db:
# supported versions are 12, 13, and 14
image: postgres:14-alpine
restart: always
volumes:
- db-data:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=postgres
networks:
- plausible # Network containers
plausible_events_db:
image: clickhouse/clickhouse-server:22.6-alpine
restart: always
volumes:
- event-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 containers
plausible:
image: plausible/analytics:latest
container_name: plausible # Make sure this container has a name and matches what you have in nginx.conf
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:
- 127.0.0.1:8000:8000 # Adding 127.0.0.1 will stop Plausible being exposed globally
env_file:
- plausible-conf.env
networks:
- plausible # Network containers
volumes:
db-data:
driver: local
event-data:
driver: local
geoip:
driver: local
# Add the network
networks:
plausible:
driver: bridge
In the notes in the snippet above you’ll see I added a container_name
to the Plausible container to ensure the DNS is resolved for the nginx.conf
file and also set the exposed port to be restricted to the localhost rather than making it available publically on the domain at port 8000
.
Let’s recreate all the containers.
sudo docker-compose up -d --force-recreate
All being well, you should see the Plausible login screen by simply browsing to your domain and of course the port 8000
route should be shut off.
But of course, at this point I was still working with an unsecure connection so I just needed to add a Let’s Encrypt certificate.
Adding an SSL certificate #
To add an SSL certificate I used the official certbot image and you need to configure some volumes to point to a folder in your nginx setup.
certbot:
image: certbot/certbot
container_name: certbot
volumes:
- ./certbot/conf:/etc/letsencrypt
- ./certbot/www:/var/www/certbot
command: certonly --webroot -w /var/www/certbot --force-renewal --email <your-email> -d <your-domain> --agree-tos
You also need to make sure the nginx container is setup to have these volumes that match what certbot is expecting.
nginx:
image: nginx:alpine
container_name: nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./certbot/www:/var/www/certbot # Adding these two lines
- ./certbot/conf:/etc/letsencrypt # to match the certbot volumes
networks:
- plausible
Then we need to recreate the nginx container to use the new volumes.
sudo docker-compose up -d --force-recreate nginx
I would then probably give it a minute or two as I found spinning up the certbot container straight away seemed to fail as the challenges couldn’t be found by Let’s Encrypt (maybe the nginx volumes weren’t quite ready).
sudo docker-compose up -d certbot
If all is good at this point we can add the secure connection in nginx.conf
.
Here’s what the nginx file should look like:
server {
listen 80;
server_name <your-domain>;
resolver 127.0.0.11;
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
ssl_certificate /etc/letsencrypt/live/<your-domain>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<your-domain>/privkey.pem;
server_name <your-domain>
location ~ /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
proxy_pass http://plausible:8000;
}
}
You could just remove the unsecure server block but I decided to change this to redirect any non-https requests to the secure server block.
So with that in place I just needed to recreate the nginx container to get the new config.
sudo docker-compose up -d --force-recreate nginx
Once that was up and running, I could then access the Plausible login with a secure connection!
Creating an account #
So this is more about using the software but once I created an account there’s one final bit of config I did on the server.
With an account created you can then add a site which gives you the <script>
tag that you can then emebd on that site to start sending analytics data.
The last bit of config I did though once I was setup is to stop any further user registrations in Plausible otherwise anyone would be able to sign up and add their own account!
To do this we just need to update the plausible-conf.env
file and add an additional configuration param.
DISABLE_REGISTRATION=invite_only
You can [https://plausible.io/docs/self-hosting-configuration](turn this off completely) but I decided to keep it invite only so I could possibly add additional users in the future.
Still stuck? Check out the video for more detail!