Free analytics with Fathom on Fly.io
As I’m interested in the number of visitors my own websites are seeing, I need some basic analytics tooling. By setting up my own instance of Fathom Lite, the open-source version of Fathom Analytics, I think I can do this in a fairly privacy-preserving way.
Fathom Lite offers a pre-built Docker image. This is great if you don’t enjoy following installation instructions consisting of more than one bullet point.
But where to deploy this image? While being able to use AWS, Azure and Google Cloud in my client projects, I try to avoid them whenever possible. As I’m not really experienced with any of their Configuration as Code solutions, deploying a Docker image and assigning a custom domain would include 25 steps in either of their fun admin UIs.
Meet Fly.io:
Fly.io runs apps close to users. We transmogrify Docker containers into Firecracker micro-VMs that run on our hardware around the world, and connect all of them to a global Anycast network that picks up requests from around the world and routes them to the nearest VM.
They had me at transmogrify. But what I actually like the most about it: Deploying an image like the Fathom one takes about a minute (assuming flyctl has been installed and your are logged in).
Basic setup
-
Create the application:
flyctl launch --image usefathom/fathom
This will prompt us for a name and a region. The result is a configuration file with the following content:
fly.toml
app = "APPLICATION_NAME" kill_signal = "SIGINT" kill_timeout = 5 processes = [] [build] image = "usefathom/fathom" [env] [experimental] allowed_public_ports = [] auto_rollback = true [[services]] http_checks = [] internal_port = 8080 processes = ["app"] protocol = "tcp" script_checks = [] [services.concurrency] hard_limit = 25 soft_limit = 20 type = "connections" [[services.ports]] force_https = true handlers = ["http"] port = 80 [[services.ports]] handlers = ["tls", "http"] port = 443 [[services.tcp_checks]] grace_period = "1s" interval = "15s" restart_limit = 0 timeout = "2s"
-
Deploy the application:
flyctl deploy
We can access it via
APPLICATION_NAME.fly.io
. -
Now we want to assign a custom domain. We do that by adding a
CNAME
record to the domain with a value ofAPPLICATION_NAME.fly.io
1 and then creating a certificate:flyctl certs create CUSTOM_DOMAIN
-
Time to bring out the champagne: Our Docker image is deployed to our custom domain.
Configuration
As we don’t want our analytics data to be public (everyone would be jealous of our dozens of visitors per year), we need to create an admin user in Fathom. To do this, we connect via SSH and execute their script:
flyctl ssh console
> cp /app
> ./fathom user add --email="EMAIL" --password="PASSWORD"
Now we will be greeted with a login when accessing Fathom:
Data persistence
Our current setup has a tiny issue: The analytics data is not persistent. By default, the Fathom Lite image creates an SQLite database in /app/fathom.db
. So whenever the app is redeployed, we lose our data. But instead of hooking up a MySQL or Postgres database, we’ll just make the SQLite database is (semi-)persistent. Fly.io has a great solution for this: Volumes.
-
Let’s create a volume in the same region where we set up our app 2:
fly volumes create fathom_data --region REGION --size 1
-
Mount the volume into our image by adding the following lines to
fly.toml
:[mounts] source = "fathom_data" destination = "/app-temp"
-
Redeploy:
flyctl deploy
-
Copy everything from
/app
to/app-temp
:flyctl ssh console > cp -R /app/. /app-temp
This will copy the
fathom
executable and thefathom.db
SQLite database to our mounted volume. -
Change the mounting destination in
fly.toml
:[mounts] source = "fathom_data" destination = "/app"
-
Redeploy:
flyctl deploy
That’s it! Our data will live through redeployments.
Download database
Now the “semi” part of (semi-)persistent is really important. As written in their announcement:
Right now, for raw Fly volumes, resilience is your problem. There! I said it!
There are snapshots from the last five days. As I don’t care too much about my analytics data, I can live with this level of resilience. But I might create the occasional offline backup.
To back up something from our volume, we use scp
or a GUI like Transmit. If you are like me and did not read the WireGuard manual, you might want do this via a proxy:
-
Issue SSH credentials and create the proxy:
flyctl ssh issue --agent flyctl proxy 10022:22
-
Download SQLite database:
scp -P 10022 root@localhost:/app/fathom.db .
Now we are prepared for the occasional data loss.
Pricing
Fly.io’s free plan has got us covered here.
Random notes
- I ❤️ Fly.io. If an app requires more than what Vercel offers, I deploy it to Fly. They offer a great service and have a refreshing way of doing marketing and recruiting.
- I’m using their most basic functionality only. See docs for all the fancy things they can do.
- Their community Discourse is helpful. It is often the best way to find specific information as the documentation does not cover everything. Specific example: I could not find documentation on how to download data from a volume, but reading through a few Discourse posts helped me figure it out at some point. Important: Don’t forget to write how much you like DNSSEC and mention thomas, this will give you priority support.
Photo credits: FX Networks and Yarn.
Footnotes
-
See details and alternatives to CNAME in the documentation. ↩
-
In case you forgot: Use
flyctl status
or the Fly.io admin UI to find the region your app is currently deployed in. ↩