How to self-host Bearlytics with Kamal

| · @kyrylo · bluesky:@kyrylo.org · mastodon:@kyrylosilin

In this article, I’ll guide you through the process of self-hosting Bearlytics with Kamal. It’s a bare-bones alternative to Google Analytics (and even Plausible Analytics - see my installation guide with Kamal).

I saw it online and immediately knew it was my kind of jam:

  • low memory footprint
  • runs on SQLite
  • simple to use and maintain
  • lightweight tracking script

It’s designed to be self-hosted, and I love simple self-hosted products (I develop them myself).

I come from the Ruby ecosystem and prefer deploying with Kamal. Its configuration file looks almost identical to docker-compose.yml, which Bearlytics already ships with, so using Kamal felt like a natural fit.

Requirements

Hardware

Bearlytics will run on virtually any VPS or hardware. My running container, with no traffic, consumes only 56 MiB of RAM. Impressive!

CONTAINER ID   NAME                                                            CPU %     MEM USAGE / LIMIT    MEM %     NET I/O           BLOCK I/O         PIDS
0db0cbf3fba1   bearlytics-web-9a7059c5bfe6d403d8bf2de40eaf845956ce69ea         0.02%     55.87MiB / 7.57GiB   0.72%     269kB / 582kB     147kB / 13.8MB    3

Go to Hetzner Cloud and grab the cheapest plan (you’ll be totally fine).

Subdomain

To self-host Bearlytics, you’ll need a subdomain, for example analytics.example.com.

On Cloudflare, create an A record pointing to your server’s IP address. This will allow you to access Bearlytics at https://analytics.example.com.

Step-by-step guide

To understand what we’ll be doing, let’s take a look at the docker-compose.yaml file from the repository and translate it into a Kamal configuration.

Bearlytics has no external dependencies: it’s a single container. All we need to do is set a few environment variables and we’re good to go.

Step 1: Clone the Bearlytics repo

Start by cloning the repository:

git clone --single-branch https://github.com/HermanMartinus/bearlytics bearlytics
cd bearlytics

Step 2: Branch out

We don’t want to accidentally push these changes upstream. We’ll be working with our own configuration values, and it’s unlikely the Bearlytics author would want this opinionated setup merged.

Create a new Git branch:

git checkout --b kamal

Step 3: Generate Kamal configuration:

Install Kamal if you don’t already have it (via RubyGems):

gem install kamal

Then generate the Kamal configuration:

kamal init

You should see output similar to this:

Created configuration file in config/deploy.yml
Created .kamal/secrets file
Created sample hooks in .kamal/hooks

We won’t need hooks, so delete that directory:

rm -rf .kamal/hooks

Step 4: Update .gitignore

Next, add the generated files to .gitignore so we don’t accidentally leak secrets:

# .gitignore
!.kamal
!.kamal/secrets
!.config
!config/deploy.yml

Step 5: Setting up Kamal secrets

Bearlytics requires several mandatory environment variables:

  • SECRET_KEY: Django secret key
  • SALT_SECRET: Secret key used to hash PII
  • DEBUG: Set to False in production
  • DB_PATH: SQLite database location (default: /app/db/db.sqlite3)
  • ALLOWED_HOSTS: Comma-separated list of allowed hosts
  • CSRF_TRUSTED_ORIGINS: Comma-separated list of trusted origins

We’ll define most of them in the config/secrets file generated by Kamal. It should look like this:

DEBUG=$DEBUG
SECRET_KEY=$SECRET_KEY
SALT_SECRET=$SALT_SECRET
DB_PATH=$DB_PATH
CSRF_TRUSTED_ORIGINS=$CSRF_TRUSTED_ORIGINS

Now we need to populate these values from the environment. I’ll use a .env file and the dotenv Ruby gem (gem install dotenv). Kamal also supports several other approaches, which are covered in the documentation.

  1. Generate SECRET_KEY and SALT_SECRET using any password generator. openssl works great.
  2. DB_PATH refers to the path inside the Bearlytics Docker container.
  3. CSRF_TRUSTED_ORIGINS should point to your installation URL.

Here’s how .env will look like:

# .env at the bearlytics root directory
SECRET_KEY=$(openssl rand -base64 48)
SALT_SECRET=$(openssl rand -base64 48)
DB_PATH="/app/data/production.sqlite3"
CSRF_TRUSTED_ORIGINS="https://analytics.example.com"

That’s it for secrets. Now we just need to tell Kamal where and how to deploy the app.

Step 6: Update Kamal configuration

We’re almost there. Let’s update the Kamal configuration file. I’ll walk you through the important sections and then provide the full config at the end.

First, set the service name and image:

service: bearlytics
image: bearlytics

Next, define the servers. We’ll use a single server named web:

servers:
  web:
    - <your-server-ip>

Now configure kamal-proxy to enable SSL. We’ll use the subdomain created earlier.

Important: Bearlytics runs on port 8000, but kamal-proxy assumes port 3000 by default, so we need to override that.

proxy:
  ssl: true
  host: analytics.example.com
  app_port: 8000
  healthcheck:
    path: /script.js

We also define a health check that hits /script.js. By default, Kamal pings /up, which Bearlytics doesn’t implement. /script.js is guaranteed to exist because it’s the tracking script itself.

Next, we’ll build the image locally and use Kamal’s local registry feature so we don’t need to push anything to GHCR or Docker Hub:

registry:
  server: localhost:5555

Wire up the environment variables:

env:
  clear:
    DEBUG: false
  secret:
    - DB_PATH
    - SALT_SECRET
    - SECRET_KEY
    - CSRF_TRUSTED_ORIGINS

Now mount a volume for the database so we get persistence and can make backups if needed:

volumes:
  - "/root/bearlytics-storage:/app/data"

Create the directory on your VPS and adjust permissions:

mkdir /root/bearlytics-storage
chown -R 1000:1000 /root/bearlytics-storage

Finally, specify the build architecture:

builder:
  arch: amd64

Here’s the full configuration for convenience:

service: bearlytics
image: bearlytics

servers:
  web:
    - <your-server-ip>

proxy:
  ssl: true
  host: analytics.example.com
  app_port: 8000
  healthcheck:
    path: /script.js

registry:
  server: localhost:5555

env:
  clear:
    DEBUG: false
  secret:
    - DB_PATH
    - SALT_SECRET
    - SECRET_KEY
    - CSRF_TRUSTED_ORIGINS

volumes:
  - "/root/bearlytics-storage:/app/data"

builder:
  arch: amd64

To verify everything is wired correctly, run:

% kamal config

Wonderful! If everything looks good, commit your changes:

git add .
git commit -m "Add Kamal configuration"

Step 7: Deploy Bearlytics

I deployed Bearlytics on a server that was already provisioned with Kamal. If you’re using a fresh server, you’ll probably want to run kamal setup first.

In my case, the server was already running Kamal-managed containers, so I simply ran:

% dotenv kamal deploy

The deployment should take about a minute.

Once it’s done, visit https://analytics.example.com and you should see the Bearlytics sign-up page.

Congratulations, you’ve just self-hosted Bearlyrics with Kamal! I’m proud of you!


Prev: