Setup GoatCounter on VPS

After starting my blog for a while, I have always wanted to integrate an analytics solution to see how people use my website. Google Analytics is the first thing that crossed my mind since I’m using it at work, but I want to use something that is more privacy-friendly and allows me to control my data (Google Analytics doesn’t unless you buy Google Analytics 360, which cost $150,000/year).

And then I found GoatCounter. After reading it’s feature and the reason why it was developed, I decided to give it a try.

GoatCounter

GoatCounter is an open-source web analytics platform. It’s available as a hosted service (free for non-commercial use) or self-hosted app.

Main features:

  • Privacy: doesn’t track users with unique identifiers and doesn’t need a GDPR notice
  • Lightweight and fast
  • Open-source
  • Own your data
  • And other features

You can use GoatCounter as a service, they provide a free plan for non-commercial use. But in this guide, I will focus on how to use it on your own server, especially with a proxy (Apache in my case).

Before installing, there is one thing you should know: GoatCounter needs a subdomain to work with (for example: stats.ckhang.com), so prepare one if you haven’t.

Installing

Download GoatCounter binary from Github:

1
curl -L https://github.com/arp242/goatcounter/releases/download/v2.2.3/goatcounter-dev-linux-amd64.gz -o goatcounter.gz

It comes in a compressed files so we need to extract it:

1
gunzip goatcounter.gz

1.png

By default, the file doesn’t come with execution permission, we can change that using chmod:

1
chmod +x goatcounter 

2.png

We just need to move it /usr/local/bin/ folder to make the command available in the whole system:

1
sudo mv goatcounter /usr/local/bin/goatcounter

Verify the installation:

1
goatcounter version

3.png

Configuration

First, you must create a database to store data. GoatCounter supports PostgresQL and SQLite. I will use SQLite since it’s easier to set up. If your site has high traffic, then you should consider using Postgres for better performance.

Run this command, remember to use your subdomain and your email:

1
goatcounter db create site -vhost stats.example.com -user.email me@example.com -createdb

Then you can try to run GoatCounter:

1
goatcounter serve

This will create a default database in ./db/goatcounter.sqlite3. Since GoatCounter listens on port *:80 and :443 by default, you may see this error message:

4.png

This is because I’m running Apache on *:80 and :443. So I need to switch to another port:

1
goatcounter serve -listen localhost:5000 -tls http -public-port=5000

5.png

If you want to run GoatCounter on a different port (rather than default *:80 and :443), make sure you have checked the documentation with goatcounter help listen. The author described in detail what you have to do.

GoatCounter is online now, but you cannot access it with stats.example.com since we have to point the subdomain to GoatCounter server.

Subdomain setting

Here I created a new virtual host for my subdomain:

1
2
3
4
5
6
7
8
9
ServerName stats.example.com
ServerAlias www.stats.example.com

ProxyPass / http://localhost:5000/
ProxyPassReverse / http://localhost:5000/

ErrorLog /var/log/httpd/stats.example.com-error.log
CustomLog /var/log/httpd/stats.example.com-access.log combined

Now go to stats.example.com, you should see the login screen like this:

6.png

Now try to login with the email/password which you set up in the previous step, you should be able to login. If you are using a proxy, you may see a phenomenon where the page just refreshes after pressing login button. In my case, I checked Apache error log and this was the reason:

Permission denied: AH00957: HTTP: attempt to connect to 127.0.0.1:5000 (localhost) failed

By default, SELinux prevents Apache from initiating outbound connections, you can change the setting by running the command below:

1
/usr/sbin/setsebool -P httpd_can_network_connect 1

That should fix it, now try to login again:

7.png

You can see GoatCounter dashboard with your website statistics.

Final step is insert the script file to your page. You can just copy from the dashboard and put it in your website:

1
<script data-goatcounter="https://stats.example.com/count" async src="//stats.example.com/count.js"></script>

Websocket problem

If you are using a proxy, you may see an error where GoatCounter cannot connect via websocket, which leads to the charts in dashboard can not be displayed

8.png

You can see the error in console…

9.png

and in log

10.png

This is because we haven’t configured the proxy for websocket.

According to the author, since lots of people are having problems with the websocket, he has made it optional (off by default). I’m still using older version so it’s not applicable, hence I will try to configure Apache to work with websocket.

Update: May 28, 2022 regarding Websocket problem

I finally found a way to forward websocket connection in Apache. In Virtualhost setting, just use the config below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
RewriteEngine on
RewriteCond %{HTTP:Connection} Upgrade [NC]
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteRule .* ws://localhost:5000/loader [P,L]

ProxyPass / http://localhost:5000/
ProxyPassReverse / http://localhost:5000/

Header add Origin "http://localhost:5000/"
RequestHeader set Origin "http://localhost:5000/"

Note that you need the last 2 lines. Since we are passing from stats.example.com to localhost, you will see ERROR: websocket: request origin not allowed by Upgrader.CheckOrigin in your log if you don’t rewrite request origin.

Now you can see all the charts in dashboard: 11.png

Final thoughts

Basically, that’s all you need to do if you want to use GoatCounter on your server. Since GoatCounter is designed to be runnable “out of the box”, it will take a few steps to configure the server. Websocket is not running but you still can see a part of your data on dashboard (all data if you access SQLite directly) updated websocket config on May 28, 2022.

References:

GoatCounter repo

Replacing Google Analytics with GoatCounter

Apache Module mod_rewrite

Apache Module mod_proxy