GoatCounter

GoatCounter is an Open Source Analytics written in Go.

Why self host?

GoatCounter offers a free hosted version for small and hobby websites.
One issue however occurs when users with an adblocker access the website, as access to external websites is often blocked.
By hosting GoatCounter yourself on a different subdomain, most adblockers will allow the request to go through.

GoatCounter most likely does not require a GDPR consent notice.

Installation

GoatCounter is distributed as a statically compiled binary.
Releases can be found on GitHub

As GoatCounter is a separate binary, I'm using the path /usr/local/bin with a new folder called goatcounter as the destination.
The binary file will be moved after downloading, in order to avoid modifying the systemd service after every update.

mkdir /usr/local/bin/goatcounter
cd /usr/local/bin/goatcounter
wget https://github.com/arp242/goatcounter/releases/download/v2.4.1/goatcounter-v2.4.1-linux-amd64.gz
gzip -d goatcounter-v2.4.1-linux-amd64.gz
mv goatcounter-v2.4.1-linux-amd64 goatcounter

The extracted binary needs to be marked as executable.

chmod +x goatcounter

GoatCounter needs write permissions for the directory it is located in to create its SQLite database on startup.
As I will be using a reverse proxy in front of it, I need to add the following two parameters when executing GoatCounter.

-listen :8888: Set listening Port to 8888 for use with a reverse proxy
-tls http: By default GoatCounter tries to generate a certificate using ACME. This option forces unencrypted HTTP

Run GoatCounter once to generate the necessary database files.

./goatcounter serve -listen :8888 -tls http

Create site

To use GoatCounter, a new site needs to be defined.
You will be prompted for the user password.

-vhost: Domain used for GoatCounter
-user.email: Login email address
./goatcounter db create site -vhost {URL} -user.email {EMAIL}

Systemd Service

Create a service file at /etc/systemd/system/goatcounter.service
The WorkingDirectory option can be changed to control where you want to create the SQLite database file.
Do note, that you will have to specify the database location using -db 'sqlite+{PATH TO DB}.sqlite3 when running the binary directly.

[Unit]
Description=GoatCounter Service
After=network.target

[Service]
Type=simple
WorkingDirectory=/usr/local/bin/goatcounter
ExecStart=/usr/local/bin/goatcounter/goatcounter serve -listen :8888 -tls http

[Install]
WantedBy=multi-user.target
systemctl daemon-reload
systemctl enable --now goatcounter

Nginx config

server {
    server_name goat.exu.li;

    # Security / XSS Mitigation Headers
    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-XSS-Protection "1; mode=block";
    add_header X-Content-Type-Options "nosniff";
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains";

    location / {
        # Proxy main traffic
        proxy_pass http://172.18.50.105:8888;
        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_set_header X-Forwarded-Protocol $scheme;
        proxy_set_header X-Forwarded-Host $http_host;
    }

    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    ssl_certificate_key /etc/acme-sh/goat.exu.li/key.pem;
    ssl_certificate /etc/acme-sh/goat.exu.li/cert.pem;
}

server {
    if ($host = goat.exu.li) {
        return 301 https://$host$request_uri;
    }

    listen 80;
    listen [::]:80;
    server_name goat.exu.li;
    return 404;
}