Solving the one thing that keeps me up at night (in software dev)

Solving the One Thing That Keeps Me Up at Night in Software Development
One of the only real things in software development that keeps me up at night is downtime—sometimes quite literally, as anyone who's received a 2 a.m. PagerDuty alert knows all too well.
Fortunately, when it comes to my deployed applications, downtime isn't that common of an occurrence. However, because I've been increasing my use of tools such as Claude Code, where I'm effectively trading stability for speed, the likelihood of one of my applications suddenly going offline has substantially risen.
Because of this—and because I want to be able to sleep at night without constantly worrying about whether or not my applications are experiencing any downtime—I've been taking steps to ensure that I'm informed whenever this is the case.
One of the best ways to achieve this is to make use of an uptime monitoring tool, which notifies you whenever downtime is detected on a configured web application, allowing you to take action before it affects too many users.
Why Uptime Kuma?
Whilst there are a number of different products out there for uptime monitoring, there's one that I've been using quite a lot recently that I would consider to be almost perfect. It not only supports nearly everything that I need when it comes to uptime monitoring, but it also happens to be:
- Open-source
- Self-hostable
- And if I'm being completely honest, it looks great
This service is Uptime Kuma, which I've been using to monitor all of my production web applications for the past few months.
However, that's not all I've been using it for. It provides a number of other features that make it incredibly useful for monitoring your infrastructure stack, including:
- API endpoints
- Database instances such as Postgres, MySQL, or Redis
- VPS firewall configurations
- DNS records
- TLS certificates
- Individual Docker containers
If that wasn't good enough, Uptime Kuma also provides a status page which is linked to all of your uptime monitors, allowing you to communicate any maintenance windows or downtime events with your users.
All of this makes Uptime Kuma almost the perfect uptime monitoring tool. And whilst there are a couple of features that I do find missing, overall it's an absolutely fantastic service, especially as you can host it yourself.
So let's go ahead and take a look at not only how easy it is to deploy an instance of Uptime Kuma, but I'll also show you how I like to configure it for my own application stack, including a couple of monitors I like to configure for my own infrastructure needs.
Installation
In order to deploy an instance of Uptime Kuma, there are a couple of different approaches we can take, laid out in the project's readme:
- Deploy using the pre-built Docker image — pretty much expected for any open source service these days
- Run it directly using something like PM2 — since it's a Node application
Personally, I'm a fan of using Docker, typically with a deployment platform such as Dockploy. However, because it's a good idea to deploy monitoring software onto different infrastructure than it's actually monitoring, I'm instead going to take a different approach and use a Docker-based feature provided by my VPS provider, Hostinger.
Using Docker Manager
This feature is their new Docker Manager, which allows you to deploy and manage a Docker Compose deployment all from within the Hostinger UI. Docker Manager is available for any Hostinger VPS instance that has been set up using the Docker ISO image, which is available to select during the VPS's initialization. This means it's possible to deploy and manage your application stack using Docker Compose without ever needing to SSH in.
I'm going to deploy the following Docker Compose, which deploys a container for Uptime Kuma version 2.0 beta as well as an instance of Traefik to work as a reverse proxy.
If you want to deploy this yourself, you can find a link to the compose.yaml on GitHub, as well as some instructions on what you need to change in order to set it up for your own configuration.
When it comes to deploying a Docker Compose stack using Docker Manager, you can do this a couple of different ways:
- Visual editor — allows you to define containers manually through the user interface, making it a great option for beginners or those who are more adverse to YAML
- YAML editor view — allows you to define the Docker Compose stack using YAML directly
Additionally, if you happen to have a Docker Compose file already available, you can either paste it into the YAML editor, or better yet, paste the URL of the Docker Compose file directly in, provided it's hosted on the internet. This is the approach I'm taking—pasting in the URL and pressing deploy.
Uptime Kuma Setup
Once the Uptime Kuma stack has been deployed, let's finish setting it up before configuring our first application monitor.
Head over to the hostname configured for your instance (defined inside the docker-compose.yaml). You'll be greeted with a page asking you to choose which database you want to use:
- Embedded MariaDB
- External MySQL/MariaDB
- SQLite
If this was a production setup, I'd probably choose an external MariaDB instance for failover or shared redundancy. However, for a single instance, I'm going with Embedded MariaDB as it provides a bit more of a performance boost compared to SQLite (although SQLite will work as well).
Upon selecting this database, you're brought to another page to set up an admin account.
Note: It's worthwhile to do this using HTTPS, which I have set up through the use of my reverse proxy in Traefik. You can change this to use one of your own domains by modifying the relevant lines inside of the Docker Compose and adding the relevant DNS record to point to your VPS's IP.
After creating your admin account with a username and password, you'll be logged into Uptime Kuma's admin panel where we can begin adding our first uptime monitor.
A Simple Monitor
The first uptime monitor we're going to add is going to be the most simple. Don't worry—we're going to add in some more advanced ones later, as well as showing a couple of interesting ones that I like to use.
For this one, we just want to ensure that a website is up and accessible at its current domain name. The website I'm going to configure is Zenprompter, a simple web application that allows me to run scripts for a teleprompter, which I can use with my phone or on my desktop. This app is very niche, but it perfectly suits my needs. I want to make sure this service is always accessible, and in the event that it does go down, I'll at least be notified.
Creating the Monitor
To create a new monitor:
- Click the Add New Monitor button to display the add monitor form
- Select the monitor type — there are many different types such as Ping, TCP Port, DNS, and Docker Container. For this monitor, we want HTTP/HTTPS (which is also the default)
This type is where Uptime Kuma sends an HTTP request to the configured domain to see if the request succeeds. If it fails, the monitor marks the website as down. This is the most simple and probably the most common monitor type.
Basic Configuration
With the monitor type selected, configure the following:
- Friendly name:
Zenprompter HTTP - URL:
https://zenprompter.app
Heartbeat Settings
Heartbeat Interval: The total time in seconds between requests sent to the configured URL. Default is 60 seconds, which is fine for most use cases. You can set it lower (like 15 seconds) for more responsiveness, but note:
- Setting it too low may result in more false positives
- It negatively affects the status page display
- Set this value as high as acceptable for your requirements
Retries: The number of successive retry attempts before determining the website is down and sending notifications. Default is zero (first failure triggers down status).
I find zero to sometimes be susceptible to false positives, which can happen when a service is redeploying. To reduce false positives, I set this to 4, meaning four total attempts after the first one to confirm the website is down.
Heartbeat Retry Interval: By default, each retry happens after 60 seconds. This is a bit slow for me—it can mean up to five whole minutes before a service is marked down. I set this to 20 seconds, which means roughly 2 minutes total.
Request Timeout: The number of seconds without a response to mark a request as failed. Default is 48 seconds, which is fine.
Resend Notifications: Configure resending notifications after consecutive down events. I leave this at zero (disabled) because I don't want to get spammed with notifications for the same outage.
Advanced Configuration
There are four important checkboxes in the advanced configuration:
- Enable notification when certificate is expired — I enable this; it's a good option to have
- Ignore HTTPS/TLS errors — I leave this disabled; I want to receive a notification if we encounter one
- Add random parameter to bypass caches — New in V2. This adds a randomly generated parameter to bypass caches like Cloudflare proxy. Since I use Cloudflare proxy, I enable this so my monitor isn't hitting cached values (which could lead to false negatives)
- Upside down mode — Inverts the monitor to send a notification when a website is accessible rather than when it's not. This may seem confusing, but we'll look at examples where this is useful later
Additional settings:
- Maximum redirects: Default is 10
- Accepted HTTP status codes: 200-299
- IP family: Auto (can select IPv4 or IPv6)
- Monitor group, description, and tags: Leave as default for now
HTTP Request Options
On the right side, you can configure the HTTP request:
- HTTP method: Default is GET
- Request body: Can be encoded as JSON, HTTP form, or XML
- HTTP headers: Useful for API tokens
- Authentication: HTTP Basic Auth, OAuth 2, NTLM, or mTLS
For this simple monitor, I leave all of these as default.
Notifications
Just above the HTTP options is where we can add a notification—the service that will notify us when the monitor detects the website is down.
Setting Up a Notification
Click Setup Notification to see a modal dialogue with many different notification types:
- Telegram
- Discord
- Gotify
- PagerDuty (my own personal nightmare)
- And many more
Despite dreading the 2 a.m. nightmare that is PagerDuty, this is actually one of my preferred notification configurations as it absolutely makes sure you're notified when something goes wrong.
For this article, I'll show how to set this up using Slack, but feel free to use whichever service you prefer.
Configuring Slack Notifications
To configure Slack notifications, you need a Slack webhook URL. Uptime Kuma provides a link to the Slack documentation with a step-by-step guide. The basic outline is:
- Create or use an existing Slack application
- Enable incoming webhooks
- Create an incoming webhook with the channel you want to post to (e.g., "uptime-alerts")
- Copy the webhook URL and paste it into the Webhook URL field in Uptime Kuma
With the required fields completed, there are additional properties to configure:
- Send rich messages — Enable
- Notify channel — Enable (ensures you receive a push notification even when set to away)
- Default enabled — Enable (this notification type will be enabled by default for future monitors)
Testing the Notification
Press the Test button to produce a test message in your configured Slack channel. Once confirmed working, save the notification—it will automatically be enabled for your HTTP monitor.
Note: A monitor can have multiple notification services configured. Typically, I like to have both Slack and PagerDuty set up, allowing me to be notified whether I'm at my desk or away.
Testing the Complete Setup
With the monitor created and notification configured, let's test everything:
- Disable the running instance of your application (I use Dockploy for deployment)
- Within 60 seconds, the monitor in Uptime Kuma should move to a pending state as it begins retrying
- After four retries, it moves to a failed state and a notification comes through on Slack
- Restart the application—after a few seconds, the monitor goes back to green and you receive another notification confirming the application is back online
Status Page
In addition to having multiple notifications, another feature I commonly use in production is the built-in status page. This allows users to view the current status of my services and enables me to communicate any current incidents or maintenance windows.
Creating a Status Page
- Head to the Status Pages section
- Click New Status Page
- Provide a name and URL slug (e.g., "zenprompter" for both)
- Add the monitors you want displayed
- Press Save
You now have a status page viewable at the slug you provided, showing your configured monitors.
You can see an example of my status page for Zenprompter at uptime.zenvps.xyz/status/zenprompter.
Status Page Limitations
One complaint I have with Uptime Kuma's status page is that it only shows a short amount of data—roughly 1 hour when you have a 60-second heartbeat configured. I'd love to be able to configure this so the range could be much larger (past month or even past year).
There is an open issue for this feature, and one of the maintainers mentioned they were waiting for V2 due to performance implications. So for now, we'll have to wait and see if it gets added.
Despite this limitation, I still link to this status page on my production services so users can see the up-to-date status at any time.
Creating Incidents
Use the Create Incident button to display a form where you can add:
- Title
- Description
- Associated color (I use this for describing severity)
Once created, this displays on the status page, communicating with your users when something is going on.
Maintenance Tasks
Uptime Kuma also provides the ability to schedule maintenance tasks through the Maintenance page.
Creating a Maintenance Task
- Create a new task with a title and description
- Choose which associated monitors will be affected
- Configure which status pages will show this task (individual pages or all)
When active, the maintenance task appears on the status page.
Scheduling Options
Maintenance tasks can be configured several ways:
- Manual toggle — Turn on/off manually
- One-off task — Scheduled at a certain time for a certain duration
- Recurring schedule — Set up in the UI or specified through a cron expression
I like to use this to communicate planned maintenance, such as upgrading a VPS instance size.
Tip: When performing tasks that might cause expected outages, you can pause a monitor to prevent it from performing checks during that time, avoiding disruptive notifications or affecting your total uptime score.
Status Page Comparison
While this status page is a nice feature to have for free, compared to other status pages (like Better Stack or StatusPage.io), Uptime Kuma's is somewhat lacking. The main missing feature is the ability for users to subscribe to status update notifications.
SSH Port Monitor
Now let's look at some other monitors I like to configure for my infrastructure stack.
Why Monitor SSH Ports?
My favorite use is to monitor the SSH ports of my VPS instances to ensure they're inaccessible over the public internet.
If you've followed my content for a while, you'll know this is one of my preferred security approaches—only allowing SSH over my TailNet provided by Tailscale. This helps improve the security posture of all my VPS instances, but only if the firewall is up and running, which can sometimes be modified inadvertently (especially when using Docker).
By adding a monitor to check for this, I can rest confidently knowing that if my SSH port ever becomes accessible, I'd receive a PagerDuty alert.
Configuration
- Set monitor type to TCP Port
- Set hostname to the public IP or configured DNS record of your VPS
- Set port to 22
Here's the key: this monitor would normally send a notification when the port isn't accessible. But I want the inverse—a notification when the port is accessible (meaning the firewall is down).
Enable Upside Down Mode to invert this behavior. The monitor will:
- Report an error and send notifications only when the port is accessible
- Mark as green when the port is inaccessible
After saving, within about a minute you should see the first request displayed as green (the TCP request timed out, meaning SSH isn't accessible over the public internet—which is desired).
Testing
- SSH into your VPS using Tailscale
- Disable the firewall
- Within about a minute, the monitor turns red and a notification comes through on Slack
- Re-enable the firewall to see the monitor return to green
Other Useful Monitors
In addition to SSH port monitoring, here are other monitors I like to configure:
- Dev instance authentication — Ensure dev versions of applications are only accessible using configured basic auth credentials
- API endpoint availability — Ensure publicly accessible API endpoints are up and can be used with an API key
- DNS records — Ensure they don't accidentally get changed from where they're supposed to be
This helps gain coverage of key areas of my infrastructure. Because of the status page, it helps users know which specific service is down during an outage—whether it's the web page, the API, or upstream with the database.
Using Tags for Context
By making use of the tags feature when configuring a monitor, you can apply more context on the status page. For example, communicating that a service being monitored is actually a third-party dependency.
Features I Wish It Had
Despite Uptime Kuma's capabilities, there are a couple of missing features I'd like to see added:
1. DNS Expiration Monitoring
This has bitten me a couple of times in the past. This monitor would notify you a configured period before a domain is set to expire—usually 1 month beforehand, then more frequently as expiration approaches.
Most of the time this is handled by domain registrars. However, email notifications don't always make it through. Having an additional notification on Slack provides peace of mind, especially when the credit card used for domain renewals expires.
Fortunately, there's an open issue and a promising pull request that looks to add this functionality.
2. Docker Labels Configuration
I'd love the ability to configure monitors through Docker labels, similar to how Traefik works—where you can configure Docker labels and CLI arguments to set up reverse proxy rules for different services.
It would be awesome to do the same with Uptime Kuma, meaning you could easily configure it in a configuration file rather than needing to use the GUI.
To be fair, Uptime Kuma does provide an API for configuring monitors, and there's an open issue for a Terraform provider which would allow declarative configuration. However, I'd still love the ability to do this through Docker Compose.
Additional Wishlist Items
- Status page improvements — View more historical data and allow users to subscribe to status updates
- Multiple account users — Currently you can only have one user, which doesn't scale beyond individual developer needs
Conclusion
Despite these missing features, Uptime Kuma is still a fantastic way to add monitoring to your applications through a self-hosted solution—one that I would consider to be almost perfect.
For myself, I'm probably going to continue using it for the foreseeable future when it comes to helping me resolve any unexpected downtime as quickly as possible.
Resources
- Uptime Kuma: https://uptime.kuma.pet/
- Docker Compose Configuration: https://github.com/dreamsofcode-io/uptime-kuma-compose
- Zenprompter Status Page Example: https://uptime.zenvps.xyz/status/zenprompter
- Zenprompter App: https://zenprompter.app
To get your own VPS instance to use with Docker Manager, visit hostinger.com/dreamsofcode and use coupon code DREAMSOFCODE for an additional 10% off.