# What Database Does OpenWork Use? SQLite + Drizzle Configuration Guide

> Discover OpenWork's database choice: SQLite with Drizzle ORM. Learn how to configure your database for optimal performance in this comprehensive guide.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: how-to-guide
- Published: 2026-08-01

---

**OpenWork uses SQLite for runtime state storage and Drizzle ORM for all database access, with MySQL available in the enterprise edition.**

OpenWork is an open-source workspace management platform built by Different AI. Understanding its database architecture is essential for contributors, self-hosters, and developers extending the platform. This guide breaks down exactly how OpenWork configures SQLite with Drizzle ORM, where the critical files live, and how the enterprise MySQL layer differs.

## Runtime Database: SQLite with Bun and Node Support

OpenWork's **runtime state**—workspace metadata, key-value stores, and session data—persists to a local SQLite file called `runtime.sqlite`. The core implementation lives in [[`dev/apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/runtime-db.ts)](https://github.com/different-ai/openwork/blob/dev/dev/apps/server/src/runtime-db.ts).

This module handles three responsibilities:

- **Path resolution**: Determines where `runtime.sqlite` lives (OpenWork config directory or `~/.config/openwork/runtime.sqlite`)
- **Driver selection**: Dynamically imports `bun:sqlite` or `node:sqlite` based on runtime environment
- **Drizzle wrapping**: Returns both raw SQLite handle and type-safe Drizzle ORM instance

### Conditional Driver Loading

The runtime database detects the execution environment and loads the appropriate native driver:

```typescript
// Bun runtime
const { Database } = await import("bun:sqlite");
const { drizzle } = await import("drizzle-orm/bun-sqlite");

// Node runtime (fallback)
const { DatabaseSync } = await import("node:sqlite");
const { drizzle } = await import("drizzle-orm");

```

Both paths converge on the same Drizzle API surface, ensuring portable code across runtimes.

### Database Initialization Pattern

