# How the Uptime Kuma Database Layer Manages SQLite and Optional MySQL/MariaDB

> Discover how Uptime Kuma's database layer expertly handles SQLite and optional MySQL/MariaDB using Knex and RedBean-Node for robust data management.

- Repository: [Louis Lam/uptime-kuma](https://github.com/louislam/uptime-kuma)
- Tags: internals
- Published: 2026-02-28

---

**Uptime Kuma stores all persistent data through a thin abstraction built on Knex and RedBean‑Node that defaults to SQLite with WAL mode while supporting external or embedded MariaDB via a JSON configuration file.**

The database implementation lives primarily in [`server/database.js`](https://github.com/louislam/uptime-kuma/blob/main/server/database.js) and follows a "default‑to‑SQLite, optional‑MySQL/MariaDB" strategy. This architecture allows single‑node deployments to run without external dependencies while giving production environments a path to scalable, network‑based storage.

## Data Directory Bootstrap

When the server starts, `Database.initDataDir()` creates a configurable data folder (default `./data/`). This directory houses the SQLite file, upload assets, screenshots, Docker TLS certificates, and the critical [`db-config.json`](https://github.com/louislam/uptime-kuma/blob/main/db-config.json) that tells the engine which database type to use.

The initialization logic ensures the folder structure exists before any connection attempts occur (lines 57‑62 in [`server/database.js`](https://github.com/louislam/uptime-kuma/blob/main/server/database.js)).

## Reading the Database Configuration

`Database.readDBConfig()` reads [`db-config.json`](https://github.com/louislam/uptime-kuma/blob/main/db-config.json) from the data directory. If the file is missing or malformed, the code transparently falls back to SQLite. The JSON schema supports keys for `type`, `hostname`, `port`, `username`, `password`, `dbName`, and SSL settings (lines 66‑71).

This configuration‑first approach means you can switch database engines without modifying application code—only the JSON file needs to change.

## Connection Logic: Three Database Modes

`Database.connect()` builds a **Knex** configuration object based on the `type` field from [`db-config.json`](https://github.com/louislam/uptime-kuma/blob/main/db-config.json). The implementation branches into three distinct paths.

### SQLite (Default)

For `type: "sqlite"`, the layer uses the internal `@louislam/sqlite3` dialect. The connection object requires only a filename. The pool is tuned for **WAL mode** (Write‑Ahead Logging) with up to 20 concurrent connections, and an `afterCreate` hook runs `Database.initSQLite()` for every raw SQLite connection (lines 155‑197).

This setup guarantees WAL journaling, foreign‑key enforcement, and PRAGMA tuning optimized for concurrent reads.

### External MariaDB/MySQL

When `type` is `"mariadb"`, the code first opens a direct MySQL connection using `mysql.createConnection()` to execute `CREATE DATABASE IF NOT EXISTS` if the target database is missing (lines 188‑199). It then constructs a Knex instance using the `mysql2` client with user‑supplied host, port, credentials, and optional SSL.

Pool limits derive from the `UPTIME_KUMA_DB_POOL_MAX_CONNECTIONS` environment variable (capped at 100). The configuration forces `timezone: "Z"` and disables automatic DATETIME type‑casting to preserve UTC strings (lines 185‑215).

### Embedded MariaDB

The `"embedded‑mariadb"` type starts an in‑process MariaDB server via the `EmbeddedMariaDB` class (defined in [`server/embedded-mariadb.js`](https://github.com/louislam/uptime-kuma/blob/main/server/embedded-mariadb.js)) and points Knex at its Unix socket. This path reuses the same pool settings as external MariaDB but requires zero external database configuration (lines 239‑247).

## SQLite‑Specific Initialization

`Database.initSQLite(rawConn, testMode)` executes a series of PRAGMA statements on every new connection. In production, it enables **WAL** mode; in test mode, it switches to **MEMORY** journaling for speed (lines 12‑18). Additional tuning includes enabling foreign keys, setting cache size to 2000 pages, enabling auto‑vacuum, and setting synchronous mode to `NORMAL` (lines 20‑27).

These settings ensure durability without sacrificing performance for a monitoring workload that generates frequent writes.

## MariaDB‑Specific Bootstrap

For MariaDB connections, the `afterCreate` pool hook ensures the charset is set to **utf8mb4** to support emoji and full Unicode in monitor names and notification text (lines 264‑270). The bootstrap logic also verifies that the database user has sufficient privileges to create the schema if it does not exist, preventing startup failures on fresh installs.

## Migration and Patching

Schema changes are handled through **Knex migrations** stored in `db/knex_migrations/`. `Database.patch()` runs `R.knex.migrate.latest()` against the configured database, temporarily disabling foreign‑key checks for SQLite to avoid constraint violations during column alterations (lines 58‑71).

Migration files are applied uniformly to both SQLite and MariaDB, ensuring consistent schema versions regardless of the underlying engine.

## Graceful Shutdown

`Database.close()` implements clean teardown for both database types. For SQLite, it flushes the WAL to the main database using `PRAGMA wal_checkpoint(TRUNCATE)` before closing connections. It then loops until all RedBean‑Node promises resolve, preventing data loss during container shutdowns or process restarts (lines 36‑53).

## Practical Implementation Examples

### Bootstrapping SQLite (Default Behavior)

```javascript
const Database = require("./server/database");

// Initialize the data folder structure
Database.initDataDir({ "data-dir": "./data/" });

// Connect with autoload models enabled
await Database.connect(false, true, false);

// Query using RedBean-Node
const monitors = await Database.R.getAll("SELECT * FROM monitor");

```

### Switching to External MariaDB

Create [`./data/db-config.json`](https://github.com/louislam/uptime-kuma/blob/main/./data/db-config.json):

```json
{
  "type": "mariadb",
  "hostname": "db.example.com",
  "port": "3306",
  "username": "kuma_user",
  "password": "secret",
  "dbName": "kuma",
  "ssl": false
}

```

Then connect:

```javascript
await Database.connect();
// Automatically creates database if missing and applies migrations

```

### Running Migrations Manually

```javascript
// After version upgrades or schema changes
await Database.patch();

```

## Summary

- **Uptime Kuma** centralized all database logic in [`server/database.js`](https://github.com/louislam/uptime-kuma/blob/main/server/database.js), using **Knex** for query building and **RedBean‑Node** for ORM operations.
- The system defaults to **SQLite** with WAL mode enabled, storing data in `./data/kuma.db` with optimized PRAGMA settings for monitoring workloads.
- Switching to **MySQL/MariaDB** requires only a [`db-config.json`](https://github.com/louislam/uptime-kuma/blob/main/db-config.json) file; the code automatically creates the database, sets `utf8mb4` charset, and manages connection pooling via environment variables.
- **Embedded MariaDB** provides a zero‑config option for development environments that need MySQL compatibility without external services.
- Schema migrations live in `db/knex_migrations/` and apply uniformly across all supported engines via `Database.patch()`.
- Shutdown logic ensures SQLite WAL checkpoints complete before process exit, preventing database corruption.

## Frequently Asked Questions

### What is the default database used by Uptime Kuma?

**SQLite** is the default database engine. On first startup, Uptime Kuma creates `./data/kuma.db` using the `@louislam/sqlite3` dialect with WAL mode enabled. This requires no configuration and works immediately for single‑instance deployments.

### How do I migrate from SQLite to MySQL or MariaDB?

Create a [`db-config.json`](https://github.com/louislam/uptime-kuma/blob/main/db-config.json) file in your data directory specifying `"type": "mariadb"` along with host, port, and credentials. Restart the application; it will automatically create the target database and run migrations. You must manually migrate existing data using export/import tools, as Uptime Kuma does not provide an automatic data migration utility between engines.

### What is embedded MariaDB in Uptime Kuma?

**Embedded MariaDB** is an in‑process database server shipped with Uptime Kuma (defined in [`server/embedded-mariadb.js`](https://github.com/louislam/uptime-kuma/blob/main/server/embedded-mariadb.js)). When configured with `"type": "embedded-mariadb"`, the application starts a local MariaDB instance bound to a Unix socket, giving you MySQL compatibility without requiring a separate database container or installation.

### How does Uptime Kuma handle database schema updates?

Uptime Kuma uses **Knex migrations** stored in `db/knex_migrations/`. During startup, `Database.patch()` executes `knex.migrate.latest()` to apply any pending schema changes. This works identically for SQLite and MariaDB, ensuring the application always runs the correct schema version regardless of the underlying database type.