# How to Connect Logto to a Custom Database: Complete Implementation Guide

> Learn how to connect Logto to a custom database. Easily set DB_URL for PostgreSQL or implement a custom OIDC adapter for MySQL, MongoDB, and more. Get the complete implementation guide today.

- Repository: [Logto/logto](https://github.com/logto-io/logto)
- Tags: how-to-guide
- Published: 2026-06-30

---

**Connect Logto to a custom database by either setting the `DB_URL` environment variable to a PostgreSQL DSN for standard setups, or implementing a custom OIDC storage adapter in [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts) to support alternative databases like MySQL or MongoDB.**

Logto, the open-source identity platform maintained in the **logto-io/logto** repository, relies on a relational database for all persistence accessed through the `DB_URL` environment variable defined in [`packages/shared/src/node/env/GlobalValues.ts`](https://github.com/logto-io/logto/blob/main/packages/shared/src/node/env/GlobalValues.ts). While the platform defaults to PostgreSQL using the built-in adapter in [`packages/core/src/oidc/adapter.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/adapter.ts), the architecture supports custom database integrations through the `oidc-provider` library's adapter pattern. This guide demonstrates both methods to connect Logto to a custom database, from simple DSN configuration to full custom adapter implementations.

## Understanding Logto's Database Architecture

Logto stores authentication data, user sessions, and application configurations in a relational database accessed via the `DB_URL` environment variable. The connection is initialized in [`packages/shared/src/node/env/GlobalValues.ts`](https://github.com/logto-io/logto/blob/main/packages/shared/src/node/env/GlobalValues.ts), where the `databaseUrl` value determines the target database endpoint.

By default, Logto uses the PostgreSQL adapter located at [`packages/core/src/oidc/adapter.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/adapter.ts). This adapter implements the storage interface required by the underlying `oidc-provider` library, handling OIDC model persistence through standard SQL operations.

## Method 1: Connect to a Custom PostgreSQL Instance

The simplest way to connect Logto to a custom database is providing a compatible PostgreSQL connection string. Since the default adapter uses standard PostgreSQL SQL, any PostgreSQL-compatible database (including AWS RDS, Google Cloud SQL, or self-hosted instances) works without code modifications.

### Configuring the DB_URL Environment Variable

Locate the environment configuration in your deployment environment. According to the source code in [`packages/shared/src/node/env/GlobalValues.ts`](https://github.com/logto-io/logto/blob/main/packages/shared/src/node/env/GlobalValues.ts), Logto reads the `DB_URL` variable to establish database connectivity.

Set the variable to your custom PostgreSQL DSN:

```dotenv

# .env

DB_URL=postgres://myuser:mypassword@my-db-host:5432/mylogto

```

This approach requires no changes to [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts) or the adapter logic, as the default implementation handles PostgreSQL connections automatically.

## Method 2: Implement a Custom OIDC Storage Adapter

For databases other than PostgreSQL (such as MySQL, SQLite, or NoSQL stores), you must implement a custom adapter that conforms to the `oidc-provider` library's interface. This replaces the default PostgreSQL adapter in [`packages/core/src/oidc/adapter.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/adapter.ts) with your database-specific implementation.

### Required Adapter Interface Methods

A custom adapter must implement seven core methods to satisfy the OIDC provider contract:

- **`upsert(id, payload, expiresIn)`** – Creates or updates a record with TTL handling
- **`find(id)`** – Retrieves a record by its identifier
- **`findByUid(uid)`** – Locates records by user identifier
- **`findByUserCode(userCode)`** – Finds records by device flow user code
- **`consume(id)`** – Marks a record as consumed (used for authorization codes)
- **`destroy(id)`** – Permanently removes a record
- **`revokeByGrantId(grantId)`** – Bulk deletes records associated with a grant

### Custom Adapter Implementation Example

Create a new file at [`packages/core/src/oidc/custom-adapter.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/custom-adapter.ts) with the following skeleton. Replace `YourDbClient` with your specific database driver:

```typescript
// packages/core/src/oidc/custom-adapter.ts
import type { Adapter } from 'oidc-provider';

export class CustomAdapter implements Adapter {
  constructor(private readonly env: any, private readonly db: YourDbClient) {}

  async upsert(id: string, payload: Record<string, unknown>, expiresIn: number) {
    await this.db.set(id, { ...payload, expiresAt: Date.now() + expiresIn * 1000 });
  }

  async find(id: string) {
    const item = await this.db.get(id);
    if (!item) return undefined;
    return { ...item };
  }

  async findByUid(uid: string) {
    const items = await this.db.query({ uid });
    return items.length ? items[0] : undefined;
  }

  async findByUserCode(userCode: string) {
    const items = await this.db.query({ userCode });
    return items.length ? items[0] : undefined;
  }

  async consume(id: string) {
    const item = await this.db.get(id);
    if (item) {
      await this.db.set(id, { ...item, consumed: true });
    }
  }

  async destroy(id: string) {
    await this.db.delete(id);
  }

  async revokeByGrantId(grantId: string) {
    await this.db.deleteMany({ grantId });
  }
}

```

### Wiring the Custom Adapter in init.ts

Modify [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts) to instantiate your custom adapter instead of the default PostgreSQL implementation:

```typescript
// packages/core/src/oidc/init.ts
import { Provider } from 'oidc-provider';
import { CustomAdapter } from './custom-adapter.js';

const provider = new Provider(issuer, {
  // ... other options ...
  adapter: (modelName) => new CustomAdapter(env, myDbClient).bind(null, modelName),
});

```

The provider instantiates the adapter per model name (AuthorizationCode, RefreshToken, etc.), passing the specific model context to your implementation.

## Step-by-Step Implementation Guide

Follow these steps to connect Logto to your custom database:

1. **Set up your database** – Ensure the database is network-accessible from the Logto process and install the appropriate Node.js driver (e.g., `npm install mysql2` for MySQL).

2. **Add the driver to dependencies** – Update [`packages/core/package.json`](https://github.com/logto-io/logto/blob/main/packages/core/package.json) to include your database client library.

3. **Create a custom adapter** – Implement the `Adapter` interface in a new file, providing all seven required methods using your database client's query syntax.

4. **Replace the default adapter** – Edit [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts) to import and inject your custom adapter into the OIDC provider configuration.

5. **Configure environment variables** – Set `DB_URL` and any additional variables your adapter requires in `.env` or your CI configuration, as documented in [`.github/CONTRIBUTING.md`](https://github.com/logto-io/logto/blob/main/.github/CONTRIBUTING.md).

6. **Run database migrations** – Execute your custom schema creation scripts. Note that `pnpm cli db seed` only supports PostgreSQL; other databases require manual migration.

7. **Start Logto** – Launch with `pnpm start:dev` or your Docker Compose setup. Verify connectivity by inspecting database logs or Logto's application logs.

## MySQL Adapter Example

For MySQL-specific implementations, replace the generic store logic with SQL queries:

```typescript
import type { Adapter } from 'oidc-provider';
import mysql from 'mysql2/promise';

export class MySQLAdapter implements Adapter {
  private pool = mysql.createPool({ uri: process.env.DB_URL });

  async upsert(id: string, payload: Record<string, unknown>, expiresIn: number) {
    const sql = `INSERT INTO oidc_storage (id, data, expires_at)
                 VALUES (?, ?, NOW() + INTERVAL ? SECOND)
                 ON DUPLICATE KEY UPDATE data = VALUES(data), expires_at = VALUES(expires_at)`;
    await this.pool.execute(sql, [id, JSON.stringify(payload), expiresIn]);
  }

  async find(id: string) {
    const [rows] = await this.pool.execute('SELECT * FROM oidc_storage WHERE id = ?', [id]);
    return rows[0] ? JSON.parse(rows[0].data) : undefined;
  }

  // Implement remaining methods (findByUid, findByUserCode, consume, destroy, revokeByGrantId) similarly...
}

```

Register this adapter in [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts) by passing the class to the provider's `adapter` option:

```typescript
adapter: MySQLAdapter, // provider will instantiate it per model name

```

## Summary

- **Logto uses `DB_URL`** defined in [`packages/shared/src/node/env/GlobalValues.ts`](https://github.com/logto-io/logto/blob/main/packages/shared/src/node/env/GlobalValues.ts) to configure database connections
- **PostgreSQL users** can connect to custom databases by changing the DSN without modifying source code
- **Alternative databases** require implementing the `oidc-provider` Adapter interface with seven required methods: `upsert`, `find`, `findByUid`, `findByUserCode`, `consume`, `destroy`, and `revokeByGrantId`
- **Integration point** for custom adapters is [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts), replacing the default PostgreSQL adapter from [`packages/core/src/oidc/adapter.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/adapter.ts)
- **Migration scripts** (`pnpm cli db seed`) are PostgreSQL-specific; custom databases require manual schema management

## Frequently Asked Questions

### Can I use MySQL or MongoDB with Logto instead of PostgreSQL?

Yes, but you must implement a custom OIDC storage adapter. Logto's default adapter in [`packages/core/src/oidc/adapter.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/adapter.ts) only supports PostgreSQL. For MySQL, MongoDB, or other databases, create a class implementing the `Adapter` interface from `oidc-provider` with the seven required methods, then wire it into [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts).

### What environment variable configures the database connection?

The `DB_URL` environment variable controls the database connection string. This variable is defined in [`packages/shared/src/node/env/GlobalValues.ts`](https://github.com/logto-io/logto/blob/main/packages/shared/src/node/env/GlobalValues.ts) and documented in [`.github/CONTRIBUTING.md`](https://github.com/logto-io/logto/blob/main/.github/CONTRIBUTING.md). For custom PostgreSQL instances, simply update this value. For completely custom adapters, you may use `DB_URL` or define additional environment variables as needed by your implementation.

### Do I need to modify the Logto source code to use a custom database?

For PostgreSQL-compatible databases, no code changes are required—only the `DB_URL` environment variable needs updating. For non-PostgreSQL databases, you must modify [`packages/core/src/oidc/init.ts`](https://github.com/logto-io/logto/blob/main/packages/core/src/oidc/init.ts) to import and register your custom adapter implementation, replacing the default PostgreSQL adapter reference.

### Will Logto's built-in migration scripts work with my custom database?

No. The `pnpm cli db seed` command and other built-in migrations are designed specifically for PostgreSQL schemas. If you implement a custom adapter for MySQL, MongoDB, or another database, you must create and run your own migration scripts to establish the required tables or collections before starting Logto.