From [`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts), the initialization flow follows this structure:

```typescript
const sqlite = new Database(path, { create: true });
const db = drizzle(sqlite);

```

The returned object exposes:

| Property | Purpose |
|----------|---------|
| `sqlite` | Raw driver handle for prepared statements |
| `db` | Drizzle ORM instance for type-safe queries |
| `close()` | Cleanup helper for graceful shutdown |

## SQLite Schema Definition with Drizzle ORM

OpenWork defines tables using `sqliteTable` from `drizzle-orm/sqlite-core`. An example from [[`dev/apps/server/src/workspace-kv-store.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/workspace-kv-store.ts)](https://github.com/different-ai/openwork/blob/dev/dev/apps/server/src/workspace-kv-store.ts):

```typescript
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";

export const kvFactory = (config) => {
  const table = sqliteTable(config.tableName, {
    workspace_id: text("workspace_id").primaryKey(),
    key: text("key"),
    value: text("value"),
  });
  // Prepared statements for performance
  const get = sqlite.prepare(config.selectSql);
  const upsert = sqlite.prepare(config.upsertSql);
  return { table, get, upsert };
};

```

This pattern combines **Drizzle's type-safe schema definitions** with **raw prepared statements** for hot-path operations.

### Practical Query Example

```typescript
import { runtimeDbPath } from "./runtime-db";
import { kvTable } from "./schema";

async function getWorkspaceConfig(workspaceId: string) {
  const { db } = await openRuntimeDb("/my/config");
  
  const result = db
    .select()
    .from(kvTable)
    .where(kvTable.workspace_id.eq(workspaceId))
    .get();
    
  return result?.value;
}

```

## Enterprise Edition: MySQL and PlanetScale via Drizzle

The paid **Den-DB** enterprise package upgrades to **MySQL** (or PlanetScale's serverless MySQL) while retaining Drizzle ORM. Entry point: [[`dev/ee/packages/den-db/src/client.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/packages/den-db/src/client.ts)](https://github.com/different-ai/openwork/blob/dev/dev/ee/packages/den-db/src/client.ts).

### MySQL Connection Setup

```typescript
import { drizzle } from "drizzle-orm/mysql2";
import { drizzle as drizzlePlanetScale } from "drizzle-orm/planetscale-serverless";

// Standard MySQL
const db = drizzle(mysqlConnection, { schema });

// PlanetScale serverless
const db = drizzlePlanetScale({ url: process.env.DATABASE_URL }, { schema });

```

### Schema and Migrations

Enterprise schemas live in [`dev/ee/packages/den-db/src/schema/`](https://github.com/different-ai/openwork/tree/dev/dev/ee/packages/den-db/src/schema) with files like:

- [`workers.ts`](https://github.com/different-ai/openwork/blob/main/workers.ts) – Background job definitions
- [`telemetry.ts`](https://github.com/different-ai/openwork/blob/main/telemetry.ts) – Usage analytics
- [`org.ts`](https://github.com/different-ai/openwork/blob/main/org.ts) – Organization multi-tenancy

Migration management uses **Drizzle Kit**, configured in [[`drizzle.config.ts`](https://github.com/different-ai/openwork/blob/main/drizzle.config.ts)](https://github.com/different-ai/openwork/blob/dev/dev/ee/packages/den-db/drizzle.config.ts):

```typescript
import { defineConfig } from "drizzle-kit";

export default defineConfig({
  schema: "./src/schema/*",
  out: "./drizzle",
  dialect: "mysql",
});

```

## Complete Configuration Walkthrough

Follow these steps to configure OpenWork's database in your environment:

1. **Locate or create the config directory**

   ```bash
   # Default path

   mkdir -p ~/.config/openwork
   
   # Or specify custom root

   export OPENWORK_ROOT=/var/lib/openwork
   ```

2. **Initialize the runtime database**

   ```typescript
   import { openRuntimeDb } from "@openwork/server/runtime-db";
   
   const { db, sqlite, close } = await openRuntimeDb(process.env.OPENWORK_ROOT);
   ```

3. **Execute schema migrations** (SQLite tables auto-create on first use)

4. **For enterprise MySQL**: Set `DATABASE_URL` and import from `@openwork-ee/den-db`

## File Reference: Key Database Modules

| File | Repository Path | Purpose |
|------|-----------------|---------|
| [`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts) | [`dev/apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/runtime-db.ts) | SQLite connection factory with runtime detection |
| [`runtime-db.test.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.test.ts) | [`dev/apps/server/src/runtime-db.test.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/runtime-db.test.ts) | Unit tests for path resolution and connectivity |
| [`workspace-kv-store.ts`](https://github.com/different-ai/openwork/blob/main/workspace-kv-store.ts) | [`dev/apps/server/src/workspace-kv-store.ts`](https://github.com/different-ai/openwork/blob/main/dev/apps/server/src/workspace-kv-store.ts) | SQLite table definitions and KV operations |
| [`client.ts`](https://github.com/different-ai/openwork/blob/main/client.ts) | [`dev/ee/packages/den-db/src/client.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/packages/den-db/src/client.ts) | MySQL/PlanetScale Drizzle connector |
| [`drizzle.config.ts`](https://github.com/different-ai/openwork/blob/main/drizzle.config.ts) | [`dev/ee/packages/den-db/drizzle.config.ts`](https://github.com/different-ai/openwork/blob/main/dev/ee/packages/den-db/drizzle.config.ts) | Drizzle Kit configuration for migrations |
| `schema/` | `dev/ee/packages/den-db/src/schema/` | Enterprise MySQL table definitions |

## Summary

- **SQLite** powers OpenWork's open-source runtime via `runtime.sqlite` files
- **Drizzle ORM** provides the abstraction layer for both SQLite and MySQL
- **Bun and Node** are both supported through conditional driver imports in [`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts)
- **MySQL/PlanetScale** replaces SQLite in the enterprise Den-DB package
- **Type-safe schemas** are defined with `sqliteTable` (SQLite) or standard Drizzle columns (MySQL)

## Frequently Asked Questions

### Which SQLite driver does OpenWork prefer?

OpenWork prefers **Bun's native `bun:sqlite`** when running on Bun, falling back to **Node's `node:sqlite`** (available in Node 22.5.0+) for standard Node environments. The [`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts) module detects the runtime and imports the appropriate driver dynamically.

### Can I use PostgreSQL instead of SQLite or MySQL?

Not without modifications. OpenWork's open-source codebase hardcodes SQLite assumptions in [`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts), and the enterprise edition hardcodes MySQL in `den-db`. PostgreSQL support would require implementing a new driver adapter following the same Drizzle ORM patterns.

### Where does the SQLite file actually get created?

By default, `runtime.sqlite` is created in `~/.config/openwork/`. If you specify a config directory via the `root` parameter to `openRuntimeDb()`, it places the file at `{root}/runtime.sqlite`. The path resolution logic handles cross-platform differences (macOS, Linux, Windows).