Two things happened recently that led to this post:
- Heroku had a DNS problem (incident#2453, and many websites went down.
- Heroku announced their next chapter (spoiler: no more free
cookiesdynos).
I was affected by both (well, the second one has not come into effect yet). And I found this set of circumstances an excellent opportunity to try something new.
Introducing Uptime Kuma
The first event made me think of setting up an uptime monitoring (with DNS checks support), and an OSS project bubbled in my mind—Uptime Kuma.
Uptime Kuma calls itself a "fancy monitoring tool". And I absolutely agree with that. It has both a great feature set and a slick UI. Here is my dashboard:
You can find the complete list of features in the repo; I was only interested in the basic HTTP and DNS checks. As for notifications, there are dozens of integrations. I chose Slack for simplicity and plan to add a Telegram integration for personal projects later.
What about deployment? There is an official Docker image (louislam/uptime-kuma
), so all you need is a platform capable of spinning up Docker containers and taking care of them. Heroku could be a candidate, but I prefer not to pay for pet/research projects. So, I turned to the alternatives.
Deploying Uptime Kuma to Fly
Fly.io is a modern deployment platform focused on multi-regional availability ("close to your users", their main page says). By "modern", I mean providing a great developer experience: CLI-first, sensible defaults and configuration, and a generous free tier.
You can deploy a Docker image to Fly with a single command:
flyctl launch --image louislam/uptime-kuma:1
That could be it for some apps, but for Uptime Kuma we need persistent storage to keep configuration and application settings (such as admin username and password). For that, we can use volumes.
Let's first create an application to generate the default fly.toml
file:
fly create
# answer the questions, don't install PostgreSQL 🙂
In the resulting fly.toml
file, update the service port (Uptime Kuma uses 3001 by default):
[[services]]
http_checks = []
internal_port = 3001
And add the mounts section:
[mounts]
source="kuma_data" # choose whatever name you want
destination="/app/data"
Now we need to create a volume manually:
fly volumes create kuma_data --region lhr --size 1
We use the smallest possible size (1 GB), which is too much for Uptime Kuma, but fly
doesn't allow passing floats 🤷♂️
Now, we're ready to deploy our application!
fly deploy
After the app has been deployed, you can open it and configure the monitors:
fly open
Going multi-regional
I decided to go further and turn my DIY monitoring into a high-available solution by creating a second application instance in another region.
The tricky part here is dealing with volumes: a volume could only be attached to a single application instance. So it's just a private storage, non-shared.
If we want to deploy more instances, we must create new volumes. Let's create one in Chicago:
fly volumes create kuma_data --region lhr --size 0.1
We can check the status of our volumes by running the following command:
$ fly volumes list
ID STATE NAME SIZE REGION ATTACHED VM
vol_xxxx created kuma_data 1GB lhr abc123
vol_yyyy created kuma_data 1GB yyz
Now, we need to add a new region to our application. First, I tried to follow the documentation and add a new region to the pool but failed:
$ fly regions add yyz
Error App 'uptime_kuma' uses volumes to control regions. Add or remove volumes to change region placement.
The error message itself wasn't really helpful (I created a volume, what's wrong?), but it led me to the community discussion which explains that you don't need to add regions manually when using volumes. All you need is to scale the app, and Fly will choose a proper region based on the free volume:
fly scale count 2
Awesome! Now we have two apps. But they are independent; how can we sync the configuration?
To deal with configuration syncing, I decided to use the built-in Backups features of Uptime Kuma: export in one region and import into another.
It turned out that there is no way to access an app deployed in the specific region, Fly takes care of geolocation-based routing itself, and you have no control over it. Solution? VPN!
So, here is the step-by-step instruction on how to "sync" Uptime Kuma instances:
- Open the app closest to your current location:
fly open
. - Download the configuration backup.
- Connect to a VPN server closer to the second location and run
fly open
again. - During the first launch, a fresh Uptime Kuma instance prompts you to enter admin login details again—just use the same as for the first app.
- Go to Backups, choose the "Overwrite" option and upload a dump from the first app. That's it!
It took me about ~30 minutes to run a multi-regional monitoring system on Fly.io, and I hope it will last for years until Fly hits its next chapter forcing me to look for an alternative 🙂
Top comments (4)
Interesting post. I wonder how this at all runs on fly.io ...
The custom Dockerfile the fly cli creates in the kuma project does not run since it uses yarn.
Plus the port change would break the whole thing completely since the fly.toml sets also an env var for the port which is then used on server start.
I assume you work in the repository clone of kuma since you mentioned to deploy it with
fly deploy
(no args) which only should work from the project itself.You could also use nats perhaps KV store
I don't know the API to kuma data store though so have to grok that
run nats embedded on each, or a global one.
here is the golang code interact with the nats kv store and ensure that the data will pop up in the other region.
github.com/mprimi/nasefa is the cli
its flexible enough to pipe things through it
Update: I take it all back - there is a much easier way. Uptime numa use Sqlite, and fly has Sqlite replication using LiteStream. I am highly confident this will work
You can also do Sqlite replication with NATS and Marmot that support multi master replications. Marmot and NATS are golang and a breezy to embed in the Docker file you deploy to Fly.
Hey @palkan_tula, how do you aggregate metrics across regions if you do it at all?
Hey!
I don't :) I rely only on notifications