# How to Run OpenSEO Locally: Complete Development Setup Guide

> Learn how to run OpenSEO locally with this complete guide. Set up your development environment, install dependencies, migrate the database, and start the server for local access.

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

---

**You can run OpenSEO locally by installing Node.js 20+, enabling pnpm via Corepack, running `pnpm install`, migrating the local D1 database with `pnpm run db:migrate:local`, configuring your `.env.local` file with DataForSEO credentials, and starting the development server with `pnpm dev:agents` to access the dashboard at `http://open-seo.localhost:1355`.**

OpenSEO is a full-stack TypeScript SEO platform built by every-app that combines a React frontend with TanStack server functions running on Cloudflare Workers. Learning how to run OpenSEO locally allows developers to customize the keyword research engine, modify audit pipelines, or test database migrations against the Drizzle ORM schema before deploying to production.

## Prerequisites and Environment Setup

Before cloning the repository, ensure your system meets the base requirements defined in the [`docs/LOCAL_DEVELOPMENT.md`](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_DEVELOPMENT.md) file. The project requires **Node.js 20 or later** (20-lts recommended) and uses **pnpm** as its package manager.

Enable Corepack to manage the correct pnpm version:

```bash
corepack enable

```

Corepack is bundled with Node 24+; on older versions, install it manually before proceeding.

## Install Dependencies and Initialize the Database

Once prerequisites are satisfied, install the project dependencies using the exact version specified in the lockfile. According to the [`package.json`](https://github.com/every-app/open-seo/blob/main/package.json) in the every-app/open-seo repository, this ensures consistency across development environments.

```bash
pnpm install --frozen-lockfile

```

OpenSEO uses **Drizzle ORM** with a local D1 (SQLite) database by default. Run the migration command to initialize the schema defined in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts):

```bash
pnpm run db:migrate:local

```

This creates the necessary tables for projects, keywords, audits, and telemetry in your local environment.

## Configure Local Environment Variables

Create a local environment file from the provided example template:

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

```

You must add your **DataForSEO** credentials to enable keyword research and rank tracking features. Generate the base64-encoded API key:

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

```

Copy the output into the `DATAFORSEO_API_KEY` variable in `.env.local`.

For local development, disable authentication checks by setting the mode defined in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts):

```bash
echo "AUTH_MODE=local_noauth" >> .env.local

```

This mode bypasses JWT validation and injects `admin@localhost` as the default user, allowing immediate access to the dashboard.

## Start the Development Server

The repository provides two methods to start the local server. The recommended approach uses `portless` to create a stable local URL.

### Option 1: Basic Development Mode

```bash
pnpm run dev

```

### Option 2: Recommended Development with Portless (Preferred)

Create a log directory and start the worker with agent logging:

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

```

This command executes the entry point in [`src/start.ts`](https://github.com/every-app/open-seo/blob/main/src/start.ts) and binds the application to `http://open-seo.localhost:1355`. When using git worktrees, `portless` automatically prefixes the URL with your branch name (e.g., `http://feature-x.open-seo.localhost:1355`).

The server functions located in `src/serverFunctions/`—such as [`searchPerformance.ts`](https://github.com/every-app/open-seo/blob/main/searchPerformance.ts) and [`serp-locations.ts`](https://github.com/every-app/open-seo/blob/main/serp-locations.ts)—handle API requests via TanStack Query, communicating with the React frontend configured in [`web/vite.config.ts`](https://github.com/every-app/open-seo/blob/main/web/vite.config.ts).

## Optional: Switch to Postgres Backend

While D1 (SQLite) is the default for local development, you can switch to PostgreSQL if testing production-like behavior or managing larger datasets. The Drizzle ORM configuration in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts) supports both backends interchangeably.

Refer to [`docs/LOCAL_POSTGRES.md`](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_POSTGRES.md) for connection string formatting and migration steps specific to Postgres.

## Understanding Authentication Modes

The [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts) file implements three distinct authentication strategies controlled via `AUTH_MODE`:

- **`local_noauth`** – Development-only mode that skips all authentication checks and assigns a default admin user.
- **`cloudflare_access`** – Validates Cloudflare Access JWTs using the `TEAM_DOMAIN` and `POLICY_AUD` environment variables.
- **`hosted`** – Integrates with Better Auth, requiring `BETTER_AUTH_SECRET` and `BETTER_AUTH_URL` configuration.

Switch between modes by editing `.env.local` and restarting the server to test different security contexts.

## Summary

Running OpenSEO locally requires setting up a Node.js 20+ environment with pnpm, initializing the Drizzle ORM database via `pnpm run db:migrate:local`, and configuring authentication credentials in `.env.local`. Key takeaways include:

- Use `pnpm dev:agents` to start the server at `http://open-seo.localhost:1355` with stable port allocation.
- The default D1 database is defined in [`src/db/schema.ts`](https://github.com/every-app/open-seo/blob/main/src/db/schema.ts) and can be swapped for Postgres following the dedicated documentation.
- Authentication behavior is controlled by `AUTH_MODE` in [`src/shared/selfhost-checks.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/selfhost-checks.ts), with `local_noauth` recommended for development.
- Server functions in `src/serverFunctions/` power the React frontend through TanStack Query, as configured in [`web/vite.config.ts`](https://github.com/every-app/open-seo/blob/main/web/vite.config.ts).

## Frequently Asked Questions

### What is the minimum Node.js version required to run OpenSEO locally?

OpenSEO requires **Node.js 20 or later**, with the 20-lts release recommended for stability. The build system relies on modern Node features and Corepack integration to manage pnpm.

### How do I generate the DataForSEO API key for my local environment?

Create a base64-encoded string of your DataForSEO login credentials in the format `login:password`. Run `printf '%s' 'YOUR_LOGIN:YOUR_PASSWORD' | base64` in your terminal and paste the output into the `DATAFORSEO_API_KEY` variable in `.env.local`.

### Can I use PostgreSQL instead of SQLite for local development?

Yes. While the default configuration uses D1 (SQLite) via Drizzle ORM, you can configure a local Postgres instance by following the instructions in [`docs/LOCAL_POSTGRES.md`](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_POSTGRES.md) and updating your database connection strings accordingly.

### Why does the development server use port 1355 instead of a standard port?

The `pnpm dev:agents` command uses **portless** to bind to `open-seo.localhost:1355`, providing a stable, predictable URL that remains consistent across restarts. This approach, implemented in [`src/start.ts`](https://github.com/every-app/open-seo/blob/main/src/start.ts), simplifies local development when using git worktrees or running multiple instances.