To self-host Uptime Kuma you run one Docker container, open port 3001, and add a monitor for each site or service you care about. This guide takes you from a fresh Linux VPS to working HTTP checks, TCP port checks and alerts that reach you by email or Telegram.
Uptime Kuma is a small self-hosted monitoring tool: it pings your endpoints on a schedule, records response times, and notifies you when something stops answering. Because you host it yourself, your check history and alert credentials stay on your own server.
Before you start
You need:
- A Linux VPS running Ubuntu 22.04/24.04, Debian 12, or AlmaLinux/Rocky 8–9, with root or sudo access.
- Docker Engine and the Docker Compose plugin. If they are not installed yet, follow the official Docker install docs for your distribution, then confirm:
docker --version
docker compose version- A domain name if you plan to publish a public status page. You can finish the install without one.
- Outbound access from the VPS to whatever you monitor. Note that many providers block outbound SMTP port 25 — use port 587 or 465 for mail alerts.
Log in over SSH and keep the server's IP handy. The examples below use 203.0.113.10.
Self-host Uptime Kuma with Docker Compose
Create the project directory
Keep the stack in its own folder so upgrades and backups stay simple.
sudo mkdir -p /opt/uptime-kuma
cd /opt/uptime-kumaWrite the Compose file
Create /opt/uptime-kuma/compose.yaml:
services:
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
restart: unless-stopped
ports:
- "3001:3001"
volumes:
- uptime-kuma-data:/app/data
volumes:
uptime-kuma-data:The named volume is what holds your monitors, users and history. Never start the container without it, or you lose everything on the next recreate.
Tip: Pin the image to a major tag such as :1 and upgrade on purpose. Check the project's release notes before a big jump so you know about database migrations.
Start the container
docker compose up -d
docker compose logs -fWait for a line about the server listening on port 3001, then press Ctrl+C to leave the logs. Confirm the container is healthy:
docker compose psOpen the firewall
sudo ufw allow 3001/tcp
sudo ufw reloadWarning: Port 3001 gives full access to the admin setup on first visit. If your provider has a cloud firewall or security group, restrict 3001 to your own IP, and put the app behind HTTPS as shown below before you rely on it. Do not leave it open to the whole internet longer than necessary.
Create your admin account and first monitor
Open http://203.0.113.10:3001 in a browser. Uptime Kuma asks you to create the first user — choose a long password and store it in your password manager. That account is the administrator; every monitor and notification lives under it.
Once you are logged in, click Add New Monitor and fill in:
- Monitor Type —
HTTP(s)for websites,TCP Portfor services like SSH or a database. - Friendly Name — something you will recognise in an alert, for example
example.com homepage. - URL or Hostname and Port.
- Heartbeat Interval — how often to check. Start with 60 seconds; shorter intervals create more traffic and more noise.
- Retries — how many failed checks in a row before it is marked down.
2or3avoids alerts from a single blip.
Save, and the monitor starts immediately. The dashboard shows a heartbeat bar per check, plus response time.
Add HTTP and TCP monitors
A practical starter set for one VPS:
- HTTP(s) on
https://example.comwith keyword checking enabled, so you catch a page that returns 200 but shows an error message. - TCP Port on
203.0.113.10:22to know SSH is reachable. - TCP Port on
203.0.113.10:3389if you also run a Windows VPS with Remote Desktop. - HTTP(s) on each self-hosted app's health endpoint, for example
https://cloud.example.com/status.phpfor Nextcloud.
To check for a keyword, expand the monitor, tick Keyword, and enter a word that only appears on a healthy page, such as your company name. Set the condition to "exists".
For anything behind HTTPS with a certificate you manage yourself, leave certificate expiry checking on — Uptime Kuma warns you before a renewal is missed.
You can also group monitors with Tags and build a public Status Page under the status page menu. Only publish a status page if you are happy for the world to see which services exist.
Set up alerts to email or Telegram
Monitors are only half the job. Add a notification so you hear about an outage.
Email (SMTP)
Go to Settings → Notifications → Setup Notification, choose Email (SMTP), and enter:
- Host and port from your mail provider — usually
587with STARTTLS or465with TLS. Avoid port 25; many providers block it outbound. - Username and password for the sending account.
- From and To addresses.
Use the Test button. If it fails, the error message from the mail server is usually specific enough to fix the setting.
Telegram
- Message
@BotFatherin Telegram, create a bot, and copy the token. - Send your new bot any message, then open
https://api.telegram.org/bot<TOKEN>/getUpdatesin a browser and copy thechat.idvalue. - In Uptime Kuma, choose Telegram, paste the bot token and chat ID, and test.
Tip: Add the notification to Default notifications so new monitors inherit it, then override per monitor where you want a different channel.
Put Uptime Kuma behind HTTPS
Running the dashboard on plain HTTP means your admin password crosses the network in clear text. Two common options:
- Terminate TLS with Nginx Proxy Manager and proxy
uptime.example.comtohttp://127.0.0.1:3001. This gives you a Let's Encrypt certificate and a single place to manage certificates for your other apps. - Use Traefik with Docker labels if your stack already runs it.
Whichever you choose, once HTTPS works, remove the public port mapping so 3001 is only reachable from the proxy, then reopen the dashboard on the domain.
Check it works
- From your own machine, open
http://203.0.113.10:3001(or your HTTPS domain) and confirm the dashboard loads. - Confirm the container restarts cleanly and keeps data:
cd /opt/uptime-kuma
docker compose restart
docker compose ps- In the UI, your monitors should still be listed with their history intact — proof the volume is mounted correctly.
- Force a failure to test alerting: add a TCP monitor for
203.0.113.10:65000, which should be closed. Within a couple of intervals it goes down and your email or Telegram alert should arrive. - Delete that test monitor.
Troubleshooting
- The container restarts in a loop. Check
docker compose logs uptime-kuma. The usual causes are a missing./datavolume, or an existing file where the volume should mount. Recreate withdocker compose down && docker compose up -dafter fixing the volume block. - The browser cannot reach the dashboard. Test from the server itself with
curl -I http://127.0.0.1:3001. If that works, the problem is the firewall or your provider's security group, not the container. - Monitors report "down" for a site you know is up. Run
curl -I https://example.comanddig example.comfrom the VPS. DNS resolution or an IPv6 preference is often the culprit; disable IPv6 per monitor if the target has no AAAA record. - No alerts arrive. For email, confirm the port is not blocked (
587/465, not25) and read the SMTP error in the test result. For Telegram, re-check the bot token and chat ID — the bot cannot message you until you have sent it a message first. - All history disappeared after an upgrade. The container was recreated without the data volume. Restore from a backup of the
uptime-kuma-datavolume; going forward, snapshot it before upgrades.
Next steps
- Add HTTPS to the dashboard with Nginx Proxy Manager in Docker.
- Pair uptime checks with deeper metrics using Prometheus, cAdvisor and Grafana.
- Deploy your next service the same way with Deploy a Self-Hosted App with Docker Compose.
