# How to Set Up a Development Environment for OpenSEO: Complete Local Setup Guide

> Set up your local development environment for OpenSEO quickly. Clone the repo, install dependencies, configure your API key, and launch the server to start building. Get started with OpenSEO today.

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

---

**Clone the repository, install Node 20+ and pnpm, run `pnpm install` and `pnpm run db:migrate:local`, configure `.env.local` with your base64-encoded DataForSEO API key, and launch the development server using `pnpm dev:agents` to emulate the Cloudflare Workers runtime.**

OpenSEO is an open-source SEO platform built on Cloudflare Workers using the Vite runtime, Drizzle ORM, and supports both Cloudflare D1 (SQLite) and PostgreSQL backends according to the every-app/open-seo repository. Setting up a local development environment requires Node 20+, pnpm, and specific API credentials to fetch live SEO data.

## Prerequisites

Before cloning the repository, ensure your system meets these requirements:

- **Node.js 20 or higher**: Required for the server code and build tooling.
- **pnpm**: The project uses pnpm workspaces for fast, lock-file-consistent dependency management.
- **DataForSEO API credentials**: You need a base64-encoded `login:password` token to fetch SEO data.
- **Docker (optional)**: Only required if running the PostgreSQL backend locally instead of the default D1 SQLite database.

As documented in [`docs/LOCAL_DEVELOPMENT.md`](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_DEVELOPMENT.md) in the source code, these prerequisites are mandatory for the local development workflow.

## Step 1: Clone and Install Dependencies

Start by cloning the repository and installing workspace dependencies:

```bash
git clone https://github.com/every-app/open-seo.git
cd open-seo
pnpm install

```

The `pnpm install` command installs both the web UI (`web/`) and server (`src/`) workspace packages defined in the pnpm workspace configuration.

## Step 2: Initialize the Local Database

OpenSEO defaults to Cloudflare D1 (SQLite) for local development. Generate the schema and apply migrations:

```bash
pnpm run db:migrate:local

```

This command reads the schema definitions under `src/db/` and creates a D1-compatible SQLite file locally. The migration tool is idempotent—it checks for already-applied migrations before executing new ones.

## Step 3: Configure Environment Variables

Copy the example environment file and configure your DataForSEO credentials:

```bash
cp .env.example .env.local

```

Generate a base64-encoded API key from your DataForSEO login credentials:

```bash
printf '%s' 'YOUR_LOGIN:YOUR_PASSWORD' | base64

```

Add the resulting string to `.env.local`:

```env
DATAFORSEO_API_KEY=dX...==

```

