Uptime Kuma is a free open-source program that keeps an eye on whether your websites and servers are working. Every minute it visits the site, tries to open the port, pings the address, and if something stops responding, you find out right away and not from your clients. Besides the checks, Kuma draws availability charts, keeps the history of outages and can publish a status page where visitors see whether everything is fine. In this article we will install Uptime Kuma on a VPS in Docker, create an administrator, add monitors of different types, deliberately cause an outage, publish a status page and go through notifications, backups and updating. We repeated all the steps on a test server with Debian 12, and the Uptime Kuma version at the time of testing was 2.5.6.
How Uptime Kuma works
Uptime Kuma is a single Node.js program with a web interface. On the server it runs as an ordinary process that performs checks on a schedule, and every check is very simple. For a website it is an HTTP request, for a port it is an attempt to establish a TCP connection, and for ping it is an ordinary echo request. A check has only two outcomes, a response was received or it was not, and the response time is added to them. Kuma writes every result to the database as a separate point, and those points make up the coloured bars on the dashboard, the availability percentages and the response time chart. Consecutive failed points form an outage, and its start and end go into the event log.
Two practical conclusions follow from this design. The first is that Kuma sees your service the way the server it is installed on sees it, not the way your clients see it. If Kuma runs on the same server as the website, the monitoring itself goes silent when the server goes down, so for real projects it is better to install it on a separate inexpensive VPS. In our test, for demonstration purposes, we watched both the server itself and a website from a container. The second conclusion concerns Docker. A container lives in its own network, and the address 127.0.0.1 inside it means the container itself, not the server. So Kuma has to check the server's ports under a special name that we will add to the settings in Step 1. We ran into this ourselves while checking the SSH port, and you can find the details in the FAQ at the end of the article.
A single failed check does not always mean a real outage, because a website could fail to respond due to a random network glitch. That is what the Retries setting is for, and every monitor has it. While retries remain, Kuma draws the failed check as an orange bar and sends nothing, and only if there is still no response after all the repeats does it mark the service as down and send notifications. In Uptime Kuma 2 new monitors have 0 retries, which means an outage is declared right after the first failed check. We will show the difference in Step 6. All of Kuma's data, from the account to the check history, lies in a single data folder on the server, and by default the check history is kept for 180 days. Thanks to this the program is easy to update and copy.
Why the image tag is 2
The developers recommend the image with the 2 tag, which always points to the latest release of the second branch. The old latest tag is marked as deprecated in their documentation and still points to the first version, so it is better not to use it.
What you need
- A server with Debian 12, for example the DE-RZ9-1 plan. The Uptime Kuma container with five monitors took about 150 MB of memory on our server, and the downloaded image took about 2.5 GB of disk space, so it is better not to install it on the smallest disk. If you do not have a server yet, order one using the guide.
- Docker and Docker Compose. If you do not have them yet, install them using the guide. We had Docker 29.9.2 and Docker Compose 5.6.1.
- An SSH connection, for example via Termius. Commands are run as
root. - The address of the website you want to watch. In the examples we use
https://example.com.
Step 1. Install Uptime Kuma
Create a folder for Kuma and a Docker Compose settings file.
mkdir -p /opt/kuma
cd /opt/kuma
nano compose.yamlPaste the settings below into the file and save it with Ctrl + O, then Enter, and exit the editor with Ctrl + X.
services:
uptime-kuma:
image: louislam/uptime-kuma:2 # official image, second version
container_name: uptime-kuma
restart: unless-stopped # start after a failure and a server reboot
volumes:
- ./data:/app/data # all Kuma data lies in the /opt/kuma/data folder
ports:
- "127.0.0.1:3001:3001" # port 3001 is open only on the server itself
extra_hosts:
- "host.docker.internal:host-gateway" # the server's name for checks from inside the containerThe file almost repeats the official compose.yaml from the Uptime Kuma repository, and we added three things to it.
container_namesets a clear container name that we will use in the commands.127.0.0.1:before the port leaves the dashboard available only from the server itself. The official file has3001:3001, and then the port is open to everyone.extra_hostsadds the namehost.docker.internalinside the container, which points to the server. You will need it in Step 5 to check the server's ports.
Start Kuma.
docker compose up -dOn the first start Docker will download an image of about 600 MB, which took about 20 seconds on our server. Then Docker will create a network and start the container.

