Deployment guide

Migrating Uptime Kuma from v1 to v2: security guide

Deploy on a VPS Cloud →

Tutorial

Security & Monitoring

Migrating Uptime Kuma from v1 to v2: security guide

The Uptime Kuma 1.x branch no longer receives security patches. CVE-2026-45618, a remote code execution flaw in the LiquidJS template engine, made this risk concrete: on an unpatched instance, an attacker can run arbitrary commands. This guide walks through the migration to version 2.x, covers data backup, post-migration checks, and hardens the instance before the next exploitation attempt.

10 min read9 steps
Contents· Why migrate now: CVE-2026-45618 and end of maintenance for the 1.x branch1/10
  1. 01Why migrate now: CVE-2026-45618 and end of maintenance for the 1.x branch
  2. 02Concrete impacts of an unmigrated instance
  3. 03Prerequisites before you start
  4. 04Mandatory backup before migration
  5. 05Backing up the data volume
  6. 06v1 → v2 migration: step-by-step procedure
  7. 07Post-migration checks
  8. 08Hardening the instance after migration
  9. 09Troubleshooting: common errors
  10. 10What this migration protects

Why migrate now: CVE-2026-45618 and end of maintenance for the 1.x branch

CVE-2026-45618 is a critical vulnerability scored CVSS 10.0 in LiquidJS, the template engine used by Uptime Kuma for notification templates. LiquidJS versions prior to 10.26.0 are affected: by exploiting the valueOf filter in a template — in Uptime Kuma, the message template of a notification — an attacker can chain prototype manipulation to reach JavaScript's Function constructor and execute arbitrary commands on the host. A public proof-of-concept demonstrates file reads and execution via child_process.execSync.

The 1.x branch received no fix: its last release, 1.23.17 (October 2025), bundles LiquidJS 10.22.0, and the project now marks the 1 and latest Docker tags as deprecated. Editing a notification template requires an account: an attacker who obtains admin access — through stolen credentials or bruteforce — has this surface. The 2.x branch has shipped LiquidJS 10.26.0 since version 2.4.0 (May 2026) and keeps receiving fixes: 2.5.8, for example, fixed an authenticated code execution via Apprise (GHSA-v2p3-6f5h-6hhf) that also affects the whole 1.x branch. The latest release, 2.5.9, came out in October 2026. Running a 1.x instance means exposing every monitor — and the clients it watches — to an attack surface with no known closure date.

Concrete impacts of an unmigrated instance

  • Remote code execution: an attacker who can edit a notification template can run system commands on the host VPS.
  • Client portfolio exposure: an agency hosting a shared instance exposes the configurations, internal URLs, and notification credentials of all its clients.
  • Transferred liability: in the event of an incident on an unmaintained instance, failure to migrate after a critical CVE is published constitutes an aggravating factor.
  • CVE accumulation: the 1.x branch receives neither security patches nor dependency updates — every new flaw in LiquidJS or its dependencies accumulates without remedy.
  • Silent detection: LiquidJS evaluates templates server-side; exploitation may remain invisible in Uptime Kuma application logs.

Prerequisites before you start

Check the following points before starting the migration. Missing one can make rollback impossible.

Minimum recommended resources. The Uptime Kuma documentation sets no figure: a small instance fits on an entry-level VPS, but RAM and disk grow with the number of monitors and the history kept. The migration aggregates the heartbeat table: the maintainer reports about 7 minutes for 20 monitors and 90 days of data, and hours on slow hardware — a VPS with NVMe SSD reduces this window.

Docker Compose v2. The required command is docker compose (integrated plugin), not docker-compose (standalone v1 binary). Check with docker compose version: expect version 2 or later (the current branch is v5). If the command fails, install the plugin via your distribution's package manager before continuing.

Root or sudo access. The procedure manipulates Docker volumes and system files; privileged access is mandatory.

Operational v1 instance. Confirm the current version via the web interface (Settings → About). The migration assumes the instance starts and that its database is consistent — if it is already corrupted, restore a backup first.

Mandatory backup before migration

The v1 → v2 migration transforms the SQLite schema irreversibly. Without a valid backup, a full rollback is impossible.

Identify the volume or data folder. If you use Docker Compose with a named volume, its full name combines the Compose project name and the volume name, for example uptime-kuma_uptime-kuma-data. Confirm with docker volume ls | grep kuma. If you use a bind mount, locate the path in your docker-compose.yml — typically ./data:/app/data.

Stop the container before the backup. A SQLite database backed up during active writes may be corrupted. Never use the -v flag when stopping: it would delete the volume and its contents.

Backing up the data volume

  1. Stop Uptime Kuma

    Stop the container without removing the volume:

    docker compose down

    Wait for the confirmation Container uptime-kuma Stopped before continuing.

  2. Back up a named Docker volume

    Use a busybox container to create a compressed archive of the volume:

    docker run --rm \
      --volume uptime-kuma_uptime-kuma-data:/app/data \
      --volume $(pwd):/backup \
      busybox \
      tar czf /backup/uptime-kuma-v1-backup.tar.gz -C /app/data .

    Check the archive size with ls -lh uptime-kuma-v1-backup.tar.gz. An archive of only a few bytes indicates a mounting problem — fix it before continuing.

  3. Back up a bind mount

    If your data is in a host folder (example: ./data):

    tar czf uptime-kuma-v1-backup.tar.gz ./data

    Keep this archive off the server (object storage, remote server) before proceeding to the migration.

