VPS Tutorials
Set Up an Uptime Kuma Monitoring Dashboard on a VPS (2026): Downtime Alerts, Status Pages, and a Complete Docker Compose Guide
This tutorial is for users who want to self-host a monitoring dashboard on a low-cost VPS. You will learn to deploy Uptime Kuma with Docker Compose, put the service behind Caddy HTTPS, add HTTP, TCP, Ping, and Docker service monitors, configure Telegram/email notifications, create a public status page, and handle data backups, upgrades, and common false alarms.

Set Up an Uptime Kuma Monitoring Dashboard on a VPS (2026): Downtime Alerts, Status Pages, and a Complete Docker Compose Guide
After many people buy a VPS, the thing they most easily overlook is this: when the machine goes down, you may be the last person to know.
I have fallen into this trap before. A small website's certificate renewal failed in the middle of the night, and Nginx simply stopped working. When I opened the admin panel the next morning, I found the site had already been down for 7 hours. Traffic was not high, but search engines, users, and customers all saw the error page, and that kind of loss is really annoying.
Uptime Kuma is well suited to solving this problem. It is not the kind of complex monitoring system where you have to learn a pile of concepts first. It is a relatively lightweight self-hosted monitoring dashboard: it can check whether a website is online, monitor ports, ping machines, send Telegram, email, and Webhook notifications, and also create a public status page.
This article is written as a VPS hands-on guide: deploy Uptime Kuma with Docker Compose, use Caddy for HTTPS, how to back up data, how to configure notifications, which services are worth monitoring, and which false alarms you do not need to worry about too much.
Uptime Kuma suits these scenarios:
- You have a few websites, APIs, or backend services to keep an eye on.
- You want to receive Telegram, email, or WeCom notifications immediately after downtime.
- You need a simple status page to tell users which services are normal and which are down.
- You do not want to bring in the relatively heavy Prometheus + Grafana stack.
- You only have one low-spec VPS and still want it to run.
It is not very suitable for these scenarios:
- You need complex metric analysis, such as CPU, memory, disk, or slow query trends.
- You need to store years of time-series data.
- You need multi-tenant permissions, complex reports, and enterprise auditing.
My advice is: use Uptime Kuma for "is the service alive" monitoring, and use Prometheus/Grafana for "why did it get slow" monitoring. They are not the same kind of thing.
Uptime Kuma is very light, and personal use is not picky about the machine.
| Scale | Recommended configuration |
|---|---|
| 5-20 monitors | 1 vCPU / 512MB RAM / 10GB SSD |
| 20-100 monitors | 1 vCPU / 1GB RAM / 20GB SSD |
| Multiple status pages + multiple notification channels | 2 vCPU / 2GB RAM / 40GB SSD |
The real thing to pay attention to is this: it is best not to put the monitoring dashboard on the same machine as all your business services.
For example, if you only have one VPS and both the website and Uptime Kuma run on that machine, then when the machine goes down, Uptime Kuma goes down with it, and of course it cannot notify you. So a better approach is:
- Main business on machine A.
- Uptime Kuma on machine B.
- If the budget is limited, a cheap annual VPS for machine B is enough.
If you only have one machine for now, it is not impossible to deploy it. At least you can first monitor the domain, certificate, ports, and Docker container status. Later, when the business becomes more important, migrate the monitoring dashboard to another VPS.
The default environment for this article:
- Ubuntu 22.04 / 24.04 or Debian 12.
- Docker and Docker Compose already installed.
- You have a domain, for example
status.example.com. - Ports 80/443 are accessible.
- Caddy is already installed, or you are ready to configure it according to this article.
First confirm Docker is available:
docker version
docker compose version
Confirm the domain already resolves to the VPS:
dig status.example.com +short
If Caddy is not installed yet, you can first read this article: Complete Guide to Caddy Reverse Proxy on a VPS.
I usually put this kind of service under /opt:
sudo mkdir -p /opt/uptime-kuma
sudo chown -R "$USER":"$USER" /opt/uptime-kuma
cd /opt/uptime-kuma
Create the data directory:
mkdir -p data
Uptime Kuma's monitors, notification configurations, status pages, and user information are all in /app/data. This directory must be persisted, otherwise data will be lost later when you delete containers or upgrade images.
Create docker-compose.yml:
nano docker-compose.yml
Write:
services:
uptime-kuma:
image: louislam/uptime-kuma:2
container_name: uptime-kuma
restart: unless-stopped
volumes:
- ./data:/app/data
ports:
- "127.0.0.1:3001:3001"
Here I deliberately bind only 127.0.0.1:3001, so that port 3001 is not directly exposed to the public internet.
The reason is simple: only 80/443 are open to the public internet, and Uptime Kuma sits behind Caddy. This makes certificates, HTTPS, and access control easier to handle.
If you also want to use Uptime Kuma to monitor Docker containers on the same VPS, you need to mount the Docker socket additionally:
services:
uptime-kuma:
image: louislam/uptime-kuma:2
container_name: uptime-kuma
restart: unless-stopped
volumes:
- ./data:/app/data
- /var/run/docker.sock:/var/run/docker.sock:ro
ports:
- "127.0.0.1:3001:3001"
Note this line:
- /var/run/docker.sock:/var/run/docker.sock:ro
Without it, Uptime Kuma usually cannot see the Docker containers on the host, so you cannot add Docker Container type monitors.
But do not casually mount the Docker socket into every container. It can read Docker status and is itself a highly sensitive entry point. It is only recommended to enable it when you really need Docker container monitoring, and continue to keep Uptime Kuma accessible only via HTTPS, without exposing port 3001 directly to the public internet.
Start the service:
docker compose up -d
Check status:
docker compose ps
docker compose logs -f --tail=100
Test locally:
curl -I http://127.0.0.1:3001
If there is a response, the container is already running.
Edit the Caddyfile:
sudo nano /etc/caddy/Caddyfile
Add:
status.example.com {
encode zstd gzip
header {
X-Content-Type-Options nosniff
Referrer-Policy strict-origin-when-cross-origin
}
reverse_proxy 127.0.0.1:3001
}
Check the configuration and reload:
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
Open in the browser:
https://status.example.com
The first visit will ask you to create an administrator account. Do not use a weak password. The monitoring dashboard usually stores notification channel tokens, Webhook addresses, and service information, so it is quite troublesome if it gets scanned.
After logging in, click Add New Monitor and start with website monitoring.
Common configuration:
- Monitor Type: HTTP(s)
- Friendly Name: Main site
- URL:
https://example.com - Heartbeat Interval: 60 seconds or 120 seconds
- Retries: 2-3 times
- Accepted Status Codes:
200-299
I generally do not set the interval too short. Many small VPSs, cheap routes, and overseas networks occasionally jitter, and that is normal. If you set it to check every 20 seconds, you may end up scaring yourself.
A more stable setting is:
间隔:60 秒
重试:3 次
失败后再通知
This can filter out some brief network jitter.
Do not add 100 monitors right away. First keep an eye on what is truly important.
It is recommended to start with these categories:
| Type | Example | Purpose |
|---|---|---|
| HTTP(s) | Official site, admin panel, API | See whether the page can open |
| TCP Port | 22, 443, 5432, 6379 | See whether the port is alive |
| Ping | VPS IP, gateway | See whether the machine or route is reachable |
| Keyword | A word that must appear on the page | Avoid "returns 200 but the page is wrong" |
| Docker Container | Nginx, Postgres, Redis | See whether the container is still running |
What I use most is HTTP(s) + Keyword. Because some websites may return 200 even when they are reporting errors. For example, the application framework shows an error page, the HTTP status code is normal, but the page content is no longer what you want.
At this point you can set a Keyword:
必须包含:网站标题、品牌名、某个固定文字
If the page no longer contains this word, Uptime Kuma will determine it as abnormal.
If you want to add Docker Container monitoring, first confirm that the Compose file above has mounted /var/run/docker.sock. Otherwise, when you select the Docker type in Uptime Kuma, it will most likely fail to connect to the Docker daemon.
Telegram is one of the notification methods I recommend most: fast, free, and simple to configure.
The general process:
- Find
@BotFatherto create a Bot. - Get the Bot Token.
- Send a message to the Bot.
- Get the Chat ID.
- Add a Telegram notification in Uptime Kuma.
If you do not know how to get the Chat ID, you can visit:
https://api.telegram.org/bot你的TOKEN/getUpdates
Find chat.id in it.
In Uptime Kuma, go to:
Settings -> Notifications -> Setup Notification
Select Telegram, fill in the Bot Token and Chat ID, and then click Test.
After the test succeeds, remember to set this notification channel as default, or manually bind it to important monitors.
Email notifications are good as a fallback. Telegram is sometimes muted by the phone system, but email can at least leave a record.
Common SMTP configuration:
Host: smtp.example.com
Port: 587
Security: STARTTLS
Username: [email protected]
Password: 邮箱授权码
From: [email protected]
Note a few pitfalls:
- Many email providers do not allow using the login password; use an app-specific password instead.
- VPS providers may restrict port 25; prefer 587 or 465.
- It is best to configure SPF, DKIM, and DMARC for the sending mailbox, otherwise it easily goes to spam.
- Do not fill in your personal main mailbox password.
If it is only for personal use, Telegram + email is already enough. For enterprise scenarios, you can also connect Slack, Discord, Webhook, WeCom, etc.
Uptime Kuma's status page is very practical. For example, if you have a few services:
- Official site
- API
- Documentation site
- Download site
- Admin panel
You can create a public page:
https://status.example.com/status/main
After users or customers open it, they can see which services are normal and which are down.
However, I do not recommend making all internal services public. The status page should only include what users need to know, such as the official site, API, and download service. Do not expose databases, Redis, or internal admin panels; there is no need to show others your internal structure.
Many people forget this point.
If Uptime Kuma only monitors others, no one will know when it goes down itself. A more reliable approach is:
- Use another free monitoring service to monitor
status.example.com. - Or find another low-cost VPS to run a second Kuma, monitoring each other.
- Or use a third-party service as a fallback, such as Better Stack, Healthchecks, UptimeRobot, etc.
Personal projects do not need to be too complicated, but at least you should know one fact: single-point monitoring can only discover other people's problems, not the problem of your own complete downtime.
Uptime Kuma's data is in /opt/uptime-kuma/data. Just back up this directory.
The simplest backup:
cd /opt/uptime-kuma
mkdir -p /opt/backups/uptime-kuma
tar czf /opt/backups/uptime-kuma/uptime-kuma-$(date +%F-%H%M).tar.gz data
If you already use restic, rclone, or object storage for backups, just add this directory to the backup list.
It is recommended to back up at least:
data/kuma.dbdata/upload/- The entire
/opt/uptime-kuma/data
When restoring, stop the service first:
cd /opt/uptime-kuma
docker compose down
mv data data.broken.$(date +%F-%H%M)
tar xzf /opt/backups/uptime-kuma/uptime-kuma-你的日期.tar.gz
docker compose up -d
After restoring, check: whether the monitors are still there, whether the notification channels can be tested, and whether the status page link is normal.
Back up before upgrading:
cd /opt/uptime-kuma
tar czf /opt/backups/uptime-kuma/before-upgrade-$(date +%F-%H%M).tar.gz data docker-compose.yml
Pull the new image and restart:
docker compose pull
docker compose up -d
Check the logs:
docker compose logs -f --tail=100
If you do not want to follow the latest version every time, you can pin the image version. For personal projects, louislam/uptime-kuma:2 is more worry-free; for production environments, it is more advisable to upgrade once on a test machine first.
First check whether the container is normal:
docker compose ps
curl -I http://127.0.0.1:3001
If it is accessible locally, the problem is usually in the Caddy configuration. Check whether the reverse proxy port is written correctly:
reverse_proxy 127.0.0.1:3001
Then:
sudo caddy validate --config /etc/caddy/Caddyfile
sudo systemctl reload caddy
Check whether this notification channel is bound to the monitor.
In Uptime Kuma, notifications can be set as default, or bound only to certain monitors. Many people only create the notification channel but forget to enable it for the monitor.
Do not rush to suspect the service is down; the detection strategy may be too aggressive.
You can adjust:
- Change Heartbeat Interval from 20 seconds to 60 seconds.
- Change Retries from 0 to 2 or 3.
- Relax the timeout appropriately.
- Do not use a single overseas node to monitor domestic route quality.
If your website users are mainly in mainland China, while Uptime Kuma is on a European VPS, the network conditions it sees do not necessarily represent the experience of users in mainland China.
Use Keyword monitoring.
For example, when your homepage is normal, it must contain:
VPScost
Then let Uptime Kuma check this keyword. As long as the page becomes an error page, maintenance page, or blank page, even if the status code is still 200, the problem can be discovered.
First check whether the container has the Docker socket:
docker compose exec uptime-kuma ls -l /var/run/docker.sock
If it says it does not exist, it means Compose did not mount this line:
- /var/run/docker.sock:/var/run/docker.sock:ro
After changing it, restart:
docker compose up -d
If the socket exists but it still cannot connect, then check Docker permissions and the Docker Host configuration in Uptime Kuma. The most common problem on personal VPSs is: you deployed the basic Compose version according to the article, but later wanted to add Docker Container monitoring, and forgot to give Kuma access to Docker.
Before formal use, it is recommended to confirm:
- The
datadirectory is persisted. - Port 3001 is bound only to
127.0.0.1. - The domain uses HTTPS.
- The administrator password is strong enough.
- At least one instant notification channel is configured.
- Important monitors are bound to notifications.
- If you want to monitor Docker containers,
/var/run/docker.sockis mounted. - The status page does not expose internal services.
- The data directory is included in backups.
- A notification test has been done.
- A recovery drill has been done.
Uptime Kuma is best suited for "first-layer monitoring": website cannot open, API is down, port is unreachable, certificate is about to expire, keyword is missing; it can remind you quickly.
But it is not omnipotent. It tells you "the service has a problem", but not necessarily "why there is a problem". If you also want to see CPU, memory, disk, and container metrics, you can add Prometheus + Grafana; if it is just a personal project, a small team website, or a few self-hosted applications, Uptime Kuma is already enough.
My own habit is: add important services to Uptime Kuma first, at least ensuring someone is alerted when they go down; when the service becomes more complex, then add logs, metrics, and tracing. Do not make the monitoring system more complex than the business from the start.