Docker created the network and started the Uptime Kuma container
Check the state with the docker compose ps command. In about 20 seconds the STATUS column will show the (healthy) mark, which means Kuma has started and is responding.
Do not open port 3001 to the internet
The Kuma dashboard sends the administrator's password in clear text over plain HTTP, so we left the port only on 127.0.0.1. In addition, Docker publishes ports bypassing the UFW firewall, and this trap is described in more detail in the article about Docker. We checked this on the test server. When we removed 127.0.0.1 from the ports line, port 3001 became reachable from our computer even though UFW had no rule for it. With 127.0.0.1 the connection was not established, and the computer simply waited until the timeout.
Step 2. Open the dashboard through an SSH tunnel
Since the port is closed from the internet, the dashboard is opened through an SSH tunnel. It forwards a port of your computer to a port of the server, so to the browser Kuma looks as if it were running on your computer.
On your computer, open a new terminal window and create the tunnel. Instead of
server-IP, put the address of your VPS.bashssh -L 3001:127.0.0.1:3001 root@server-IPWithout closing this window, open
http://localhost:3001in your browser.
If port 3001 on your computer is already taken, forward a different one, for example ssh -L 13001:127.0.0.1:3001 root@server-IP, and open http://localhost:13001. This is exactly how we worked ourselves.
Step 3. Choose a database and create an administrator
On the first launch Kuma 2 asks Which database would you like to use? The interface opened in Russian for us right away because our browser was set to Russian, and you can change the language in the Language list on the same page. There are three options.
- Embedded MariaDB – nothing needs to be configured, the Docker image already contains a ready MariaDB.
- MariaDB/MySQL – a connection to an external database. You need to specify the host name, port, username, password and database name.
- SQLite – a simple database file, recommended for small installations.
Select SQLite and click Next. This is the simplest option, Kuma itself recommends it for small installations, and the whole database will be a single file in the data folder, which makes backups easier.

Choosing a database on the first launch
After a few seconds Kuma will prepare the database and offer to Create your admin account.
- In the Username field, enter the administrator's name, for example
admin. - In the Password and Repeat Password fields, enter a long password. It is better to generate it in a password manager.
- Click Create.

The administrator account
Kuma will open the dashboard. On the left there will be the list of monitors, still empty, and on the right Quick Stats, which counts monitors by state, and the log of important events.

The dashboard before any monitors are added
Step 4. Add the first monitor for a website
For a website, the best choice is a monitor that checks not only the response code but also the content of the page. A broken website often keeps answering with code 200 but shows an empty space or an error message instead of the page. A keyword monitor will notice this too.
- Click Add New Monitor.
- In the Monitor Type list, choose HTTP(s) - Keyword.
- In the Friendly Name field, enter a name, for example
example.com (keyword). - In the URL field, enter the website address, in our case
https://example.com. - In the Keyword field, enter a word that must be on the page. For example.com it is
Example Domain. The letter case matters. - Click Save.

A keyword monitor
After a few seconds Kuma will make the first check, and the monitor will turn green with the Up status. Our website responded in 52 ms, and an entry 200 - OK, keyword is found appeared in the log. For an HTTPS website, Kuma shows on the same page how many days are left until the certificate and the domain name expire.

The first check passed, the monitor is green
The form has settings that are worth knowing from the very beginning.
- Heartbeat Interval – how often Kuma checks the website, 60 seconds by default.
- Retries – how many times to repeat a failed check before declaring an outage. 0 by default.
- Request Timeout – how many seconds to wait for a response, 48 by default.
- Certificate Expiry Notification – a warning when the website's certificate is about to expire.
Step 5. Add monitors for the server
Now let's check the server itself. First we will add an SSH port monitor, which will show that the server is on and accepts connections.
- Click Add New Monitor and choose the TCP Port type.
- In the Friendly Name field, enter
Server SSH. - In the Hostname field, enter
host.docker.internal. - In the Port field, enter
22. - Click Save.
Pay attention to the host name. The address 127.0.0.1, which seems the obvious choice, will not work inside the container, because it leads to the container itself, where there is no port 22. We checked this ourselves, and the monitor for 127.0.0.1:22 turned red right away.

This is what a monitor that looks at 127.0.0.1 inside the container looks like
After we replaced the host name with host.docker.internal, the monitor turned green and the port responded in 1 ms. The name works because we added extra_hosts to the Compose file in Step 1. In our test it pointed to the address 172.17.0.1, which is the server's address inside the Docker network.
Now let's add a ping monitor, which checks whether an address responds to echo requests.
- Click Add New Monitor and choose the Ping type.
- In the Friendly Name field, enter
Ping 1.1.1.1. - In the Hostname field, enter
1.1.1.1. - Click Save.
Ping to 1.1.1.1 responded in 6.45 ms. It is useful for making sure the server has internet access, or for watching a router and other devices that have no website.
The list of types has other monitors too. Among them are DNS, SMTP, Docker Container, Push, as well as the database checks MySQL/MariaDB, PostgreSQL, MongoDB and Redis. The principle is the same for all of them, Kuma contacts the service on a schedule and records the result.
Step 6. Cause an outage and see what it looks like
Monitors exist for those minutes when something breaks, so let's check what that looks like. We will start with the simplest outage, a port that nobody is listening on.
- Click Add New Monitor and choose the TCP Port type.
- In the Friendly Name field, enter
Closed port 12345. - In the Hostname and Port fields, enter
host.docker.internaland12345. - In the Heartbeat Interval field, enter
20so you do not have to wait a minute. - Click Save.
After a few seconds the monitor will turn red, and a red message will appear in the corner of the screen. The event log will record the Down status with the text Connection failed, and in the quick stats the Down counter will change to 1. Such a monitor will never turn green, and we will leave it on the dashboard as an example of an outage.

Quick stats and the event log. The closed port monitor is highlighted in red
Now let's cause a more lifelike outage. We will start a simple website in Docker on the server and stop it. The container has to be placed in the same network as Kuma. Docker Compose created it by itself, and the network name is made of the folder name and the word default, in our case kuma_default.
docker run -d --name kuma-demo-site --network kuma_default --restart unless-stopped nginx:alpineAdd a monitor for this website.
- Click Add New Monitor and choose the HTTP(s) type.
- In the Friendly Name field, enter
My site. - In the URL field, enter
http://kuma-demo-site. Containers in the same network find each other by name. - In the Heartbeat Interval field, enter
20. - In the Retries field, enter
2. The Heartbeat Retry Interval field will appear next to it, leave20in it. - Click Save.
Stop the website and wait about a minute.
docker stop kuma-demo-siteKuma does not declare an outage right away. It draws the first two failed checks as orange bars, and only the third one in a row turns red. In our test the Down status appeared about a minute after the container was stopped, and until that moment not a single notification had been sent. Now start the website again.
docker start kuma-demo-siteOn the nearest check the monitor returned to Up, and a recovery entry appeared in the log. In our test this happened just a few seconds after the container was started.

Orange bars, a red outage and a green recovery
The error text depends on exactly how the service stopped responding. In our two stops it was connect ECONNREFUSED 172.21.0.3:80 and connect EHOSTUNREACH 172.21.0.3:80.
Step 7. Publish a status page
A status page is a public page where visitors see the state of your services without logging in to the dashboard. It is handy to show to clients so that they do not write to support at every outage.
- In the top menu, click Status Pages, then New Status Page.
- In the Name field, enter a title, for example
Service status. - In the Slug field, enter a part of the address, for example
main. The page will open at the address/status/main. A slug can contain only lowercase Latin letters, digits and hyphens. - Click Next.
The editor will open. The page settings are on the left and the preview is on the right.
- Description and Footer Text – the texts at the top and bottom of the page. They support Markdown.
- Refresh Interval – how often the page reloads itself, 300 seconds by default.
- Theme – light, dark or the same as the visitor's system.
- Domain Names – the addresses on which exactly this page is shown, which lets you keep several pages on one Kuma.
Select monitors in the Add a monitor list and click Save. Monitors can be arranged into groups with the Add Group button. On our page all the monitors ended up in the Services group, and we even added the closed port monitor there to show what an outage looks like for visitors.

