# How to Configure Open-SEO Settings: A Complete Self-Hosting Guide

> Learn how to configure Open-SEO settings for self-hosting. Follow this guide to set up your .env file and validate essential environment variables for a smooth deployment.

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

---

**Configure Open-SEO by creating a `.env` file based on `.env.example`, then run `npm run selfhost-preflight` to validate required environment variables like `DATAFORSEO_API_KEY`, `POSTGRES_DATABASE_URL`, and Cloudflare credentials before deployment.**

Open-SEO is a self-hosted SaaS-style SEO platform maintained by every-app. Understanding how to configure Open-SEO settings properly ensures your instance connects to external data providers, databases, and authentication services without runtime failures.

## Environment Variable Architecture

Open-SEO centralizes configuration through a **runtime-env** abstraction layer. The [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts) module standardizes access to environment variables across deployment targets, whether you're running locally, in Docker, or on Cloudflare Workers.

The [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) script validates every required variable at startup. This pre-flight check catches missing or malformed configuration before the application boots, preventing cryptic runtime errors.

## Required Environment Variables

All configuration lives in a single `.env` file. Copy `.env.example` from the repository root as your starting template.

### DataForSEO Integration

The `DATAFORSEO_API_KEY` variable powers rank tracking and backlink analysis features. Without this key, Open-SEO cannot fetch search engine data.

```bash
DATAFORSEO_API_KEY=your-dataforseo-api-key

```

