Self-HostingDocker

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).
WhatValue
Current version2.5.7, released 2026-10-23 (GitHub releases)
Pinned imagelouislam/uptime-kuma:2.5.7 (official image, the tag exists on Docker Hub)
Port3001 (web interface)
Data~/docker_volumes/uptime-kuma/data (SQLite database by default)
Official docsUptime 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.

How Uptime Kuma 2 works: websites, servers and optional Docker containers are checked on a timer; if a target is still up the result goes to the dashboard and status page and is saved in the SQLite data folder; if not, Uptime Kuma retries, marks it down and sends an alert by email, Telegram or Discord

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.

SystemWhat you need
LinuxDocker Engine with the Compose plugin, bash, curl
WindowsDocker Desktop (WSL2 backend), PowerShell 7
MacDocker 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 --version and docker 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.

TopicIn v2
DatabaseSQLite (default) or MariaDB. Settings come from UPTIME_KUMA_DB_* variables.
Image tags2 and 2-slim (moving), 2.x.x (pinned), -rootless variants. The latest tag is deprecated and still points to v1.
Alpine imageDropped. The Debian-based image is used.
BackupThe JSON backup/restore feature is gone. Backing up the data folder is the only supported method.
New monitorsRetries now default to 0 instead of 1.
Email notificationsSubject and body templates use LiquidJS. Variables are case-sensitive.
BadgesThe :duration part only accepts values like 24, 24h, 30d, 1y.
MigrationHeartbeats 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#

Run in terminalbash
mkdir -p ~/docker_volumes/uptime-kuma/{data,backups}
cd ~/docker_volumes/uptime-kuma

Windows (PowerShell)#

Codepowershell
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:

Run in terminalbash
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.

File contentyaml
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"
  • ./data is ~/docker_volumes/uptime-kuma/data, because the file sits one level above it.
  • The official Compose file uses "3001:3001" and image: louislam/uptime-kuma:2. Mine pins the version and binds to 127.0.0.1. The README shows this 127.0.0.1 form for a localhost-only install.
  • Want to reach it from other computers on your home network? Change the port line to "3001:3001".

Linux#

Run in terminalbash
nano compose.yaml

Windows (PowerShell)#

Run in terminalpowershell
notepad compose.yaml

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
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#

Run in terminalbash
docker compose up -d
docker compose ps

Windows (PowerShell)#

Run in terminalpowershell
docker compose up -d
docker compose ps

Mac (Terminal)#

Same commands as Linux:

Run in terminalbash
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:

Run in terminalbash
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.

  1. Open http://localhost:3001.
  2. If the page asks which database to use, pick SQLite. It needs no extra container.
  3. Choose a language, a username and a long password. Use a password manager.
  4. Click Add New Monitor.
  5. Set Monitor Type to HTTP(s), give it a name and enter your site's URL.
  6. 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.
  7. Set Retries to 2 or 3 so a single blip does not wake you up.
  8. 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.

  1. Go to Settings, Notifications, Setup Notification.
  2. Pick a service: Telegram, Discord, Slack, Pushover, Gotify, email (SMTP) and many others.
  3. Fill the fields the form asks for (for Telegram: bot token and chat ID).
  4. Press Test. Nothing arrives? Fix it here, not during an outage.
  5. Tick Default enabled so new monitors use it, and Apply on all existing monitors.

Monitor types worth knowing:

TypeUse it for
HTTP(s)A website or API returns a good status
HTTP(s) Keyword / JSON QueryThe page really contains what it should
TCP PortA service listens on a port
PingA server or router is reachable
DNSA record resolves as expected
PushA cron job checks in by calling a URL
Docker ContainerA 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:

File contentyaml
      - /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:

Run in terminalbash
docker network create proxy

Then use this Compose file. Nothing is published on the host; Traefik reaches the container over the proxy network.

File contentyaml
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 caseWhat Caddy doesYou need
Public domainAutomatic Let's Encrypt certificateDNS A record to the server, ports 80 and 443 open
Home only, no domaintls internal: Caddy's own local certificateTrust Caddy's root certificate on each device

Public domain, Caddyfile for Uptime Kuma:

File contentcaddyfile
app.example.com {
    reverse_proxy localhost:3001
}

Home only:

File contentcaddyfile
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.

File contentyaml
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 thisWhy
Bind to 127.0.0.1 or sit behind TraefikThe login page should not face the internet raw
Strong password plus 2FAThe admin can reach every monitor and notification secret
No Docker socket unless you need itThe socket is root on the host
Keep tokens out of the Compose fileIf you use MariaDB, load credentials from .env or a _FILE variable
Update on a scheduleThe 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:

Run in terminalbash
chmod 600 .env

Tests:

Run in terminalbash
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#

ProblemSolution
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 allocatedSomething else uses 3001. Change the left number, e.g. "3002:3001".
SQLITE_BUSY: database is lockedPut 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 slowNormal. Watch docker compose logs -f and do not stop it.
Invalid header value char on a site behind a WAFSet NODE_OPTIONS=--insecure-http-parser in the container environment.
Self-signed HTTPS site shows as downTick "Ignore TLS/SSL errors" on that monitor, or mount your CA and set NODE_EXTRA_CA_CERTS.
Alerts do not arrivePress Test on the notification. Check the log. Behind a proxy: set NOTIFICATION_PROXY.
Docker monitor says permission deniedThe container cannot read /var/run/docker.sock. Check the mount.
Container will not start on an old DebianDebian/Raspbian Buster has a libseccomp2 bug. Update the host.
Rootless image fails after upgradingThe 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:

Run in terminalbash
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#

Run in terminalbash
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#

Run in terminalbash
cd ~/docker_volumes/uptime-kuma
docker compose stop
tar -czf backups/kuma-$(date +%F).tar.gz data
docker compose start

Windows (PowerShell)#

Codepowershell
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:

Run in terminalbash
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 -e and add 0 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.

  1. Stop the app: docker compose down.
  2. Rename data to data-old.
  3. Unpack the newest archive into the app folder: tar -xzf backups/kuma-<date>.tar.gz (Windows: Expand-Archive).
  4. Start: docker compose up -d.
  5. Log in. Your monitors and history should be back.
  6. 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-kuma after 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 -slim image 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#

Checked against the official docs on 2026-10-26.