The status page editor
Visitors see the page without the control buttons. We checked this by opening it in a separate window without being logged in to Kuma. If some of the monitors are down, the heading Partially Degraded Service appears at the top automatically.
If you need to explain the reason for an outage, click Create Incident in the editor. Fill in the Title and Content, click Post and then save the page, and the message will appear above the list of services. Later it can be edited, resolved or deleted, and the incident has the Edit, Resolve and Delete buttons for that.

The status page as a visitor sees it
How to show the page to visitors
While Kuma listens only on 127.0.0.1, only you can see the page, through the tunnel. There are two ways to let others see it.
- Open the port directly. In
compose.yaml, replace the line"127.0.0.1:3001:3001"with"3001:3001"and rundocker compose up -d. The page will become available athttp://server-IP:3001/status/main, but together with it the dashboard login window will also open to the internet over unencrypted HTTP. We checked that the port opens after such a replacement. - Put Kuma behind nginx with a domain and HTTPS. This is the main option for real use. Point a domain to the server using the guide, install nginx and get a free certificate. Kuma works over WebSocket, so nginx has to pass two more headers,
UpgradeandConnection. Ready-made configuration examples are on the Reverse Proxy page in the Uptime Kuma wiki. The same page says that to show different pages by domain, the proxy must pass Kuma theHostorX-Forwarded-Hostheader. We did not build this setup on the test server because ports 80 and 443 are taken by another service there.
The page does not update instantly
According to the Uptime Kuma wiki, the status page caches results for 5 minutes and updates itself once every 5 minutes. So it lags behind the dashboard, and that is normal.
Step 8. Set up notifications
The dashboard is only useful when you are looking at it, while notifications arrive on their own. Kuma can send them to dozens of services. The list of types includes Telegram, Discord, Slack, Email (SMTP), Webhook, Apprise and many others. We will go through the form using Telegram as an example.
Open the profile menu in the top right corner, choose Settings, then Notifications and click Set Up Notification. In the Notification Type list, Telegram is selected by default.

The Telegram notification form
The form has these fields.
- Friendly Name – the name of the notification, by which you will find it in the lists.
- Bot Token – the key of your bot. It is issued by the @BotFather bot, and there is a link to it under the field. The token is entered in hidden form, and the eye icon shows it.
- Chat ID – the identifier of the chat, group or channel the bot will write to. Kuma hints at how to find it. You need to send a message to the bot and open
https://api.telegram.org/bot<YOUR BOT TOKEN HERE>/getUpdatesin the browser, and the response will containchat_id. - Message Thread ID – an optional field for forum topics. It suits only supergroups with topics.
- Server Url – an optional field, which contains
https://api.telegram.orgby default. It is usually not changed. - Use custom message template – turn it on if you want to write the notification text yourself.
- Send Silently – messages arrive without a sound.
- Protect Forwarding/Saving – Telegram will not allow forwarding or saving the bot's messages.
- Default enabled – the notification will be turned on for every new monitor.
- Apply on all existing monitors – attaches the notification to all the monitors that already exist.
The Test button sends a test message, and Save saves the notification. We did not create a real bot for this article, so we only went through the Telegram form field by field.
A saved notification has to be attached to monitors. Open a monitor, click Edit and find the Notifications block on the right. It lists all the notifications you have created, with switches. Turn on the one you need and click Save. If you ticked Default enabled, the switch will already be on in new monitors.
We checked how notifications arrive using the Webhook type, which sends a request to any address. For every stop and start of the website container exactly two messages arrived, the first on the switch to Down and the second on recovery. The orange checks with retries did not trigger notifications. Kuma composes the notification text itself.
[My site] [🔴 Down] connect EHOSTUNREACH 172.21.0.3:80
[My site] [✅ Up] 200 - OKIf you want Kuma to remind you about a prolonged outage, the monitor form has the Resend Notification if Down X times consecutively field, and resending is disabled by default.
Planned work without extra notifications
The profile menu has the Maintenance item. According to the Uptime Kuma wiki, no notifications are sent for the selected monitors during a maintenance window, and a message about the work can be shown on status pages. Create such a window before you turn a service off for an update.
Step 9. Set up backups
Everything Kuma knows lies in the /opt/kuma/data folder. This is the administrator account, the monitors, the notifications, the status pages and the check history. Uptime Kuma 2 removed the old export of settings to JSON, and in the guide for migrating to the second version the developers state directly that copying the data folder remains the only supported backup method.
It has to be copied carefully. While Kuma is running, the SQLite database keeps fresh changes in separate files kuma.db-wal and kuma.db-shm, and we saw them next to kuma.db in a running Kuma. If you copy the files on the fly, the copy may turn out to be incomplete. That is why the script below stops Kuma for a few seconds, archives the folder and starts Kuma again. After the stop, only a single kuma.db file remains in the folder.
nano /opt/kuma/backup.sh#!/bin/sh
# Stops Kuma, archives the data folder and starts Kuma again
cd /opt/kuma || exit 1
mkdir -p backups
docker compose stop
tar -czf "backups/kuma-$(date +%F-%H%M).tar.gz" data
docker compose start
# Keeps the 7 latest archives
ls -1t backups/kuma-*.tar.gz | tail -n +8 | xargs -r rm --Make the script executable and run it manually.
chmod +x /opt/kuma/backup.sh
/opt/kuma/backup.sh
ls -lh /opt/kuma/backups
The script stopped Kuma, made an archive and started it again
On our server stopping and starting together took about 5 seconds, and the archive with five monitors weighed about 50 KB. During that time Kuma does not perform checks, so there will be a short gap in the chart.
To make copies automatically, add a job to the schedule, for example for 4 a.m. A file in the /etc/cron.d folder is simpler than editing the general schedule.
echo "0 4 * * * root /opt/kuma/backup.sh" > /etc/cron.d/kuma-backupWe checked the schedule by temporarily setting the run to every minute, and two new archives appeared within two minutes. You can read more about scheduling in the article about cron. Copies on the same server will not help if the server itself is lost, so from time to time take the archives to your own machine, for example with the rsync command.
How to restore Kuma from a backup
Stop Kuma, move the current data folder aside and unpack the archive. Instead of the archive name, put the file you need from the backups folder.
cd /opt/kuma
docker compose down
mv data data.old
tar -xzf backups/kuma-2026-10-21-1526.tar.gz
docker compose up -dWe checked the restore separately. We unpacked the archive into a clean folder, started a second Kuma from it and logged in with the same username and password. The monitors with their check history, the status page with the incident and the configured notification were all in place.
Step 10. Update Uptime Kuma
New versions of Kuma come out regularly and fix bugs and vulnerabilities. For example, the release notes for 2.5.6 on GitHub include both a bug fix and security updates of dependencies. Updating in Docker comes down to downloading a fresh image and creating the container again. The data stays in the data folder and is not affected. Before updating, make a backup as in Step 9.
cd /opt/kuma
docker compose pull
docker compose up -d --force-recreateThe first command downloads the fresh image for the 2 tag, and the second creates the container again.

The image is updated, the container is created again
You can see the installed version in the dashboard, in the profile menu, under Settings, then About. We checked the update on a separate instance. First we started Uptime Kuma 2.4.0 with the louislam/uptime-kuma:2.4.0 image, created an administrator and a monitor in it, then replaced the tag with 2 and ran the commands above. After that About showed version 2.5.6, the administrator logged in with their password, and the monitor stayed in place together with its check history.
Migrate from version 1 by following the guide
If you already run Uptime Kuma 1, do not update it by simply changing the tag. The developers' wiki has a separate guide for migrating to the second version. It says to make a copy of the data folder first, and the migration itself can take a long time and must not be interrupted. We did not test this migration.
If you need a specific version and not the latest one in the branch, specify it in image, for example louislam/uptime-kuma:2.5.6. This is how we started Uptime Kuma 2.4.0 in the update check.
Useful commands
docker compose ps # container status
docker logs uptime-kuma # Kuma log
docker compose stop # stop Kuma, the data stays
docker compose start # start Kuma again
docker compose pull # download the fresh image
docker compose up -d --force-recreate # recreate the container on the new image
docker exec -it uptime-kuma npm run reset-password # reset the administrator password
docker stats --no-stream uptime-kuma # memory and CPU of the containerAll the commands are run in the /opt/kuma folder.
Frequently asked questions
The monitor for 127.0.0.1:22 shows Down and Connection failed
Inside the container, the address 127.0.0.1 means the container itself, not the server. We checked this with a command from inside the container, and it was refused.
docker exec uptime-kuma bash -c "echo > /dev/tcp/127.0.0.1/22"The response will be Connection refused. In the monitor's Hostname field, enter host.docker.internal. If you did not add extra_hosts to compose.yaml, do it as in Step 1 and run docker compose up -d. Compose will recreate the container, and the data will not be harmed. After that the monitor turned green for us.
ERR_CONNECTION_REFUSED when opening localhost:3001
The browser found nothing on the port of your computer. Check three things.
- The terminal window with the
ssh -Lcommand must be open, because the tunnel works only while it is open. - The port in the address must match the first port in the
ssh -Lcommand. - The container on the server must be running, which the
docker compose pscommand shows.
You also will not be able to open the dashboard at http://server-IP:3001. The port listens only on 127.0.0.1, and for us such a connection simply hung until the timeout.
Cannot connect to the socket server. [Error: xhr poll error] Reconnecting...
The dashboard has lost its connection to Kuma. We got this message when we stopped the container with the docker compose stop command while the dashboard was open. After docker compose start the dashboard reconnected by itself. Under the message Kuma adds a hint, "Using a Reverse Proxy? Check how to config it for WebSocket". If Kuma is behind nginx, the cause is usually missing WebSocket headers, and a configuration example is on the Reverse Proxy page in the Uptime Kuma wiki.
Request failed with status code 404
The website responded, but with a code that is not among the allowed ones. By default, codes 200–299 are considered successful. We got this error while checking the non-existent page https://example.com/missing. Check the address in the URL field. If the website responds with a different code and that is intended, add it to the Accepted Status Codes field in the monitor form.
certificate has expired and self-signed certificate
The website has a problem with its HTTPS certificate. We got certificate has expired on the test website expired.badssl.com and self-signed certificate on self-signed.badssl.com. First, fix the certificate on the website itself if you can. If you need to watch a website that you trust but whose certificate is deliberately self-signed, turn on the Ignore TLS/SSL errors for HTTPS websites checkbox in the monitor form. After that the monitor with the expired certificate started showing 200 - OK for us.
200 - OK, but keyword is not in [...]
The website responded, but the word from the Keyword field is not on the page. We got this error by entering Hello world for example.com. Kuma searches for the word with the letter case taken into account and shows the beginning of the page it received in square brackets. Compare the word with the page and correct it in the monitor form.
getaddrinfo ENOTFOUND name
Kuma could not turn the name into an address. Check whether there is a typo in the name. If you are checking a container by name, like kuma-demo-site in Step 6, make sure it is running and connected to the same network as Kuma. We got this error for a non-existent domain, and also on a copy of Kuma that ran in a different Docker network and did not see the kuma-demo-site container.
Incorrect username or password. How to reset the password
We ran into this message when we logged in to Kuma with the old password after a reset. If you have forgotten the password, you can reset it with a command on the server.
docker exec -it uptime-kuma npm run reset-passwordThe program will find the user, ask for the new password twice and report Password reset successfully.. The password is shown on the screen as you type it, so do this when nobody is watching you.

Resetting the administrator password
Kuma shows Down, but the website opens in my browser
Kuma checks the website from its own server, so the response may differ from the one you see. The developers advise checking availability directly from the container. The image already has curl.
docker exec -it uptime-kuma bash
curl -I https://example.comIf you get the same error here, the problem is in the server's network, firewall or DNS, not in Kuma itself.
Our resources
Telegram channel: https://t.me/f3cloud_news
F3 Cloud: https://f3cloud.com