Based on https://github.com/plausible/hosting
This repository allows you to get up and running with Plausible Analytics. It configures a reverse proxy with SSL using Let's Encrypt and sets up a crontab service for automatic backups to Cloudflare R2.
If you are not using Ubuntu, install docker engine: https://docs.docker.com/engine/install/. Otherwise, the script will do it for you.
Clone this repository to the server and navigate to the directory.
git clone https://github.com/opusdeits/plausible-hosting.git;
cd plausible-hosting;
Create plausible-conf.env at the root of this project, add these vars and set your actual values after the equal sign, no quotes or spaces. Check this if you have any doubts https://plausible.io/docs/self-hosting-configuration about any specific configuration.
SERVER_NAME var values are used to name dirs inside the bucket where backups will be uploaded and downloaded.
For the geolocation db, follow this link and create a license key https://www.maxmind.com/en/accounts/current/license-key, then paste it at MAXMIND_LICENSE_KEY
. For R2, follow this link https://developers.cloudflare.com/r2/api/s3/tokens/
An important var is FOLLOWER
. If it is set to true, this will make this server never backup the databases and restore from the configured RESTORE_FROM_SERVER_NAME
bucket folder. This is useful if you have two servers, one for receiving pageview events and another for making queries to the data.
Check the configuration examples at the end of this file for more details.
# plausible-conf.env
# Plausible settings
BASE_URL=
MAXMIND_LICENSE_KEY=
MAXMIND_EDITION=GeoLite2-City
DISABLE_REGISTRATION=invite_only
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
MAILER_EMAIL=
SMTP_HOST_ADDR=
SMTP_HOST_PORT=
SMTP_USER_NAME=
SMTP_USER_PWD=
SMTP_HOST_SSL_ENABLED=true
SMTP_RETRIES=2
# Let's Encrypt settings
LETS_ENCRYPT_EMAIL=
DOMAINS=
# R2 backup and restore settings
DISABLE_CRON_SCRIPTS= # to disable all cron automated scripts
BACKUP_TO_SERVER_NAME=
RESTORE_FROM_SERVER_NAME=
R2_BUCKET=
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_ENDPOINT=
# clouding.io settings
FOLLOWER=false # if set to true, this server will never backup. It will only do a restore from the configured SERVER_NAME bucket folder
CLOUDING_APIKEY=
FOLLOWER_TO_WAKEUP= # the ip of the follower server
AUTO_RESIZE_DISK=false # if set to true, the server will resize the disk before restoring if needed.
SECRET_KEY_BASE
Will be generated and put here later by the script itself. But you can place your own if you want.
Now that you have the env file it's time to run ./first-start.sh
the script will configure everything for you, providing all config vars are correctly setup in plausible-conf.env
You only need to run this the first time.
R2 is a Cloudflare service that is compatible with S3. The main difference is that R2 is cheaper and don't has egress fees. See more at R2 vs S3.
R2 api is compatible with S3, we even use aws-cli to connect to it. So, of course, you can use S3 instead. Just set the R2_ENDPOINT to the S3 endpoint and the R2_BUCKET to the S3 bucket name and the access and secret keys from S3.
You always need the plausible part of settings but all the other settings are optional. In the following examples we will skip the plausible settings and only show the optional ones.
There are basically three types of configurations:
- Minimal. A single server with plausible, no automated scripts.
- Single server with plausible and cron scripts for backups and restores and let's encrypt;
- Two servers, one for receiving pageview events and another for making queries to the data. Named as leader and follower. At least the follower server needs to be on clouding.io, because it needs to be woken up by the leader server. Nore on this later.
For local testing, the minimal configuration is enough. For a production server, with less than five million pageviews per month the single server configuration is ok. For more than that and if you don't want to expend 300$ per month, you should use the two servers configuration.
You can skip the first-start.sh script. The only thing you need is a SECRET_KEY_BASE
and the other plausible vars, and a nginx.conf file. There is a configuration example here: nginx-reverse-proxy/nginx.conf.example
. You can rename it to nginx.conf
, replacing localhost
with your domain and it will work.
# plausible settings
...
DISABLE_CRON_SCRIPTS=true
FOLLOWER=false
If you don't need SSL or you already have it configured, you can disable it on the first-start.sh script. It will ask you if you want to do so.
The crontab table used is in this repository, at crontab/table/cron
.
# plausible settings
...
[email protected]
DOMAINS="your_domain.com your_other_domain.com"
FOLLOWER=false
BACKUP_TO_SERVER_NAME=plausible-backups
RESTORE_FROM_SERVER_NAME=plausible-backups
R2_BUCKET=your_bucket_name
R2_ACCESS_KEY_ID=your_access_key
R2_SECRET_ACCESS_KEY=your_secret_key
R2_ENDPOINT=your_endpoint
This is my actual use case.
We have two roles, leader and follower. The leader receives the pageview events and it's always active. The follower is most of the time asleep and is the one responsible of making queries for stadistical reports. The leader will never automatically restore the databases, it will only make backups. Then, the follower will restore from the leader's backups to be always up to date.
The leader is configured as a single server plus some one more var, FOLLOWER_TO_WAKEUP
. This is the ip of the follower server. The leader will wake up the follower after a backup for the follower to restore from the leader's backups.
# plausible settings
...
[email protected]
DOMAINS="leader.your_domain.com leader.your_other_domain.com"
BACKUP_TO_SERVER_NAME=plausible-leader
RESTORE_FROM_SERVER_NAME=plausible-leader # this is needed for manual restores
R2_BUCKET=your_bucket_name
R2_ACCESS_KEY_ID=your_access_key
R2_SECRET_ACCESS_KEY=your_secret_key
R2_ENDPOINT=your_endpoint
FOLLOWER=false
CLOUDING_APIKEY=your_clouding_api_key
FOLLOWER_TO_WAKEUP= # the ip of the follower server
The follower will try to restore from the leader's backups on wake up if needed. It will also resize the disk before restoring if needed.
# plausible settings
...
[email protected]
DOMAINS="follower.your_domain.com follower.your_other_domain.com"
BACKUP_TO_SERVER_NAME=plausible-follower # this is needed for logs and manual backups
RESTORE_FROM_SERVER_NAME=plausible-leader
R2_BUCKET=your_bucket_name
R2_ACCESS_KEY_ID=your_access_key
R2_SECRET_ACCESS_KEY=your_secret_key
R2_ENDPOINT=your_endpoint
FOLLOWER=true
CLOUDING_APIKEY=your_clouding_api_key
AUTO_RESIZE_DISK=true # if set to true, the server will resize the disk before restoring if needed.
You have three options: use the clouding.io admin panel, use the clouding api or, if you use django, check out my other repository clouding-wake-up. It's a simple django app that wakes up a server when you press a button. It's useful if you have a django website and want to wake up the follower from it's admin.
Clouding.io is a simple cloud provider that allows you to choose the amount of CPU, RAM and disk space you need. You can resize it later, has persistent data storage and can be archived to save a lot of money. It's a good option for a follower server. It's also a good option for a leader server, but you will need to pay for the whole month, so a VPS is a cheaper option most of the time.
You can use another cloud provider, but you will need to fork this repository and change the wake up call at crontab/scripts/cron-backup.sh
and the sleep call at crontab/scripts/cron-archive.sh
to use the other provider's api.
This requires you to have configured R2 vars in plausible-conf.env
. Optionally, you can pass the R2 folder name as the first positinal argument, if not, it will use the current BACKUP_TO_SERVER_NAME
value.
From this folder run this:
docker compose exec crontab bash -c "/scripts/backup-db.sh"
or
docker compose exec crontab bash -c "/scripts/backup-db.sh your_folder_name"
WARNING!!!! This will destroy all current data and cannot be undone.
This will download the last backup of both databases, drop the existing tables and then restore. Optionally, you can pass the R2 folder name as the first positinal argument, if not, it will use the current RESTORE_FROM_SERVER_NAME
value.
docker compose exec crontab bash -c "/scripts/restore-db.sh"
or
docker compose exec crontab bash -c "/scripts/restore-db.sh your_folder_name"
API keys have a default limit of 600 requests per hour. If you want to change it to the maximum value, do the following:
docker compose exec plausible_db psql -U postgres -d plausible_db -c 'UPDATE api_keys SET hourly_request_limit = 2147483647'
That will set the limit to the highest possible value.