Self-host a status page with Uptime Kuma on your VPS

You run a growing service on your rent VPS, and every time a client reports downtime before you notice it, you lose trust. A public status page, all systems operational, is the fix. But you don't want another $15/month SaaS bill for a monitor that pings five URLs. Uptime Kuma is the open-source status page you run on your own server, and it barely uses resources.
Key takeaways- One Docker Compose file installs Uptime Kuma with persistent data and automatic restarts.
- You get HTTP(s), TCP, ping, DNS, and keyword checks, all from a single lightweight container.
- Monitors can send alerts to Telegram, email, Slack, Discord, Webhook, or your own ntfy instance.
- A dedicated NVMe VPS hosting keeps your status page independent of the services it monitors, preventing a single point of failure.
Prerequisites
- A Linux VPS running Ubuntu 24.04 LTS or Debian 12 (or any distribution with Docker). For a production monitoring box, a 2GB RAM VPS with 2 vCPUs is sufficient even for fifty monitors.
- Docker and Docker Compose v2 installed (
docker compose, not the olddocker-compose). - A domain or subdomain pointing to the VPS IP if you want a public-facing status page (optional; you can also access it by IP:port for private use).
- Root or sudo access.
Why self-host a status page
A self-hosted status page puts the monitor on hardware you control. Uptime Kuma runs as a single container, stores its SQLite database on disk, and serves a dashboard and a public status page from the same process. Unlike SaaS alternatives, it costs nothing per month beyond your existing VPS. You own the alert history, you set the check intervals (as low as 20 seconds), and you add custom domains without paying per seat.
If you already self-host n8n for workflow automation on a VPS, the same Docker-on-VPS mindset applies here. Uptime Kuma runs alongside your other containers.
Step 1, Install Docker and Docker Compose
If Docker is not already on your system, install it from the official repository. The Docker package in Ubuntu's default repo is often outdated, so use their convenience script or add the official repo.
sudo apt update
sudo apt install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc
echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
Verify both are installed and the Docker daemon is running:
sudo docker --version
sudo docker compose version
sudo systemctl is-active docker
Expected output: a version string for Docker and Compose, plus active for the daemon. Then add your user to the docker group so you don't need sudo for every command:
sudo usermod -aG docker $USER
Log out and back in, or run newgrp docker to pick up the group in the current session.
Step 2, Create the Docker Compose file for Uptime Kuma
Create a directory for Uptime Kuma and its data. The container mounts a volume for the SQLite database, so data survives container recreation.
mkdir -p ~/uptime-kuma/data
cd ~/uptime-kuma
Create a compose.yaml (the modern Compose file name):
nano compose.yaml
Paste the following:
services:
uptime-kuma:
image: louislam/uptime-kuma:latest
container_name: uptime-kuma
restart: unless-stopped
ports:
- "3001:3001"
volumes:
- ./data:/app/data
This maps port 3001 on the host to the container. The restart: unless-stopped policy ensures the monitor starts on boot and stays up unless you explicitly stop it.
Save the file (Ctrl+O, Enter, Ctrl+X in nano).
Step 3, Start the Uptime Kuma container
docker compose up -d
Wait a few seconds for the container to pull the image and start. Check the logs:
docker logs uptime-kuma --tail 20
You should see the node process starting and listening on port 3001. Verify the container is running:
docker ps --filter name=uptime-kuma
Expected output: STATUS Up and PORTS 0.0.0.0:3001->3001/tcp.
Step 4, Configure the status page via the web UI
Open your browser at http://YOUR_VPS_IP:3001. You are greeted by the setup wizard.
- Create an admin account, set a username and a strong password.
- Add your first monitor, click "Add New Monitor". Choose the monitor type:
HTTP(s)for a web service,Pingfor an IP or domain,TCP Portfor a specific service port. - Set check interval, default 60 seconds. You can go down to 20 seconds for critical services, but be aware each check uses a small amount of CPU and network.
- Enable the public status page, go to Settings → General → toggle "Enable status page". You can customize the title, description, icon, and theme (light/dark).
For each monitor you add, the dashboard shows a green checkmark when the service responds within the configured timeout, and a red cross when it fails. The public status page aggregates all monitors into a single clean view you can share with clients.
Step 5, Set up notifications (Telegram, email, Slack)
Open a monitor's settings and scroll to "Notification". Click "Setup Notification" and choose your provider. For Telegram, you need a bot token and a chat ID. For email, Uptime Kuma can send SMTP mail, but you need an SMTP relay. If you rent a SMTP VPS with clean IP for email delivery, configure it here.
Test the notification immediately: the UI has a "Test" button that sends a mock alert. Only move on when you receive the test message.
Step 6, Expose the status page via a reverse proxy (optional but recommended)
Accessing port 3001 works, but you get no HTTPS, no nice domain, and the port is obvious to scanners. Use Caddy or Nginx to reverse-proxy port 3001 to a subdomain with automatic TLS. Caddy is simpler:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update && sudo apt install -y caddy
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
Create /etc/caddy/Caddyfile:
status.yourdomain.com {
reverse_proxy localhost:3001
}
Then start and enable Caddy:
sudo systemctl enable --now caddy
Caddy automatically obtains a Let's Encrypt certificate and serves your status page over HTTPS. Now your status page lives at https://status.yourdomain.com.
You can also set this up with Nginx if you have existing sites. See our guide on reverse proxy with automatic HTTPS using Caddy for more detail.
Troubleshooting
Container exits immediately on start
Check the logs with docker logs uptime-kuma. The most common cause is a port conflict, another process is already listening on port 3001. Change the host port in compose.yaml (e.g. "3002:3001") and restart.
Status page loads but shows no monitors
You may have created monitors while not logged in, or the database permissions are wrong. Ensure the ./data directory is writable by the container user (UID 1000 inside the container). sudo chown -R 1000:1000 ~/uptime-kuma/data and restart.
The public status page is unreachable
Check the firewall. sudo ufw status, if you are proxying, only ports 80 and 443 need to be open. If you are using the raw IP:port method, allow port 3001: sudo ufw allow 3001/tcp.
FAQ
What can Uptime Kuma check?
HTTP(s), ping, TCP/UDP port, DNS lookup, ICMP ping, Steam game server, keyword presence in a page, push monitors, and Docker container status. You can set different check intervals per monitor, from 20 seconds to 24 hours.
How many monitors can I run on a cheap 2GB RAM VPS?
Around fifty monitors at 60-second intervals, including several with HTTP keyword checks. If you push every monitor to 20-second intervals, CPU usage climbs. A 2GB RAM VPS is a good baseline for a moderate monitoring load.
Does Uptime Kuma support multiple users?
Yes. After login, go to Settings → Users to add additional read-only users. Only the admin can create and edit monitors. Read-only users can view the dashboard and the status page.
Can I monitor services that are on the same VPS?
Yes, and you should. Use localhost or the internal Docker hostname (if using Docker networking). But keep in mind: if your VPS goes down, Uptime Kuma goes down with it and cannot report the outage. For critical infrastructure, consider monitoring from a separate VPS with full root access or a lightweight external heartbeat.
How do I back up Uptime Kuma's data?
Stop the container, copy the ./data/ directory, then restart. The SQLite database stores all monitors, settings, and status history. A simple cron job that takes a daily snapshot and syncs it to remote storage is enough.
Can I change the default port 3001?
Yes. Edit compose.yaml and change the left side of the port mapping. For example, "9000:3001" maps host port 9000 to container port 3001. Update your reverse proxy or firewall accordingly.
Related articles
- Reverse proxy with automatic HTTPS using Caddy
- Self-hosting n8n for workflow automation on a VPS
- Set up a WireGuard VPN on your VPS in 10 minutes


