Uptime Kuma 2 with Docker: Monitor Your Sites and Services (Linux, Windows, Mac)
Before you start
- 16 min read
Boxes marked Run in terminal are what you type; plain text explains why. Check what you need first

What you get in one minute#
Uptime Kuma is a free, self-hosted monitoring tool: it checks your websites, servers and containers on a timer, shows the history on a dashboard and sends you an alert the moment something stops answering.
People use it to know about a dead website before a customer does, to watch home-lab services, and to publish a public status page. Version 2 is the current line. It adds a choice of database, a faster storage format and many fixes. This guide installs it with Docker and keeps your data in one folder you can back up.
At a glance
- What you get: a dashboard, alerts to Telegram, Discord, email and 90+ other services, and public status pages.
- Time needed: about 10 minutes for the install, 20 more for HTTPS.
- Difficulty: easy. You copy a Compose file and run one command.
- You need: Docker, a terminal, and a computer that stays on.
- Version used: 2.5.7 (see the table).
| What | Value |
|---|---|
| Current version | 2.5.7, released 2026-10-23 (GitHub releases) |
| Pinned image | louislam/uptime-kuma:2.5.7 (official image, the tag exists on Docker Hub) |
| Port | 3001 (web interface) |
| Data | ~/docker_volumes/uptime-kuma/data (SQLite database by default) |
| Official docs | Uptime Kuma wiki |
Which version, and why. I looked at the issue tracker for the weeks around 2.5.7. I found no reports of failed installs or lost data. I found one narrow behaviour change: TCP monitors with SSL against a self-signed certificate show as down after the update (the maintainer says 2.5.6 wrongly showed them as always up) (#8019, open at the time of writing). If you use that exact monitor type, wait for a fix or stay on 2.5.6. Everyone else can use 2.5.7. The maintainers also publish a moving 2 tag; this guide pins the exact number so an update is always your choice.
How it fits together#
Uptime Kuma runs a check against each target, judges the answer, retries before it calls something down, then updates the dashboard and sends your alert.
Everything Uptime Kuma knows lives in the data folder. Keep that folder safe and you can rebuild the rest in minutes.
Before you start#
You need Docker with the Compose plugin and a machine that runs all day.
| System | What you need |
|---|---|
| Linux | Docker Engine with the Compose plugin, bash, curl |
| Windows | Docker Desktop (WSL2 backend), PowerShell 7 |
| Mac | Docker Desktop for Mac, Terminal |
- The README and wiki list no official RAM or CPU numbers (checked 2026-10-26). It runs on small machines such as a Raspberry Pi or a 1 GB VPS.
- Do not run it on a laptop you close at night. A monitor that sleeps cannot warn you.
- Do not put the data folder on NFS. The README says network file systems are not supported.
- Debian or Raspbian "Buster" hosts should not run v2: the migration guide warns about a startup bug there.
Checklist:
- [ ] Docker works:
docker --versionanddocker compose version - [ ] Port 3001 is free on the host
- [ ] If you are upgrading from 1.x: read the migration section first
- [ ] Only for the HTTPS section: a domain, DNS pointing at your server, ports 80 and 443 open
Checked against the official docs on 2026-10-26. Not run by DIY Lab Hub: Linux, Windows and Mac commands follow the official wiki and README.
What changed in version 2#
The short version: a newer database layer, a slimmer image, and a few removals. Read this table before you upgrade a 1.x install.
| Topic | In v2 |
|---|---|
| Database | SQLite (default) or MariaDB. Settings come from UPTIME_KUMA_DB_* variables. |
| Image tags | 2 and 2-slim (moving), 2.x.x (pinned), -rootless variants. The latest tag is deprecated and still points to v1. |
| Alpine image | Dropped. The Debian-based image is used. |
| Backup | The JSON backup/restore feature is gone. Backing up the data folder is the only supported method. |
| New monitors | Retries now default to 0 instead of 1. |
| Email notifications | Subject and body templates use LiquidJS. Variables are case-sensitive. |
| Badges | The :duration part only accepts values like 24, 24h, 30d, 1y. |
| Migration | Heartbeats are rewritten into a new format on first start. |
Source: the official migration guide and Docker tags pages.
How long does the migration take? The maintainer's own install had 20 monitors and 90 days of data and took about 7 minutes. Slower hardware or more monitors can take hours. Do not stop it halfway. If it is interrupted, restore your backup and try again.
Step 1: Create the folders#
Make one folder tree for the app so the data lives where you can see and back it up.
Linux#
mkdir -p ~/docker_volumes/uptime-kuma/{data,backups}
cd ~/docker_volumes/uptime-kuma
Windows (PowerShell)#
New-Item -ItemType Directory -Force "$HOME\docker_volumes\uptime-kuma\data","$HOME\docker_volumes\uptime-kuma\backups" | Out-Null Set-Location "$HOME\docker_volumes\uptime-kuma"
Mac (Terminal)#
Same commands as Linux:
mkdir -p ~/docker_volumes/uptime-kuma/{data,backups}
cd ~/docker_volumes/uptime-kuma
Check it worked: ls data (Linux/Mac) or dir data (Windows) shows an empty folder.
Step 2: Write the Compose file#
Save this as compose.yaml in the app folder. It publishes the port on the loopback address only, so nobody on your network can reach it until you decide so.
services:
uptime-kuma:
image: louislam/uptime-kuma:2.5.7
container_name: uptime-kuma
restart: unless-stopped
volumes:
- ./data:/app/data
ports:
- "127.0.0.1:3001:3001"
./datais~/docker_volumes/uptime-kuma/data, because the file sits one level above it.- The official Compose file uses
"3001:3001"andimage: louislam/uptime-kuma:2. Mine pins the version and binds to127.0.0.1. The README shows this127.0.0.1form for a localhost-only install. - Want to reach it from other computers on your home network? Change the port line to
"3001:3001".
Linux#
nano compose.yaml
Windows (PowerShell)#
notepad compose.yaml
Mac (Terminal)#
Same commands as Linux:
nano compose.yaml
Potential issue: on Windows, Notepad may add .txt. Save as "All files" and check the name.
Step 3: Start it#
Pull the image and start the container in the background.
Linux#
docker compose up -d docker compose ps
Windows (PowerShell)#
docker compose up -d docker compose ps
Mac (Terminal)#
Same commands as Linux:
docker compose up -d docker compose ps
Check it worked: docker compose ps should show uptime-kuma as running. Follow the log if you like:
docker compose logs -f
Potential issue: on Windows with the data folder in your profile, SQLite sometimes complains about locking. If you see it, use a named volume instead (replace the volume line with uptime-kuma-data:/app/data and add a top-level volumes: block).
First-run setup#
Open the page, make your admin account, and add one monitor.
- Open
http://localhost:3001. - If the page asks which database to use, pick SQLite. It needs no extra container.
- Choose a language, a username and a long password. Use a password manager.
- Click Add New Monitor.
- Set Monitor Type to HTTP(s), give it a name and enter your site's URL.
- Leave the interval at the default. The 2.5.7 source allows an interval as short as 1 second, but 60 is gentler on everything.
- Set Retries to 2 or 3 so a single blip does not wake you up.
- Save.
Check it worked: the monitor shows a green Up badge within a few seconds.
Alerts and status pages#
A monitor you never hear from is a dashboard decoration. Add one notification, then test it.
- Go to Settings, Notifications, Setup Notification.
- Pick a service: Telegram, Discord, Slack, Pushover, Gotify, email (SMTP) and many others.
- Fill the fields the form asks for (for Telegram: bot token and chat ID).
- Press Test. Nothing arrives? Fix it here, not during an outage.
- Tick Default enabled so new monitors use it, and Apply on all existing monitors.
Monitor types worth knowing:
| Type | Use it for |
|---|---|
| HTTP(s) | A website or API returns a good status |
| HTTP(s) Keyword / JSON Query | The page really contains what it should |
| TCP Port | A service listens on a port |
| Ping | A server or router is reachable |
| DNS | A record resolves as expected |
| Push | A cron job checks in by calling a URL |
| Docker Container | A container is running |
Status pages. Open Status Pages, New Status Page, choose a name and a short URL slug, add groups and monitors, and save. You can map a page to its own domain. Keep internal monitors off public pages: names and URLs are visible to everyone who sees the page.
Optional: watch your containers (Docker socket)#
Uptime Kuma can monitor Docker containers, but it needs the Docker socket, and that is a real security trade-off.
The socket is the control panel of Docker. Any program that can use it can start containers and read the host. A :ro flag on the mount does not make the Docker API read-only. So:
- Only add it if you need container monitors.
- Never expose Uptime Kuma to the internet while it holds the socket.
- Protect the login with a strong password and 2FA (Settings, Security).
To enable it on Linux or Mac, add this line under volumes: in the Compose file and run docker compose up -d again:
- /var/run/docker.sock:/var/run/docker.sock:ro
Then add a Docker Host under Settings, Docker Hosts with the socket path /var/run/docker.sock. On Docker Desktop for Windows and Mac the same mount is commonly used, but we found no official statement, so test it on your version. A better route for anything exposed: skip the socket and use plain HTTP or TCP monitors.
Known limit: a container that keeps crashing and restarting can still show as up (#7956, open at the time of writing). Add an HTTP monitor for the service as well.
Put it behind HTTPS (with or without Traefik)#
With Traefik#
This section is for a server with a domain. If you only use Uptime Kuma at home, skip it.
You need DNS for <your-domain> pointing at the server, ports 80 and 443 open, and Traefik already running with a certificate resolver. Create the shared network once if it is missing:
docker network create proxy
Then use this Compose file. Nothing is published on the host; Traefik reaches the container over the proxy network.
services:
uptime-kuma:
image: louislam/uptime-kuma:2.5.7
container_name: uptime-kuma
restart: unless-stopped
volumes:
- ./data:/app/data
networks:
- proxy
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.kuma.rule=Host(`<your-domain>`)
- traefik.http.routers.kuma.entrypoints=https
- traefik.http.routers.kuma.tls=true
- traefik.http.routers.kuma.tls.certresolver=<your-resolver>
- traefik.http.services.kuma.loadbalancer.server.port=3001
networks:
proxy:
external: true
Replace the entrypoint and resolver names with the ones in your Traefik setup. Uptime Kuma needs WebSockets, and Traefik passes them through without extra headers. Nginx or Apache would need the Upgrade and Connection headers.
After it starts, open Settings, Reverse Proxy and set Trust Proxy to Yes, but only if Traefik is the only way in. Uptime Kuma does not support running under a sub-path such as /kuma. Use a sub-domain.
Without Traefik: Caddy#
Choose Caddy when you have no Traefik yet and run one small server. Facts are from caddyserver.com/docs.
| Your case | What Caddy does | You need |
|---|---|---|
| Public domain | Automatic Let's Encrypt certificate | DNS A record to the server, ports 80 and 443 open |
| Home only, no domain | tls internal: Caddy's own local certificate | Trust Caddy's root certificate on each device |
Public domain, Caddyfile for Uptime Kuma:
app.example.com {
reverse_proxy localhost:3001
}
Home only:
app.home.arpa {
tls internal
reverse_proxy localhost:3001
}
Trust the root certificate (home only): on the Caddy machine run sudo caddy trust (Linux). On Windows and Mac copy pki/authorities/local/root.crt from Caddy's data folder and import it as a trusted root (Windows certificate manager, Mac Keychain Access).
Run Caddy (2.11.7 was current on 2026-10-26): Linux with the official apt repository from the install page, then sudo systemctl reload caddy; Windows winget install CaddyServer.Caddy then caddy run; Mac brew install caddy then caddy run. Or use Docker: put caddy:2.11.7 on the same network as the app, drop the app's ports: line and write reverse_proxy uptime-kuma:3001. Save the Caddyfile as ~/docker_volumes/caddy/conf/Caddyfile (Linux/Mac: mkdir -p ~/docker_volumes/caddy/conf; Windows PowerShell: New-Item -ItemType Directory -Force "$HOME\docker_volumes\caddy\conf") and the Compose file below as ~/docker_volumes/caddy/docker-compose.yml.
services:
caddy:
image: caddy:2.11.7
container_name: caddy
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./conf:/etc/caddy:ro
- ~/docker_volumes/caddy/data:/data
- ~/docker_volumes/caddy/config:/config
WebSockets work without extra settings. After it starts, open Settings, Reverse Proxy and set Trust Proxy to Yes, only if Caddy is the only way in.
Check it worked: curl -I https://app.example.com answers HTTP/2 200 (or the login redirect). On Windows use curl.exe.
Security and testing#
Keep it private by default and test the lock.
| Do this | Why |
|---|---|
Bind to 127.0.0.1 or sit behind Traefik | The login page should not face the internet raw |
| Strong password plus 2FA | The admin can reach every monitor and notification secret |
| No Docker socket unless you need it | The socket is root on the host |
| Keep tokens out of the Compose file | If you use MariaDB, load credentials from .env or a _FILE variable |
| Update on a schedule | The 2.5.7 release itself shipped a security fix in the image |
If you use an external MariaDB, the official variables are UPTIME_KUMA_DB_TYPE, UPTIME_KUMA_DB_HOSTNAME, UPTIME_KUMA_DB_PORT, UPTIME_KUMA_DB_NAME, UPTIME_KUMA_DB_USERNAME and UPTIME_KUMA_DB_PASSWORD. The username, password and CA also accept _FILE versions for Docker secrets. Keep the .env file private:
chmod 600 .env
Tests:
docker compose ps curl -I http://127.0.0.1:3001 curl -I https://<your-domain>
The first curl should answer with an HTTP status. The second one should show HTTP/2 200 or a redirect to the login page. On Windows PowerShell use curl.exe instead of curl.
Troubleshooting#
| Problem | Solution |
|---|---|
| Page loads, but "Cannot connect to the socket server. Reconnecting..." | The proxy is not passing WebSockets. Add the Upgrade and Connection headers (nginx/Apache) or use Traefik as above. |
Bind for 0.0.0.0:3001 failed: port is already allocated | Something else uses 3001. Change the left number, e.g. "3002:3001". |
SQLITE_BUSY: database is locked | Put the data on a local disk or named volume, not NFS. Do not turn off the single-connection default unless you know why. |
| Migration from 1.x is slow | Normal. Watch docker compose logs -f and do not stop it. |
Invalid header value char on a site behind a WAF | Set NODE_OPTIONS=--insecure-http-parser in the container environment. |
| Self-signed HTTPS site shows as down | Tick "Ignore TLS/SSL errors" on that monitor, or mount your CA and set NODE_EXTRA_CA_CERTS. |
| Alerts do not arrive | Press Test on the notification. Check the log. Behind a proxy: set NOTIFICATION_PROXY. |
| Docker monitor says permission denied | The container cannot read /var/run/docker.sock. Check the mount. |
| Container will not start on an old Debian | Debian/Raspbian Buster has a libseccomp2 bug. Update the host. |
| Rootless image fails after upgrading | The wiki warns against rootless images when upgrading from v1. Use the normal image. |
Known bugs#
- TCP port monitor with SSL and a self-signed certificate fails after 2.5.7 (#8019). The maintainer says 2.5.6 wrongly showed such monitors as up and calls the TCP TLS feature buggy. Workaround: switch it to a plain TCP check, or pin 2.5.6.
- Incomplete SMTP STARTTLS (#8026). If email alerts fail with STARTTLS, try another port or a different notification channel.
Diagnostics:
docker logs uptime-kuma --tail 100
docker compose ps
docker inspect uptime-kuma --format '{{.State.Status}}'
Updates and backups#
Back up the data folder before every update. It is the only supported backup method in v2.
Update#
docker compose pull docker compose up -d --force-recreate
That is the official command. To move to a newer release, change the tag in compose.yaml first. Look for the new version on the releases page and read the notes. For a new image, wait if Docker Hub does not have the tag yet; the wiki says the build takes a while after each release.
Backup script#
SQLite copies safely only when the app is stopped, so the script stops it for a few seconds.
Linux#
cd ~/docker_volumes/uptime-kuma docker compose stop tar -czf backups/kuma-$(date +%F).tar.gz data docker compose start
Windows (PowerShell)#
Set-Location "$HOME\docker_volumes\uptime-kuma" docker compose stop Compress-Archive -Path data -DestinationPath "backups\kuma-$(Get-Date -Format yyyy-MM-dd).zip" -Force docker compose start
Mac (Terminal)#
Same commands as Linux:
cd ~/docker_volumes/uptime-kuma docker compose stop tar -czf backups/kuma-$(date +%F).tar.gz data docker compose start
Schedule it#
- Linux / Mac: run
crontab -eand add0 3 * * * cd ~/docker_volumes/uptime-kuma && docker compose stop && tar -czf backups/kuma-$(date +\%F).tar.gz data && docker compose start - Windows: save the three PowerShell lines in
kuma-backup.ps1, then create a daily 03:00 task with Task Scheduler (action:pwsh.exe -File <path>\kuma-backup.ps1).
Copy the backups folder off the machine too. A backup on the same disk dies with it.
Restore test#
Do this once on a spare folder, before you need it.
- Stop the app:
docker compose down. - Rename
datatodata-old. - Unpack the newest archive into the app folder:
tar -xzf backups/kuma-<date>.tar.gz(Windows:Expand-Archive). - Start:
docker compose up -d. - Log in. Your monitors and history should be back.
- Happy? Delete
data-old.
Performance and resources#
It is light. I did not measure it for this guide, so check your own numbers.
- Run
docker stats uptime-kumaafter a day. That tells you the real RAM and CPU on your hardware. - More monitors and shorter intervals mean more work. 60 seconds is plenty for most sites.
- Keep a cap on history: Settings, Monitor History sets how many days of data to keep. Less data means a smaller database and a quicker backup.
- SQLite is fine for a home lab or a small team. Think about MariaDB only for very large setups; it is an extra service to run and back up.
- Prefer the
-slimimage only if you do not need the extras it leaves out (check the Docker tags page).
For server health (CPU, memory, disk), a companion tool such as Beszel pairs well with Uptime Kuma, which only asks "is it answering?".
FAQ#
Is Uptime Kuma 2 stable enough to use? Yes. It is the current main line and the project ships point releases every few weeks. Pin an exact tag and read the release notes before you update.
Can I upgrade straight from 1.x? Yes, with a backup first. Change the tag to 2.5.7, start it and watch the logs while it converts your history. Expect minutes to hours.
Should I use the latest tag? No. It is deprecated and points to v1. Use 2 if you want automatic minor updates, or an exact tag like this guide does.
Can it monitor things behind my firewall? Yes, when it runs inside the same network. For a public status page with private monitors, host it on the inside and publish only the status page through your proxy.
Do I need MariaDB? Probably not. SQLite is the default and is enough for most people.
Sources#
- Uptime Kuma README and Compose file (read on 2026-10-26)
- Release 2.5.7, 2026-10-23
- Migration From v1 To v2 (wiki, read on 2026-10-26)
- Docker Tags (wiki, read on 2026-10-26)
- Environment Variables (wiki, read on 2026-10-26)
- How to Update (wiki, read on 2026-10-26)
- Reverse Proxy (wiki, read on 2026-10-26)
- Docker Hub image
- Issues: #8019, #8026, #7956
- Caddy docs: automatic HTTPS, tls internal, reverse_proxy, install (read on 2026-10-26)
Checked against the official docs on 2026-10-26.