# How to Configure OpenSEO for D1/SQLite Database: 5-Step Setup Guide

> Configure OpenSEO with D1 SQLite database in 5 simple steps. Use Cloudflare D1 for your OpenSEO project and skip PostgreSQL. Get started now.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-09-05

---

**Set `DATABASE_PROVIDER=d1` (or leave it unset), run `pnpm run db:migrate:local`, and start the dev server to run OpenSEO on Cloudflare D1 (SQLite) — no PostgreSQL required.**

OpenSEO is an open-source SEO management platform built for Cloudflare Workers that supports two data stores: **Cloudflare D1 (SQLite)** and **PostgreSQL**. The D1/SQLite configuration is the default and simplest way to get started, whether you're running locally or deploying to production. This guide walks through the exact configuration steps based on the OpenSEO source code, with references to specific files and implementation details.

## Set the Database Provider in Environment Variables

OpenSEO detects your database backend through the **`DATABASE_PROVIDER`** environment variable. For D1/SQLite, you have two options:

- **Recommended**: Omit `DATABASE_PROVIDER` entirely — D1 is the default
- **Explicit**: Set `DATABASE_PROVIDER=d1` in your `.env.local` or [`wrangler.toml`](https://github.com/every-app/open-seo/blob/main/wrangler.toml)

The provider detection logic lives in **[`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts)**. This file exports `getDatabaseProvider()`, which returns `"d1"` or `"postgres"` and determines which driver and schema set the application loads.

```bash

# .env.local

DATABASE_PROVIDER=d1
AUTH_MODE=local_noauth

```

With `AUTH_MODE=local_noauth`, you bypass BetterAuth for local development — this mode works seamlessly with the D1 store without additional authentication configuration.

## Verify the SQLite Schema Files

When `DATABASE_PROVIDER` resolves to `"d1"`, OpenSEO automatically loads the **SQLite schema files** located in `src/db/*.schema.ts`. Key files include:

- **[`src/db/app.schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/app.schema.ts)** — Core application tables
- Other `*.schema.ts` files in `src/db/` — Feature-specific table definitions

These files define the Drizzle ORM schema used when the provider is SQLite. No manual configuration is required; the schema is imported automatically based on the provider selection.

## Run D1 Database Migrations

Once your environment variables are set, create the local D1 database by running the migration command:

```bash
pnpm run db:migrate:local

```

This executes Drizzle migration scripts against the SQLite file bound to your local Worker environment. The migration creates all necessary tables according to the SQLite schema definitions.

After the initial migration, subsequent schema changes are handled through Drizzle's migration system — run the same command to apply updates.

## Start the Development Server

With migrations complete, launch the development server using one of these commands:

```bash

# Standard development server

pnpm run dev

# Agents-friendly server (recommended for most setups)

pnpm dev:agents

```

The `dev:agents` variant is optimized for development environments using agent-based workflows and is the recommended approach in the [LOCAL_DEVELOPMENT.md](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_DEVELOPMENT.md) documentation.

## How the D1 Database Adapter Works

The actual database connection and Drizzle adapter creation happens in **[`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts)**. Here's the implementation that switches between PostgreSQL and SQLite:

```typescript
const database =
  getDatabaseProvider() === "postgres"
    ? drizzleAdapter(pgDb, { provider: "pg", schema: pgSchema })
    : drizzleAdapter(d1Db, { provider: "sqlite", schema: d1Schema });

```

When `getDatabaseProvider()` returns `"d1"` (the default), the code:

1. Uses `d1Db` — the D1 database binding passed through the execution context
2. Sets `provider: "sqlite"` — tells Drizzle to use SQLite dialect
3. Loads `d1Schema` — the SQLite-specific schema definitions from `src/db/*.schema.ts`

This architecture allows OpenSEO to maintain **dual database support** with a single codebase. The conditional logic in [`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts) and [`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts) handles the abstraction, so your application code remains provider-agnostic.

## Optional: Switch to PostgreSQL Later

If you need to migrate from D1 to PostgreSQL in the future, change a single environment variable:

```bash
DATABASE_PROVIDER=postgres

```

You'll also need to provide a **Hyperdrive binding** or local connection string for PostgreSQL. The same adapter code in [`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts) automatically switches to the Postgres Drizzle adapter when the provider changes — no other code modifications required.

## Summary

- **D1 is the default**: Leave `DATABASE_PROVIDER` unset or explicitly set it to `"d1"` to use SQLite
- **Three key files control the setup**: [`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts) (detection), [`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts) (adapter creation), and `src/db/*.schema.ts` (table definitions)
- **Two commands to start**: `pnpm run db:migrate:local` then `pnpm dev:agents`
- **Auth mode matters**: Use `AUTH_MODE=local_noauth` for frictionless local D1 development
- **Future-proof**: One environment variable change switches the entire stack to PostgreSQL

## Frequently Asked Questions

### What is the default database provider in OpenSEO?

D1 (SQLite) is the default database provider. If you don't set `DATABASE_PROVIDER`, `getDatabaseProvider()` in [`src/db/provider.ts`](https://github.com/every-app/open-seo/blob/main/src/db/provider.ts) returns `"d1"` and the application initializes with SQLite bindings. This design prioritizes the simplest local development experience and aligns with Cloudflare's edge-native architecture.

### Can I use OpenSEO with D1 without any authentication setup?

Yes. Set `AUTH_MODE=local_noauth` in your environment variables. This mode bypasses the BetterAuth integration entirely and allows you to interact with the D1 database without configuring OAuth providers, sessions, or user management. It's the recommended approach for initial local development.

### Where does the actual database connection happen in the OpenSEO codebase?

The database adapter is instantiated in **[`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts)** using Drizzle's adapter pattern. The code evaluates `getDatabaseProvider()` and creates either a PostgreSQL adapter (`provider: "pg"`) or SQLite adapter (`provider: "sqlite"`). The `d1Db` binding comes from the Cloudflare Workers environment, injected at runtime through Wrangler's local dev server or production deployment.

### Is there any performance difference between D1 and PostgreSQL in OpenSEO?

For typical SEO management workloads — crawling, indexing status tracking, and metadata storage — D1 performs well within Cloudflare's edge network. The same Drizzle ORM queries execute against both backends. PostgreSQL becomes advantageous for complex analytical queries, high-volume concurrent writes, or when you need specific Postgres extensions. The abstraction in OpenSEO allows you to start with D1 and migrate to PostgreSQL without rewriting application code.