DEV Community

Vladimir Dementyev
Vladimir Dementyev

Posted on

DIY multi-regional uptime monitoring with Fly.io and Uptime Kuma

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 cookies dynos).

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:

Uptime Kuma 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
Enter fullscreen mode Exit fullscreen mode

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 🙂 
Enter fullscreen mode Exit fullscreen mode

In the resulting fly.toml file, update the service port (Uptime Kuma uses 3001 by default):

[[services]]
  http_checks = []
  internal_port = 3001
Enter fullscreen mode Exit fullscreen mode

And add the mounts section:

[mounts]
  source="kuma_data" # choose whatever name you want
  destination="/app/data"
Enter fullscreen mode Exit fullscreen mode

Now we need to create a volume manually:

fly volumes create kuma_data --region lhr --size 1
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

After the app has been deployed, you can open it and configure the monitors:

fly open
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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     
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
denym_ profile image
Denny Mueller

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.

Collapse
 
gedw99 profile image
Gerard Webb • Edited

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.

Collapse
 
pocheptsov profile image
Slava Pocheptsov

Hey @palkan_tula, how do you aggregate metrics across regions if you do it at all?

Collapse
 
palkan_tula profile image
Vladimir Dementyev

Hey!
I don't :) I rely only on notifications