Deployment guide

Monitoring your VPS with Uptime Kuma

Deploy on a VPS Cloud →

Tutorial

Security & Monitoring

Monitoring your VPS with Uptime Kuma

Knowing that a service is down before your customers report it to you changes everything. Uptime Kuma is an open source availability monitoring tool that you host yourself on a VPS, with no subscription and no limit on checks. Here's how to set it up properly.

8 min read11 steps
Contents· Why self-host Uptime Kuma on a VPS1/10
  1. 01Why self-host Uptime Kuma on a VPS
  2. 02The concrete benefits of this template
  3. 03Requirements to host it
  4. 04Deploy Uptime Kuma with Docker and SSL
  5. 05What the Docker monitor does not see
  6. 06Why a single signal is not enough
  7. 07Complete the Docker monitor with several signals
  8. 08The Retry of a TCP monitor: a fixed case, to check on your version
  9. 09One alert, several signals
  10. 10The official documentation

Why self-host Uptime Kuma on a VPS

SaaS monitoring services often bill by the number of monitors or the check frequency, and cap the interval at 1 or 5 minutes on free plans. By deploying Uptime Kuma on your own VPS, you monitor as many sites as you want, APIs, TCP ports, Docker containers or DNS servers, with intervals as short as 20 seconds. Hosting the probe on a VPS separate from your production servers is an advantage: if your application server goes down, the monitoring VPS stays up and triggers the alert. Uptime Kuma also offers a public status page that you can expose to your customers, and more than 90 notification channels (Telegram, email, Discord, webhook). Everything stays on your infrastructure, without sending your internal URLs to a third party.

The concrete benefits of this template

  • Monitors with no cap: HTTP(s), TCP, ping, DNS, Docker containers, SSL certificates and page keywords.
  • Multi-channel alerts (Telegram, email, Discord, Slack, webhook) with more than 90 integrations.
  • Customizable public status page to share with your customers or your team.
  • SSL certificate expiration monitoring so you're never caught off guard again.
  • Uptime history and statistics kept locally, with no paid retention.
  • Checks as frequent as every 20 seconds, impossible on most free plans.

Requirements to host it

Uptime Kuma is very frugal: a 1 vCPU VPS with 512 MB to 1 GB of RAM is more than enough to monitor several dozen monitors. The embedded SQLite database takes up little space; count on 2 to 5 GB of disk depending on the history retained. You need Docker installed, a domain or subdomain (status.mydomain.com) pointing to the VPS, and ports 80/443 open for the reverse proxy and SSL. For reliable monitoring, host Uptime Kuma preferably on a VPS separate from your production servers.

Deploy Uptime Kuma with Docker and SSL

  1. Create the volume and start the container

    On the VPS, create a persistent volume then start the container: docker run -d --restart=always -p 3001:3001 -v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1. The interface is accessible on port 3001.

  2. Configure the administrator account

    Open http://IP_DU_VPS:3001 and create the admin account on first login. Choose a strong password: this interface will have access to all your monitored URLs.

  3. Put a reverse proxy and SSL in front

    Configure Nginx or Caddy to expose status.mydomain.com to localhost:3001. With Caddy, two lines are enough to get an automatic Let's Encrypt certificate: status.mydomain.com { reverse_proxy localhost:3001 }.

  4. Add your first monitors

    Create HTTP(s) monitors for your sites, a TCP monitor for your database, an SSL check to monitor certificate expiration, and a keyword to detect error pages served with HTTP 200.

  5. Wire up notifications and the status page

    In Settings > Notifications, add Telegram or email and test the delivery. Then enable a public status page grouping your customer services, with a dedicated domain name if needed.

  6. Logging in for the first time

    Open the URL without delay: the very first screen asks you to create the administrator account (username + password), after which you reach the dashboard. There are no default credentials.

To avoid false alerts during a minor network incident, configure the Retries field (for example 3 consecutive failures before alerting) and a suitable Heartbeat Interval. Combine it with a group notification: Uptime Kuma can send an aggregated message rather than one alert per monitor, which avoids flooding your Telegram channel during a global network outage that takes down ten services at once.

What the Docker monitor does not see