As documented in [`docs/SELF_HOSTING_CLOUDFLARE.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_CLOUDFLARE.md), these secrets can also be managed via the Cloudflare dashboard when self-hosting, but `.env.local` is required for local development.

## Step 4: Choose an Authentication Mode

The OpenSEO source code in `src/` supports three authentication modes controlled via the `AUTH_MODE` environment variable:

- **`local_noauth`**: Disables authentication and injects a trusted `admin@localhost` identity. This is automatically set by the `pnpm dev` and `pnpm dev:agents` scripts for rapid local development.
- **`cloudflare_access`**: Validates Cloudflare Access JWTs (`cf-access-jwt-assertion`) using `TEAM_DOMAIN` and `POLICY_AUD` variables. Use this for production-like testing of Cloudflare Access.
- **`hosted`**: Enables Better Auth email/password flow requiring `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL`. Use this to exercise the full hosted authentication stack locally.

To test Cloudflare Access locally, explicitly set the mode:

```bash
AUTH_MODE=cloudflare_access pnpm dev

```

## Step 5: Start the Development Server

OpenSEO provides two development server options depending on your testing needs.

**Option 1: Simple Vite Dev Server**

```bash
pnpm run dev

```

This starts the worker at `http://localhost:5173` using the Vite runtime. While suitable for quick UI iteration, this method does not emulate Cloudflare's routing model.

**Option 2: Portless Workflow (Recommended)**

```bash
mkdir -p .logs && touch .logs/dev-server.log
pnpm dev:agents

```

The `portless` tool proxies requests to `http://open-seo.localhost:1355` and mirrors the Cloudflare runtime environment, including environment bindings and hostnames. Raw worker logs are written to `.logs/dev-server.log` for easier debugging.

## Step 6: Switch to PostgreSQL (Optional)

If you outgrow the D1 SQLite limits, switch to a PostgreSQL backend using Docker.

**Start the PostgreSQL container** on port 5433 to avoid conflicts:

```bash
docker run --name openseo-postgres \
  -e POSTGRES_USER=openseo \
  -e POSTGRES_PASSWORD=openseo \
  -e POSTGRES_DB=openseo \
  -p 5433:5432 \
  -d postgres:16

```

Wait for readiness:

```bash
docker exec openseo-postgres pg_isready -U openseo -d openseo

```

**Apply PostgreSQL migrations** (stored in `drizzle-pg/`):

```bash
POSTGRES_DATABASE_URL=postgres://openseo:openseo@localhost:5433/openseo \
  pnpm db:migrate:pg

```

**Configure the provider** in `.env.local`:

```env
DATABASE_PROVIDER=postgres

```

The Vite runtime reads this flag from `.env.local` and maps the `HYPERDRIVE` binding to your connection string defined in `wrangler.jsonc`. No code changes are required. To revert to D1, set `DATABASE_PROVIDER=d1` and restart.

## Summary

- Install **Node 20+** and **pnpm**, then run `pnpm install` to populate workspace dependencies in the `web/` and `src/` directories.
- Initialize the local database with `pnpm run db:migrate:local` for SQLite/D1 development, which reads schemas from `src/db/`.
- Configure **DataForSEO API credentials** in `.env.local` using base64 encoding of your `login:password` string.
- Use **`pnpm dev:agents`** to run the portless workflow that emulates Cloudflare Workers runtime at `open-seo.localhost:1355`.
- Switch to **PostgreSQL** by running a Docker container on port 5433, applying migrations with `pnpm db:migrate:pg` (which uses files in `drizzle-pg/`), and setting `DATABASE_PROVIDER=postgres`.
- Select the appropriate **`AUTH_MODE`** (`local_noauth`, `cloudflare_access`, or `hosted`) based on your authentication testing requirements.

## Frequently Asked Questions

### What Node.js version is required for OpenSEO development?

OpenSEO requires **Node.js 20 or higher** to run the server code and development tooling. This version ensures compatibility with the Vite-powered Cloudflare Workers runtime and pnpm workspaces used throughout the `every-app/open-seo` codebase.

### How do I switch between SQLite and PostgreSQL in local development?

By default, OpenSEO uses Cloudflare D1 (SQLite) via `src/db/` schemas. To switch to PostgreSQL, start a Docker container on port 5433, run `pnpm db:migrate:pg` to apply the schema from `drizzle-pg/`, and set `DATABASE_PROVIDER=postgres` in `.env.local`. The Vite runtime automatically reconfigures the `HYPERDRIVE` binding in `wrangler.jsonc` without code changes. Revert by setting `DATABASE_PROVIDER=d1`.

### What's the difference between `pnpm dev` and `pnpm dev:agents`?

`pnpm dev` starts a basic Vite server at `localhost:5173` for quick UI work but does not emulate Cloudflare's routing. `pnpm dev:agents` uses the **portless** tool to proxy requests to `open-seo.localhost:1355`, accurately mimicking the Cloudflare Workers runtime with proper environment bindings and logging to `.logs/dev-server.log`.

### How do I authenticate with DataForSEO for local development?

You need a base64-encoded string of your DataForSEO `login:password` credentials. Generate this using `printf '%s' 'LOGIN:PASSWORD' | base64`, then add the result to `DATAFORSEO_API_KEY` in `.env.local`. This token enables the application to fetch SEO data from the DataForSEO API during development.