The [`src/serverFunctions/config.ts`](https://github.com/every-app/open-seo/blob/main/src/serverFunctions/config.ts) endpoint exposes whether this integration is active:

```typescript
// From src/serverFunctions/config.ts
export const dataforseoConfigured = Boolean(
  env.DATAFORSEO_API_KEY?.trim()
);

```

### Database Connection

Open-SEO requires PostgreSQL for data persistence. The drift-SQL layer consumes `POSTGRES_DATABASE_URL` in [`drizzle-pg.config.ts`](https://github.com/every-app/open-seo/blob/main/drizzle-pg.config.ts):

```bash
POSTGRES_DATABASE_URL=postgresql://user:password@host:5432/openseo

```

### Cloudflare Workers Deployment

When deploying to Cloudflare, three additional variables enable D1 database and KV storage access:

| Variable | Purpose |
|----------|---------|
| `CLOUDFLARE_ACCOUNT_ID` | Identifies your Cloudflare account |
| `CLOUDFLARE_API_TOKEN` | API token with Workers and D1 permissions |
| `CLOUDFLARE_DATABASE_ID` | Specific D1 database identifier |

These are consumed by [`drizzle-prod.config.ts`](https://github.com/every-app/open-seo/blob/main/drizzle-prod.config.ts) and [`wrangler.toml`](https://github.com/every-app/open-seo/blob/main/wrangler.toml) during production builds.

### Optional Authentication

Better-Auth integration is controlled by two variables read in [`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts):

```bash
BETTER_AUTH_URL=https://auth.yourdomain.com
BETTER_AUTH_SECRET=your-256-bit-secret

```

### Telemetry Opt-Out

Disable usage analytics by setting either flag documented in [`SELF_HOSTING_CLOUDFLARE_OPERATIONS.md`](https://github.com/every-app/open-seo/blob/main/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md):

```bash
OPENSEO_TELEMETRY_DISABLED=1

# Alternative:

DO_NOT_TRACK=1

```

The `isTelemetryOptOutValue` helper in [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) handles both variants.

## Complete Configuration Example

```bash

# Required: External data services

DATAFORSEO_API_KEY=dfseo_live_abc123xyz

# Required: Database

POSTGRES_DATABASE_URL=postgresql://openseo:securepass@db:5432/openseo

# Required only for Cloudflare deployment

CLOUDFLARE_ACCOUNT_ID=1a2b3c4d5e6f
CLOUDFLARE_API_TOKEN=your_cloudflare_api_token
CLOUDFLARE_DATABASE_ID=uuid-of-your-d1-database

# Optional: Authentication

BETTER_AUTH_URL=https://auth.internal.company.com
BETTER_AUTH_SECRET=generated-secret-from-better-auth-cli

# Optional: Privacy

OPENSEO_TELEMETRY_DISABLED=1

```

## Validating Your Configuration

Run the pre-flight validation before starting Open-SEO:

```bash
npm run selfhost-preflight

```

This executes [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts), which checks:

- Presence of required variables
- Non-empty string values
- Telemetry flag interpretation

On validation failure, you'll receive specific guidance:

```

✖ Open-SEO configuration error: POSTGRES_DATABASE_URL is not set
✖ Open-SEO configuration error: DATAFORSEO_API_KEY appears malformed

```

Success output confirms readiness for deployment.

## Docker Deployment

The [`docker-compose.yaml`](https://github.com/every-app/open-seo/blob/main/docker-compose.yaml) demonstrates production-grade configuration injection:

```yaml
services:
  open-seo:
    image: ghcr.io/every-app/open-seo:latest
    env_file: .env
    environment:
      - NODE_ENV=production
    ports:
      - "3000:3000"
    depends_on:
      - postgres

  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: openseo
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: openseo
    volumes:
      - pgdata:/var/lib/postgresql/data

volumes:
  pgdata:

```

The `env_file: .env` directive mounts your configuration directly into the container runtime where [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts) can access it.

## Google Search Console Integration

Additional variables enable GSC data syncing. See [`docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_GOOGLE_SEARCH_CONSOLE.md) for obtaining and configuring:

- `GSC_CLIENT_ID`
- `GSC_CLIENT_SECRET`
- `GSC_REFRESH_TOKEN`

These follow the same `.env` pattern and are validated by the pre-flight script.

## Runtime Environment Access

Within application code, always use the typed `env` object rather than direct `process.env` access:

```typescript
import { env } from "@/server/lib/runtime-env";

export function checkDataSources() {
  return {
    dataforseo: Boolean(env.DATAFORSEO_API_KEY?.trim()),
    gsc: Boolean(env.GSC_REFRESH_TOKEN?.trim()),
    cloudflare: Boolean(
      env.CLOUDFLARE_ACCOUNT_ID &&
      env.CLOUDFLARE_API_TOKEN &&
      env.CLOUDFLARE_DATABASE_ID
    )
  };
}

```

This abstraction ensures compatibility across Node.js and Cloudflare Workers runtimes.

## Summary

- **Configuration source**: Single `.env` file based on `.env.example`
- **Validation**: `npm run selfhost-preflight` executes [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts)
- **Required variables**: `DATAFORSEO_API_KEY`, `POSTGRES_DATABASE_URL`
- **Cloudflare variables**: `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_API_TOKEN`, `CLOUDFLARE_DATABASE_ID`
- **Access pattern**: Use [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts) instead of raw `process.env`
- **Telemetry control**: Set `OPENSEO_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK=1`

## Frequently Asked Questions

### What happens if I skip the pre-flight check?

Open-SEO may start with undefined configuration, causing runtime failures when features attempt to connect to DataForSEO or PostgreSQL. The [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts) module will return `undefined` for missing keys, and subsequent API calls will fail with authentication or connection errors. Always run `npm run selfhost-preflight` before deployment.

### Can I use different environment files for different stages?

Yes. Specify an alternative file with `ENV_FILE=.env.production` or reference it directly in [`docker-compose.yaml`](https://github.com/every-app/open-seo/blob/main/docker-compose.yaml). The `runtime-env` module reads whatever environment is injected at runtime; the file name itself has no special meaning to Open-SEO.

### How do I rotate secrets without downtime?

Update your `.env` file, then restart containers or redeploy Workers. Open-SEO reads configuration once at startup, so changes require a process restart. For zero-downtime rotations in Kubernetes or Docker Swarm, use rolling deployments that incrementally replace containers with new environment state.