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 field — such as a monitor name — 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 only a partial fix, scoped to authenticated contexts: an attacker with admin access — or able to force it through bruteforce — retains the original attack surface. The 2.x branch ships LiquidJS 10.26.0 with a complete remediation. No further patches are planned for the 1.x branch: version 2.5.0, released August 1, 2026, marks the active trajectory of the project. 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 controls a monitor name or template field 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. Uptime Kuma v2 runs in the same footprint as v1: 1 vCPU and 512 MB of RAM are sufficient for under 50 monitors. Plan for 2 vCPU and 1 GB for a fleet of 200 monitors or more. SQLite migration can take several minutes on slow storage — 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 a response starting with Docker Compose version v2. 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, the default name is 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
Stop Uptime Kuma
Stop the container without removing the volume:
docker compose downWait for the confirmation Container uptime-kuma Stopped before continuing.
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.
Back up a bind mount
If your data is in a host folder (example: ./data):
tar czf uptime-kuma-v1-backup.tar.gz ./dataKeep this archive off the server (object storage, remote server) before proceeding to the migration.
v1 → v2 migration: step-by-step procedure
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:2If you use the latest tag, prefer an explicit version tag like louislam/uptime-kuma:2.5.0 to avoid regressions during future automatic upgrades.
Pull the new image
Download the v2 image before starting the service:
docker pull louislam/uptime-kuma:2Verify the image is present: docker images | grep uptime-kuma.
Start the v2 container
Launch the service. Docker Compose detects the image change and recreates the container:
docker compose up -dWait for the confirmation Container uptime-kuma Started.
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 tens of minutes depending on database size:
docker compose logs -f uptime-kumaDo not interrupt the container during this phase. Wait for a line confirming the end of the migration before continuing.
Check the healthcheck
Verify the container is in healthy state:
docker compose psThe 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.
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). v2 applies strict Liquid template validation: a field containing non-conforming tags will be rejected. Use this opportunity to audit monitor names and remove any unexpected template content — this is the exact 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. Close port 3001 at the firewall: ufw deny 3001.
Fail2ban or CrowdSec in front of the proxy. Add a bruteforce detection rule on interface login attempts. CrowdSec has an uptime-kuma scenario in its Hub. 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. The error address already in use :::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 3002:3001) and update your reverse proxy configuration.
Notification template rejected after migration. If a notification channel shows a template error, LiquidJS 10.26.0 strict validation rejected a template that was tolerated in v1. 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 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, Docker will restart the container indefinitely. Switch to restart: no, 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 image by default, actively maintained dependencies, and a 14-day delay mechanism before integrating new npm dependencies — to limit supply chain attacks, as documented in the article Securing the npm supply chain in CI.
Managing and securing multiple clients' infrastructure from a single centralized space, without bearing the risks of end-of-maintenance alone, is what the ServOrbit agency space enables.