How to Run OpenSEO Locally: Complete Development Setup Guide

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 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:

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 in the every-app/open-seo repository, this ensures consistency across development environments.

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:

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:

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:

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:

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

pnpm run dev

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

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

This command executes the entry point in 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 and serp-locations.ts—handle API requests via TanStack Query, communicating with the React frontend configured in 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 supports both backends interchangeably.

Refer to docs/LOCAL_POSTGRES.md for connection string formatting and migration steps specific to Postgres.

Understanding Authentication Modes

The 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 and can be swapped for Postgres following the dedicated documentation.
  • Authentication behavior is controlled by AUTH_MODE in 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.

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 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, simplifies local development when using git worktrees or running multiple instances.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →