Never Locked Out: Monitoring SSH Reachability with Uptime Kuma and Tailscale

Hero image for Never Locked Out: Monitoring SSH Reachability with Uptime Kuma and Tailscale | Coders' Compass Publishing

Introduction

This article is about a small, focussed application of Uptime Kuma. There is an enormous variety of things you can monitor with it. One such item you can monitor is a TCP port. I use this functionality to watch TCP port 22 on every machine in my Tailscale tailnet. This helps me find out about a machine being unreachable via SSH before anything else goes down, and while trying to SSH in I discover there’s no way in.

If you haven’t set up Uptime Kuma yet, we have a dedicated article on installing it.

Why Monitor SSH?

SSH is the entry point to our machines, both remote and local. Anecdotal experience suggests that SSH failures are of the silent kind. sshd might break after an update. You could forget to reboot after a kernel update, a firewall might block port 22 without you knowing, or you might accidentally lock yourself out by incorrectly changing the sshd_config file. The machine keeps running, independent of all these cases. You only realise the entrance is inaccessible when you try to walk through it, which is usually the moment you actually need it.

A passive, instantaneous check every minute barely costs anything, and it turns a “that machine won’t let me in” problem into an alert that comes hours sooner.

Note that this covers the necessity of monitoring. By itself, it will not be of much use unless you have a fallback mechanism for regaining access to your machine(s). We talk about it in this section.

Prerequisites

Verification

Before adding monitors, confirm that the Uptime Kuma host can actually reach each machine over Tailscale. Run these from the host that runs Uptime Kuma.

Check that Tailscale is up:

1tailscale status

You should see a list of peers in your tailnet with their MagicDNS short names in the first column.

Resolve a MagicDNS short name to a 100.x.y.z Tailscale address:

1tailscale ip gandalf

Replace gandalf with one of your machine names. Expected output:

1100.64.0.12
2fd7a:115c:a1e0::abcd

Confirm port 22 actually answers from this host:

1nc -zv gandalf 22

Expected output:

1Connection to gandalf (100.64.0.12) 22 port [tcp/ssh] succeeded!

If any of those fail, fix the Tailscale side first. Uptime Kuma will see exactly what your shell sees.

Add a TCP Port Monitor

In the Uptime Kuma dashboard, click Add New Monitor. Set Monitor Type to TCP Port.

TCP Port monitor configured for SSH with a MagicDNS hostname and port 22

Fill in the form:

Save it, then repeat for every machine you care about. To save time, clone the first and change the hostname for the rest. A dashboard with one tile per machine takes minutes to set up and pays for itself the first time something quietly breaks.

Uptime Kuma dashboard with several SSH monitors showing green heartbeats

Going Further: Catching Accidental Public Exposure

Let’s switch gears and consider the opposite scenario. This setup addresses and notifies you when tailnet SSH stops working. This doesn’t cover SSH access from unauthorised spots, anywhere outside your Tailscale network. Things can go wrong quietly if a firewall rule expires or ‘sshd’ ends up on a public interface because of a configuration mistake, endangering your tailnet-only setup.

A second Uptime Kuma instance running outside your tailnet (say, on a small VPS or a friend’s home server) can address this. Add a TCP Port monitor for the public hostname or IP of each machine on port 22, and turn on Uptime Kuma’s Upside Down Mode. When that’s toggled, “reachable” shows as DOWN. You will be notified when SSH unexpectedly becomes available on the public internet.

Note: I haven’t deployed this myself. The boxes are firewalled at the cloud-provider level, and I’ve trusted that single layer. If you decide to set it up, the wiring is the same shape as a normal TCP monitor. There’s a toggle to flip the monitoring to alert if something’s up instead of down.

There are other alternative approaches available. Upside Down mode on Uptime Kuma is pragmatic because we’re already self-hosting Uptime Kuma and we don’t want another tool to maintain.

Fallback Paths Before You Need Them

Monitoring is one part of the puzzle. It alerts you to the existence of a failure, that something went wrong. The other piece is administering the fix through fallback paths. These two support each other. Having only one of them is insufficient.

Test your provider’s out-of-band console on VPS-hosted machines to confirm you have an alternative means of entry before SSH becomes inaccessible. Hetzner Cloud’s Rescue System or the console, Akamai Linode’s Lish, and DigitalOcean’s Recovery Console all let you in over the provider’s own channel when the network path is broken. AWS, OCI, and Vultr have similar equivalents that function identically. Quickly boot a VPS into rescue, then log in, and save the page.

A USB keyboard, a working HDMI cable, and a functional display are simpler, more dependable fallback options for machines I can physically interact with. Dumb, but extremely reliable. Before setting up remote access through SSH and installing Tailscale on a new machine, I conduct this check to guarantee I can recover it, even with a broken network.

Here’s the full stack:

  1. Tailscale to connect to the box.
  2. Uptime Kuma to monitor its reachability.
  3. An out-of-band solution for when both fail together.

Conclusion

A simple TCP port monitor on port 22 over Tailscale’s Magic DNS is a tiny but powerful application of Uptime Kuma. This automation has saved me from guessing, “When did this machine go down?” This approach applies to any TCP service that needs monitoring within your tailnet. It could be a Redis instance, a Postgres database, a Minecraft server, or any service listening on a port.

How do you keep yourself from getting locked out of your own machines? Share it in a comment below!

Comments