DIY multi-regional uptime monitoring with Fly.io and Uptime Kuma
Tags: monitoring, fly, tutorial
Published: 2022-08-31 22:03:45

Two things happened recently that led to this post:
- Heroku had a DNS problem ([incident#2453](https://status.heroku.com/incidents/2453), and many websites went down.
- Heroku announced their [_next chapter_](https://blog.heroku.com/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][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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5dub6b4vo3gavvgqugfx.png)

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][fly] 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:

```sh
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](https://fly.io/docs/reference/volumes).

Let's first create an application to generate the default `fly.toml` file:

```sh
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):

```toml
[[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:

```sh
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!

```sh
fly deploy
```

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

```sh
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:

```sh
fly volumes create kuma_data --region lhr --size 0.1
```

We can check the status of our volumes by running the following command:

```sh
$ 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](https://fly.io/docs/reference/scaling/#modifying-the-region-pool) and add a new region to the pool but failed:

```sh
$ 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](https://community.fly.io/t/deploying-redis-image-to-multiple-regions/6324/2) 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:

```sh
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 🙂

[kuma]: https://github.com/louislam/uptime-kuma
[fly]: https://fly.io