v1 → v2 migration: step-by-step procedure

  1. Update the image in docker-compose.yml

    Open your docker-compose.yml and replace the image tag:

    # Before
    image: louislam/uptime-kuma:1
    # After
    image: louislam/uptime-kuma:2

    If you use the latest tag, prefer an explicit version tag like louislam/uptime-kuma:2.5.9 (latest release, October 2026) to avoid regressions during future automatic upgrades, and follow the repository's security advisories to move it forward. Avoid rootless tags for this migration: the official guide advises against them when coming from v1.

  2. Pull the new image

    Download the v2 image before starting the service:

    docker pull louislam/uptime-kuma:2

    Verify the image is present: docker images | grep uptime-kuma.

  3. Start the v2 container

    Launch the service. Docker Compose detects the image change and recreates the container:

    docker compose up -d

    Wait for the confirmation Container uptime-kuma Started.

  4. Follow the database migration

    On first start, Uptime Kuma migrates the SQLite schema to the v2 format. This can take from a few seconds to several hours depending on database size and hardware speed:

    docker compose logs -f uptime-kuma

    Do not interrupt the container during this phase. Wait for a line confirming the end of the migration before continuing.

  5. Check the healthcheck

    Verify the container is in healthy state:

    docker compose ps

    The Status column must show healthy. If it stays on starting for more than two minutes after migration logs end, check the logs to identify the error.

  6. Confirm the active version in the interface

    Log in to the web interface. Go to Settings → About and verify the displayed version starts with 2.. Confirm all your monitors are present and their statuses match the actual state of your services.

Post-migration checks

A successful migration is not just a container that starts. Go through these points before considering the operation complete.

Monitors. Open the dashboard and compare the monitor count with your v1 inventory. A missing monitor may indicate a partial migration issue — check logs at the ERROR or WARN level.

Notifications. Manually trigger a notification test for each configured channel (email, Telegram, webhook). In v2, email (SMTP) templates move from a regular-expression replacement to LiquidJS: variables become case-sensitive and an unknown variable is silently ignored. Use this opportunity to audit each notification template and remove any unexpected content ({% %} tags, valueOf filter) — this is the vector of CVE-2026-45618.

Status page. If you expose a public status page, verify it responds correctly and lists the right services. The URL and slug are preserved after migration.

SSL certificates. TLS probes restart their verification cycle after migration. A certificate close to expiry before the migration may trigger an alert immediately: verify the actual date before treating this as a regression.

Hardening the instance after migration

Mandatory authentication. In v2, authentication is enabled by default. Check in Settings → Security that passwordless access is disabled. On a freshly deployed instance with no account configured, the interface is publicly accessible during initial setup: create the admin account within the first minutes after the first start.

Dedicated subdomain behind a reverse proxy. Do not expose Uptime Kuma on a direct port (:3001). Place it behind Nginx or Caddy on a dedicated subdomain (status.your-domain.com) with TLS. A ufw deny 3001 is not enough: ports published by Docker bypass ufw rules. Publish the port on the loopback only in docker-compose.yml ("127.0.0.1:3001:3001") so that only the reverse proxy can reach it.

Fail2ban or CrowdSec in front of the proxy. Add a bruteforce detection rule on interface login attempts. The CrowdSec Hub offers a community collection, timokoessler/uptime-kuma, with a bruteforce scenario. See the article Securing your VPS with CrowdSec for the full procedure.

Network isolation. Place Uptime Kuma in a dedicated Docker network. It only needs to reach the targets it monitors — not your production databases or internal APIs.

Troubleshooting: common errors

Incompatible volume or corrupted database. If logs show SQLITE_ERROR: no such table or database disk image is malformed, the mounted database is not a valid Uptime Kuma v1 database, or the backup was taken while the database was being written. Restore the archive, stop the container cleanly, and restart the procedure from the backup step.

Port 3001 already in use. A port is already allocated or address already in use error on port 3001 means a process already uses this port. Identify it with ss -tlnp | grep 3001 and stop it, or change the port mapping in docker-compose.yml (for example 127.0.0.1:3002:3001) and update your reverse proxy configuration.

Notification template rejected after migration. If a notification channel shows a template error, the template contains syntax that LiquidJS refuses; in v1, SMTP email did not use LiquidJS but a simple regular-expression replacement. Edit the template in Settings → Notifications and remove or fix the offending tags.

TLS certificate shown as expired. TLS probes restart their cycle after migration. Verify the actual date with echo | openssl s_client -connect your-domain.com:443 -servername your-domain.com 2>/dev/null | openssl x509 -noout -dates before treating this alert as an incident.

Container in a restart loop. If migration fails and the policy is restart: always or unless-stopped, Docker will restart the container indefinitely. Switch to restart: "no" (quoted in YAML), fix the issue identified in the logs, then restore the original policy.

What this migration protects

Migrating to Uptime Kuma 2.x closes CVE-2026-45618 on a surface directly accessible from the administration interface. For an agency managing multiple clients' infrastructure from a shared instance, this surface is proportional to the number of accounts and monitors configured: each additional client widened the exposed perimeter.

v2 also brings structural changes that reduce the long-term attack surface: rootless Docker images offered as an option (2-rootless tags), actively maintained dependencies, and a 14-day delay before integrating new npm dependency versions (min-release-age=14 in the project's .npmrc) — to limit supply chain attacks.

With a ServOrbit.com Agency plan, you create and track your clients' cPanel accounts from one space.

Your clients' cPanel accounts in one place

With a ServOrbit.com Agency plan, you create and track your clients' cPanel accounts from one space. You manage the client relationship, we run the infrastructure.