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
Option 2: Recommended Development with Portless (Preferred)
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 theTEAM_DOMAINandPOLICY_AUDenvironment variables.hosted– Integrates with Better Auth, requiringBETTER_AUTH_SECRETandBETTER_AUTH_URLconfiguration.
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:agentsto start the server athttp://open-seo.localhost:1355with stable port allocation. - The default D1 database is defined in
src/db/schema.tsand can be swapped for Postgres following the dedicated documentation. - Authentication behavior is controlled by
AUTH_MODEinsrc/shared/selfhost-checks.ts, withlocal_noauthrecommended for development. - Server functions in
src/serverFunctions/power the React frontend through TanStack Query, as configured inweb/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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →