Traefik is up – now we hang the first real app behind it. Uptime Kuma is ideal for that: tiny, immediately useful and without a database. It monitors your services and raises the alarm when one fails – from now on you notice first that something is stuck, not your users. Along the way you learn the pattern every further app repeats.
What are we building?
By the end, Uptime Kuma 2.4 runs at https://status.YOUR_DOMAIN, secured via Traefik with automatic HTTPS. You've set up the first monitor (which checks every minute whether a service responds), connected a notification and optionally a public status page. For the first time we bind a service that doesn't listen on port 80 – an important detail for all the following apps.
Prerequisites
- A running Traefik reverse proxy including the
proxynetwork and a working Let's Encrypt resolverle - A DNS record
status.YOUR_DOMAINthat points to the server
Step by step
Step 1: The compose.yaml
Own folder, own file:
mkdir -p ~/uptime-kuma && cd ~/uptime-kuma
services:
uptime-kuma:
image: louislam/uptime-kuma:2
volumes:
- kuma-data:/app/data
networks:
- proxy
labels:
- "traefik.enable=true"
- "traefik.http.routers.kuma.rule=Host(`status.YOUR_DOMAIN`)"
- "traefik.http.routers.kuma.entrypoints=websecure"
- "traefik.http.routers.kuma.tls.certresolver=le"
- "traefik.http.services.kuma.loadbalancer.server.port=3001"
restart: unless-stopped
volumes:
kuma-data:
networks:
proxy:
external: true
You know this from the Traefik tutorial – except for one new, decisive line:
- "traefik.http.services.kuma.loadbalancer.server.port=3001"
Uptime Kuma listens internally on port 3001, not on 80. This line tells Traefik which port to forward the requests to. Without it, Traefik guesses wrong and you get a Bad Gateway. Remember the label – every app that doesn't run on port 80 needs it.
The rest is the familiar pattern: no ports: (only reachable via Traefik), proxy network, named volume for the data.
Step 2: Start and first login
docker compose up -d
docker compose logs -f uptime-kuma
When Kuma is ready, the log ends with this line – from here the service accepts requests (Ctrl+C only ends the following):
[SERVER] INFO: Welcome to Uptime Kuma
[SERVER] INFO: Uptime Kuma Version: 2.4.0
[SETUP-DATABASE] INFO: Listening on:
[SETUP-DATABASE] INFO: - http://localhost:3001
On the first start, Kuma creates its database in the volume – that takes a few seconds. Then open https://status.YOUR_DOMAIN in the browser. Uptime Kuma 2.x first asks for the database – for a setup like ours, SQLite is the right, simplest choice (select it, click Next). Right after that you create the admin account (username + strong password).
⚠️ Create the admin account immediately
As long as no admin account exists, anyone who opens the page can create one. Do this right after the first start – not "later".
Step 3: Create the first monitor
A monitor is a recurring check. Click Add New Monitor and create one for Traefik itself:
-
Monitor type:
HTTP(s) -
Friendly name:
Traefik Dashboard -
URL:
https://traefik.YOUR_DOMAIN -
Heartbeat interval:
60seconds -
Authentication: the Traefik dashboard is protected by basic auth (step 7 of the Traefik tutorial) – so choose HTTP Basic Auth as the authentication method below and enter the user and password. Without credentials, Kuma only gets
401and reports the monitor as Down.
Save – after a few seconds the monitor is Up (green) and shows the response time.
Besides the simple HTTP(s) check, it pays to choose the matching monitor type:
- HTTP(s) – Keyword: additionally checks whether a certain word appears in the response text. This detects "the server responds, but shows an error page".
- TCP port: for services without a web interface (e.g. a database, an SSH port). Only checks whether the port is open.
- Ping: the simplest reachability test via ICMP.
- Docker container: checks the container status directly via the Docker socket – handy for internal services without their own domain.
- Push: here the monitored service calls Kuma regularly. Ideal for cron jobs and backups: if the script doesn't report in time, Kuma raises the alarm ("dead man's switch").
Create the matching monitor for every important service.
💡 Tip
Check public services via their real domain (
https://…), not vialocalhost. This way you simultaneously test that Traefik and the certificate work from outside – not just that the container is running.
For the HTTP(s) monitor, the option "certificate expiry notification" is also worth it: Kuma then warns in good time before a TLS certificate expires. For services behind Traefik, Let's Encrypt renews automatically – but this very automation occasionally fails silently (a DNS record is changed, port 80 accidentally closed). The monitor is your safety net and reports in while there are still days to fix it, instead of visitors suddenly facing a certificate warning.
Step 4: Set up notifications (email and Telegram)
A monitor without an alarm is just a pretty chart. Under Settings → Notifications → Setup Notification you create a channel. Uptime Kuma supports over 90 – we set up the two most common completely: email for the classic message and Telegram for push straight to the phone.
Email (SMTP)
Choose "Email (SMTP)" as the notification service and enter your mail provider's data:
-
Hostname / port: e.g.
smtp.YOUR_PROVIDER.comand587 -
Security:
STARTTLS(port 587) orTLS/SSL(port 465) - Username / password: your SMTP credentials
- From / to address: which address the warning comes from and which it's sent to
Click Test – within a few seconds a test message lands in the mailbox. Only when it really arrives are the credentials and port correct. Don't forget to save.
Telegram
Telegram is ideal for instant push alerts to the phone – without your own mail server. You need two pieces of information:
-
Bot token: message @BotFather in Telegram, send
/newbot, assign a name – BotFather replies with the token. -
Chat ID: send your new bot any message, then open
https://api.telegram.org/bot<YOUR_TOKEN>/getUpdatesin the browser and read thechat.idfrom the response.
Enter the token and chat ID into the Telegram notification (Uptime Kuma links both helpers directly in the dialog) and click Test – the message should appear in the chat immediately.
⚠️ Otherwise the alarm stays silent
A configured notification does not take effect automatically. Enable it in each monitor (checkbox in the monitor form) or, in the notification dialog, turn on "Enabled by default" and "Apply on all existing monitors".
If you want push messages entirely under your own control (without a Telegram server), ntfy comes later – self-hosted, with its own recipe in the series.
Step 5: A public status page (optional)
Kuma can publish a status page where visitors see whether your services are running. Here's how:
-
Status Pages → New Status Page, then assign a name (e.g. "Serverküche Status") and a slug (the URL, e.g.
serverkueche) and click Next. - In the editor, Add Group (e.g. "Services"), and below it, add the desired monitors via the selection field.
-
Save at the top right – done. The page is then publicly reachable at
https://status.YOUR_DOMAIN/status/serverkueche.
Only include what really everyone may see – better leave internal services out.
Step 6: Avoid false alarms – fine-tune the alarm behavior
A monitor that raises the alarm at every brief network hiccup is quickly ignored – and then you miss the real outage. In the monitor form you set the behavior appropriately:
-
Retries: the service only counts as "Down" after n failed checks.
2–3filters out individual dropouts without obscuring real outages for long. - Heartbeat interval on failure: Kuma may check more often in the error case (e.g. every 20 seconds) to detect recovery quickly.
- Resend notification: Kuma can remind you every x minutes as long as a service is down – useful so an outage at night doesn't get lost in a single mail.
💡 Plan maintenance windows
If you plan an update with downtime, create a maintenance window under Maintenance. Kuma then pauses the alarms for the affected monitors – so you (and the status page) don't get false alarms while you're at work.
Step 7: Monitor cron jobs with a push monitor
Classic monitors check from outside whether a service responds. For things that run silently in the background – a nightly backup, a sync script, a cron job – the push monitor flips the principle: not Kuma asks, but your script reports in. If the report fails to come, Kuma raises the alarm – the classic "dead man's switch".
Create a monitor of type Push. Kuma then shows you a unique push URL:
https://status.YOUR_DOMAIN/api/push/YOUR_TOKEN?status=up&msg=OK&ping=
You call this URL at the end of your script – for example after a successfully completed backup:
# ... your backup command ...
curl -fsS "https://status.YOUR_DOMAIN/api/push/YOUR_TOKEN?status=up&msg=Backup+OK" > /dev/null
Set the check interval in Kuma a bit more generously than your cron cadence (if the backup runs hourly, give Kuma e.g. 90 minutes of tolerance). If no curl comes in that time, the monitor goes to Down and you're notified – so you learn about a backup that did not run, not only when you urgently need it.
Step 8: Secure the admin login with 2FA
Your Kuma login protects access to all monitors, the stored notification credentials and the status-page configuration – and it's publicly on the net. So enable two-factor authentication: under Settings → Security → Two-Factor Authentication. Kuma shows a QR code you scan with an authenticator app (e.g. Aegis or 2FAS); to activate, you enter the generated code once.
💡 Secure the recovery
Keep the TOTP secret or a second authenticator in a safe place (password manager). If you lose your phone and have no copy, you can otherwise only get back in via the database in the
kuma-datavolume.
When things go wrong
Bad Gateway (502) when opening status.YOUR_DOMAIN. Almost always the port label traefik.http.services.kuma.loadbalancer.server.port=3001 is missing or has a wrong port. Traefik then reaches the container but knocks on the wrong port. Check the label and run docker compose up -d again.
404 page not found instead of Kuma. As with every app behind Traefik: traefik.enable=true set? Container on the proxy network? Is the domain in the Host(...) rule correct and does the DNS record status.YOUR_DOMAIN point to the server? The Traefik dashboard shows under "HTTP Routers" whether kuma is registered.
The interface loads, but the live update stutters / breaks off. Kuma uses WebSockets. Traefik forwards those correctly by default – if the problem still occurs, it's usually an upstream CDN/proxy (e.g. Cloudflare in "proxy" mode) that blocks WebSockets. For direct operation behind Traefik, no extra configuration is needed.
After a re-setup all monitors are gone. The kuma-data volume was deleted (e.g. by docker compose down -v). All configuration and history lives solely in this volume – that's why it's at the top of the next section.
Maintenance & backups
-
Back up: the complete heart of Kuma is the
kuma-datavolume (a SQLite database). Back it up regularly – if it's gone, all monitors and the history are gone. We build the off-site backup for it in the Restic tutorial. -
Updates: the tag
:2stays on the 2.x series and brings bug fixes withdocker compose pull && docker compose up -d. Before a jump to a new major version (e.g. later:3), read the release notes and back up the volume first. -
Coming from 1.x? The switch to
:2migrates the SQLite database automatically on the first start – that can take a moment, and going back to:1is not intended afterwards. So back up thekuma-datavolume beforehand, then you're on the safe side. New installations (like above) aren't affected. - Honest limitation: a monitor that runs on the same server as the monitored services can't warn you when the whole server fails – then Kuma is offline too. For that case, add an external watcher. Two cheap ways: a second Uptime Kuma on a small server (or at home) that only monitors this instance via HTTP – or a free external ping service that pings your public status page. This way you also get a notice when the whole host is gone – the only case a local monitor inherently can't cover.
With that you've internalized the app pattern and monitor everything you hang behind Traefik from now on. What every further app requires are encrypted off-site backups with Restic – so your data survives a server crash.
This post first appeared on serverkueche.de.





Top comments (0)