> ## Documentation Index
> Fetch the complete documentation index at: https://www.towbar.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Uptime Kuma

> Uptime Kuma monitors websites and services and displays their availability on status pages.

export const UseCaseNavigation = () => {
  useLayoutEffect(() => {
    const storageKey = "towbar-use-case-open-groups";
    const groupSelector = "#sidebar li[data-title] > button[aria-expanded]";
    let restoring = false;
    let frame = 0;
    function storedGroups() {
      try {
        return new Set(JSON.parse(sessionStorage.getItem(storageKey) || "[]"));
      } catch {
        return new Set();
      }
    }
    function save(groups) {
      try {
        sessionStorage.setItem(storageKey, JSON.stringify([...groups]));
      } catch {}
    }
    function groupName(button) {
      return button.parentElement?.dataset.title;
    }
    function rememberOpenGroups() {
      const groups = new Set();
      for (const button of document.querySelectorAll(groupSelector)) {
        if (button.getAttribute("aria-expanded") === "true") {
          const name = groupName(button);
          if (name) groups.add(name);
        }
      }
      save(groups);
    }
    function restoreOpenGroups() {
      frame = 0;
      const groups = storedGroups();
      if (!groups.size) return;
      restoring = true;
      for (const button of document.querySelectorAll(groupSelector)) {
        if (groups.has(groupName(button)) && button.getAttribute("aria-expanded") === "false") {
          button.click();
        }
      }
      restoring = false;
    }
    function scheduleRestore() {
      if (!frame) frame = requestAnimationFrame(restoreOpenGroups);
    }
    function onClick(event) {
      if (restoring || !(event.target instanceof Element)) return;
      const button = event.target.closest(groupSelector);
      if (button) {
        const name = groupName(button);
        if (!name) return;
        const groups = storedGroups();
        if (button.getAttribute("aria-expanded") === "true") groups.delete(name); else groups.add(name);
        save(groups);
        return;
      }
      if (event.target.closest('a[href^="/docs/use-cases/"]')) {
        rememberOpenGroups();
      }
    }
    const observer = new MutationObserver(scheduleRestore);
    document.addEventListener("click", onClick, true);
    observer.observe(document.getElementById("sidebar") || document.body, {
      childList: true,
      subtree: true
    });
    scheduleRestore();
    return () => {
      document.removeEventListener("click", onClick, true);
      observer.disconnect();
      cancelAnimationFrame(frame);
    };
  }, []);
  return null;
};

<UseCaseNavigation />

<img className="towbar-doc-brand-logo" src="https://mintcdn.com/avgeek/Wq-9tmR7jDSzqtpH/assets/use-case-logos/uptimekuma.svg?fit=max&auto=format&n=Wq-9tmR7jDSzqtpH&q=85&s=141c4052d354a68c9d591d95ee3ea7c9" alt="Uptime Kuma logo" aria-hidden="true" width="24" height="24" data-path="assets/use-case-logos/uptimekuma.svg" />

**Upstream source:** [Current installation guide](https://github.com/louislam/uptime-kuma/wiki/%F0%9F%94%A7-How-to-Install).

Uptime Kuma stores users, monitors, and status pages in its SQLite database under `/app/data`. It does not need a separate Datastore for a small instance; keep the Service volume on local storage that supports file locking.

## Towbar manifest

```yaml title=".towbar/services/uptime-kuma.service.yml" theme={"system"}
id: uptime-kuma
name: Uptime Kuma
buildServer: null
deployment:
  type: image
  image: louislam/uptime-kuma:2
container:
  port: 3001
  volumes:
    - name: data
      mountPath: /app/data
      initialData: image
environments:
  production:
    server: 192.0.2.10
rollout:
  type: recreate
  maintenanceMode: true
  reason: Persistent application data uses a single writer
domains:
  primary: uptime-kuma.example.com
tls:
  mode: direct
```

## Configure

1. Prepare the example server, connect the repository, and map `production` to the branch containing these manifests. Replace the example server IP and domain. Commit the manifests, then sync the repository and inspect the resolved configuration.
2. Keep the Service's named volumes attached across deployments. Towbar's Datastore backup policy does not back up Service volumes, so include them in your own recovery plan.
3. Deploy the Service, then perform the checks below before enabling auto-deploy.

## Verify

Create a monitor and a status page, redeploy, and verify both and their history remain. Keep a backup of the `/app/data` volume.

For field constraints, see [Service manifest](/docs/services/manifest).