The Docker Container monitor queries the Docker engine and reads the container's state. That is convenient, but the state describes the process, not the service. An open issue on the tool's repository (#7956, reported on version 1.23.17) gives a clear case: a container started with a restart policy (restart: always, unless-stopped, or the --restart=always option of the deployment command above) that crashes in a loop is displayed as UP with the message "restarting". The reporter ties this behavior to the check on State.Running, which stays true during the delay between two restarts. The monitor stays green, no notification goes out, and your application no longer responds. When we re-read the thread, the issue was still open, with no merged fix: only proposed patches appear in it. The Docker monitor answers the question "is the container running?", not "is the application serving its users properly?".

Why a single signal is not enough

  • The process runs, the service does not — a container can start, crash two seconds later and restart: on each poll, the Docker engine may describe it as running.
  • No healthcheck, no verdict — without a HEALTHCHECK instruction in the image or a healthcheck block in your compose file, the engine only has the raw state to pass on to Uptime Kuma.
  • Unhealthy does not alert everywhere — according to the issue thread, treating the "unhealthy" state as DOWN was merged for the 2.1.0 branch (PR #6292), while the 1.x branch leaves that case pending (PENDING), so without an alert. The deployment above uses the louislam/uptime-kuma:1 image.
  • The restart policy hides the outage — restart: always helps recover from an isolated incident, but it turns a clear outage into an intermittent one that widely spaced polls do not catch.
  • A monitoring tool can have its own blind spots — a feature that works one month can regress at the next update: verify your alerts with a real test, not only by reading the interface.

Complete the Docker monitor with several signals

  1. Monitor the application itself

    Add an HTTP(s) monitor, or "HTTP(s) - Keyword" if the page must contain a specific word, on the application URL (public or internal to the VPS). A container in a crash loop does not respond: this monitor fails while the Docker monitor stays green. Keep Retries at 3 to avoid false alerts.

  2. Declare a healthcheck in the compose file

    In the monitored service, add for example healthcheck: test: ["CMD-SHELL", "wget -q --spider http://localhost:3000/ || exit 1"] with interval: 10s, timeout: 3s, retries: 3 and start_period: 30s. Adapt the port and the tool: the image must contain wget or curl. Docker then reports a healthy or unhealthy state, which you read in docker ps.

  3. Read the restart counter

    On the VPS, docker ps --filter status=restarting lists the containers being restarted, and docker inspect mon-app | grep -E "RestartCount|Status" gives the number of restarts and the state. A RestartCount that climbs from one check to the next signals a loop, even if the displayed state is running.

  4. Send a heartbeat with a Push monitor

    Create a Push-type monitor in Uptime Kuma, then run a small script from cron on the VPS that reads the RestartCount and calls the Push URL only if it has not moved since the previous check. If the counter climbs, the heartbeat is missing and the monitor goes DOWN: the alert relies on a fact the Docker monitor does not look at.

  5. Test the alert chain for real

    Start a container that crashes on purpose: docker run -d --name crash-test --restart unless-stopped alpine sh -c "sleep 2; exit 1". Create a Docker monitor on crash-test: if it stays UP while docker ps --filter status=restarting lists it, you are reproducing the case described in the issue. Check that your HTTP or Push monitor does trigger the notification, then remove the container with docker rm -f crash-test.

The Retry of a TCP monitor: a fixed case, to check on your version

A second issue (#7979) concerned the Retries field. On version 2.5.6, a TCP Port monitor configured with SSL or StartTLS ignored this setting: on the first failure it went straight to red, while HTTP(s) monitors with the same number of attempts went to pending first. The case is narrower than the title suggests: it affects the TCP variant with TLS, not every TCP monitor. The issue was closed with the fix from PR #7989, which the 2.5.7 release notes list. If you monitor a TLS port (mail, XMPP, encrypted database) with an earlier version, you get alerts on every isolated failure. Update the tool, then check the behavior by stopping the tested service: the monitor must go pending before it goes down. Another detail from the thread: the Down Count field is a counter tied to notification resending, not an attempt counter.

One alert, several signals

Attach the HTTP monitor, the Push monitor and the Docker monitor to the same group notification, and reserve the urgent message for the application signal: a page that no longer responds is an outage, a climbing RestartCount is a warning to handle within the hour. That way, the failure of a single monitor no longer leaves your Telegram channel silent.

The official documentation

For advanced configuration and options specific to the tool, refer to the official Uptime Kuma documentation. This guide covers deployment on a VPS; the vendor's documentation remains the reference for fine-tuning, major updates and specific use cases.

Host your monitoring probe

Deploy Uptime Kuma on a ServOrbit Cloud VPS dedicated to monitoring: resources isolated from your production and HTTP/HTTPS ports ready to use.