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

> Set up your OpenSEO development environment effortlessly. Follow this guide to install dependencies, configure your API key, and launch the local server with pnpm.

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

---

**Install Node 20+ and pnpm, clone the repository, install dependencies with `pnpm install`, initialize the SQLite database with `pnpm run db:migrate:local`, configure your DataForSEO API key in `.env.local`, and run `pnpm dev:agents` to start the Vite-powered development server.**

OpenSEO is an open-source SEO platform built for Cloudflare Workers using Node 20+, pnpm workspaces, and Drizzle ORM. To set up a development environment for OpenSEO, you will configure a local SQLite database (or optional PostgreSQL), set up authentication modes, and run a Vite-powered worker that mimics the Cloudflare production runtime. This guide walks through the exact steps documented in the `every-app/open-seo` repository.

## Prerequisites

Before you begin, ensure you have the following installed:

- **Node 20+** – Required runtime for the server code and tooling.
- **pnpm** – The project uses pnpm workspaces for fast, lock-file-consistent installs.
- **DataForSEO API credentials** – OpenSEO fetches SEO data from DataForSEO; you need a base64-encoded `login:password` token.
- **Docker (optional)** – Needed only if you want to run the PostgreSQL backend locally instead of the default Cloudflare D1 SQLite.

## Step-by-Step Setup

### Clone the Repository

Start by cloning the OpenSEO repository and navigating into the project directory:

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

```

### Install Dependencies

Run pnpm to install both the web UI (`web/`) and server (`src/`) workspace packages:

```bash
pnpm install

```

### Initialize the Local Database

OpenSEO uses Drizzle ORM with a Cloudflare D1-compatible SQLite backend by default. Generate the schema and apply migrations:

```bash
pnpm run db:migrate:local

```

This command reads the schema definitions under `src/db/` and creates a local SQLite file. It is safe to re-run, as the tool checks for already-applied migrations.

### Configure Environment Variables

Copy the example environment file and add your DataForSEO credentials:

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

```

Encode your DataForSEO `login:password` with base64 and add it to `.env.local`:

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

```

Insert the resulting string into `.env.local`:

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

```

According to the [`docs/SELF_HOSTING_CLOUDFLARE.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_CLOUDFLARE.md) file, this secret can also be configured via the Cloudflare dashboard when self-hosting.

### Choose Authentication Mode

OpenSEO supports three authentication modes controlled via the `AUTH_MODE` environment variable:

- **`local_noauth`** – No auth checks; injects a trusted `admin@localhost` identity. This is automatically set when using the development scripts.
- **`cloudflare_access`** – Validates Cloudflare Access JWTs (`cf-access-jwt-assertion`). Requires `TEAM_DOMAIN` and `POLICY_AUD` for production-like testing.
- **`hosted`** – Better Auth email/password flow; requires `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL`.

To test Cloudflare Access locally, run:

```bash
AUTH_MODE=cloudflare_access pnpm dev

```

### Run the Development Server

You have two options for running the local development server.

**Option 1: Simple Vite dev server**

```bash
pnpm run dev

```

This starts the worker at `http://localhost:5173` but does not emulate Cloudflare’s routing model.

**Option 2: Recommended `portless` workflow**

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

```

The `portless` tool proxies requests to `http://open-seo.localhost:1355` and writes raw worker logs to `.logs/dev-server.log`. This mirrors the Cloudflare runtime, exposing the same environment bindings and hostnames used in production.

### Switch to PostgreSQL (Optional)

If you prefer PostgreSQL over SQLite, or if you are testing scaling scenarios, you can run a local Postgres container.

Start the Docker 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 the Postgres migrations located under `drizzle-pg/`:

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

```

Update `.env.local` to switch the provider:

```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`. To revert to D1, set `DATABASE_PROVIDER=d1` and restart.

## Common Development Commands

Here is a consolidated reference for the most frequent commands when developing OpenSEO:

```bash

# Install dependencies

pnpm install

# Initialize SQLite database

pnpm run db:migrate:local

# Copy environment template

cp .env.example .env.local

# Edit .env.local to add base64-encoded DATAFORSEO_API_KEY

# Start recommended dev server (with portless)

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

# Test with Cloudflare Access auth

AUTH_MODE=cloudflare_access pnpm dev

# Start PostgreSQL locally (optional)

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

# Apply PostgreSQL migrations

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

```

## Summary

- **Install prerequisites**: Node 20+, pnpm, and optionally Docker.
- **Initialize the database**: Run `pnpm run db:migrate:local` to create the SQLite schema from `src/db/`.
- **Configure secrets**: Copy `.env.example` to `.env.local` and add your base64-encoded DataForSEO API key.
- **Start the server**: Use `pnpm dev:agents` for the most accurate Cloudflare runtime simulation via portless.
- **Switch backends**: Set `DATABASE_PROVIDER=postgres` and run `pnpm db:migrate:pg` after starting a Docker PostgreSQL container on port 5433.
- **Test auth flows**: Set `AUTH_MODE` to `cloudflare_access` or `hosted` when testing specific authentication requirements.

## Frequently Asked Questions

### What are the minimum system requirements for OpenSEO development?

You need **Node 20 or higher** and **pnpm** installed on your system. The default database is SQLite (via Cloudflare D1 compatibility), so no additional database software is required unless you choose to run PostgreSQL locally. If using PostgreSQL, Docker is required to run the containerized database.

### How do I obtain and configure the DataForSEO API key?

You must have a DataForSEO account to get login credentials. According to the [`docs/LOCAL_DEVELOPMENT.md`](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_DEVELOPMENT.md), you encode your `login:password` string using base64 (e.g., `printf '%s' 'login:pass' | base64`) and paste the result into the `DATAFORSEO_API_KEY` field of your `.env.local` file. This token is required for the application to fetch SEO data.

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

`pnpm dev` starts a standard Vite development server at `http://localhost:5173` and automatically sets `AUTH_MODE=local_noauth`. `pnpm dev:agents` runs the worker through **portless**, which mimics Cloudflare’s routing model and proxies to `http://open-seo.localhost:1355`. The portless method is recommended because it provides environment bindings identical to production and logs output to `.logs/dev-server.log` for easier debugging.

### Can I switch between SQLite and PostgreSQL without changing code?

Yes. The application uses a provider-agnostic DB layer in `src/db/`. To switch backends, set the `DATABASE_PROVIDER` environment variable to `postgres` or `d1` in your `.env.local` file. For PostgreSQL, you must also run `pnpm db:migrate:pg` after setting the `POSTGRES_DATABASE_URL`. The Vite runtime automatically maps the `HYPERDRIVE` binding in `wrangler.jsonc` based on this configuration, requiring no code changes.