# Open SEO Configuration Options: Complete Guide to Environment Variables and Deployment Settings

> Explore Open SEO configuration options. Master environment variables for authentication, databases, APIs, and telemetry. Deploy with confidence using our comprehensive guide.

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

---

**Open SEO runtime behavior is controlled entirely through environment variables read at startup, with 15+ configuration options spanning authentication modes, database backends, external APIs, and telemetry settings.**

All Open SEO settings live in environment variables that are validated at startup by the runtime helper in [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts). This self-hosted SEO analytics platform supports Cloudflare Workers, Docker, and traditional server deployments—each requiring different configuration combinations. Below is the authoritative breakdown of every major setting, its source file, and how to use it.

## Authentication Mode Configuration

The `AUTH_MODE` variable is the single most important setting—it determines how Open SEO handles all user authentication.

### `AUTH_MODE`

Available in [`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts), this enum controls the entire auth strategy:

| Value | Use Case | User Identity |
|-------|----------|---------------|
| `cloudflare_access` (default) | Cloudflare Access protected deployments | Derived from Cloudflare Access JWT |
| `local_noauth` | Trusted local development or Docker | Fixed admin user `admin@localhost` |
| `hosted` | Self-hosted with email/password login | Better Auth managed users |

```bash

# For local Docker development

AUTH_MODE=local_noauth

# For production Cloudflare Workers

AUTH_MODE=cloudflare_access

# For self-hosted with user management

AUTH_MODE=hosted

```

### Cloudflare Access Settings (`cloudflare_access` mode)

When using Cloudflare Access, two additional variables are required in [`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts):

- **`TEAM_DOMAIN`** — Your Cloudflare Access organization URL (e.g., `https://myteam.cloudflareaccess.com`)
- **`POLICY_AUD`** — The audience tag from your Cloudflare Access application configuration

Both values are used to verify incoming JWT signatures against the correct Access organization.

### Better Auth Settings (`hosted` mode)

For email/password authentication, Open SEO integrates with Better Auth. Configure these in [`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts):

- **`BETTER_AUTH_URL`** — URL of your Better Auth service (e.g., `https://auth.your-domain.com`)
- **`BETTER_AUTH_SECRET`** — Random secret generated on first start, used to sign JWTs

Without these, `hosted` mode will fail validation during [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) checks.

## Database Configuration Options

Open SEO supports two database backends, selected implicitly by which connection variables you provide.

### Cloudflare D1 (Default for Workers)

Defined in [`drizzle-prod.config.ts`](https://github.com/every-app/open-seo/blob/main/drizzle-prod.config.ts), these three variables enable Cloudflare's serverless database:

- **`CLOUDFLARE_ACCOUNT_ID`** — Hex string from Cloudflare dashboard
- **`CLOUDFLARE_API_TOKEN`** — API token with D1 edit permissions
- **`CLOUDFLARE_DATABASE_ID`** — UUID of your D1 database instance

```bash

# Cloudflare dashboard → Workers → Settings → Variables

CLOUDFLARE_ACCOUNT_ID=your_account_id_here
CLOUDFLARE_API_TOKEN=your_token_here
CLOUDFLARE_DATABASE_ID=your_database_uuid_here

```

### PostgreSQL (Docker or Self-Hosted)

For traditional deployments, [`drizzle-pg.config.ts`](https://github.com/every-app/open-seo/blob/main/drizzle-pg.config.ts) accepts a standard connection string:

- **`POSTGRES_DATABASE_URL`** — Full PostgreSQL URI: `postgresql://user:pass@host:port/database`

This is the recommended approach when running Open SEO in Docker Compose or on a VM with managed Postgres.

## External API Integrations

### DataForSEO (`DATAFORSEO_API_KEY`)

Located in [`docs/DATAFORSEO_API_KEY.md`](https://github.com/every-app/open-seo/blob/main/docs/DATAFORSEO_API_KEY.md), this enables keyword research, backlink analysis, and site audit features. The value must be Base64-encoded `email:password`:

```bash

# Generate from your DataForSEO account credentials

DATAFORSEO_API_KEY=$(echo -n "your@email.com:yourpassword" | base64)

```

Without this variable, all DataForSEO-backed endpoints return 503 Service Unavailable.

### Cloudflare Turnstile (`TURNSTILE_SITE_KEY`)

Set in [`src/lib/auth-turnstile.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-turnstile.ts), this enables bot protection on sign-up and login pages. Required when `AUTH_MODE=hosted` and you want to prevent automated registration.

## Telemetry and Analytics Configuration

### Self-Host Telemetry Control

Two variables in [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) control usage reporting:

- **`OPENSEO_TELEMETRY_DISABLED=true`** — Explicit opt-out
- **`DO_NOT_TRACK=true`** — Alternative opt-out (honored if first variable unset)

When neither is set, Open SEO sends lightweight usage statistics to help the development team prioritize features.

### PostHog Analytics (`POSTHOG_HOST`, `POSTHOG_PUBLIC_KEY`)

Configured in [`src/lib/selfhost-telemetry.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/selfhost-telemetry.ts), these route product analytics to your own PostHog instance instead of the default telemetry endpoint:

```bash
POSTHOG_HOST=https://ph.yourcompany.com
POSTHOG_PUBLIC_KEY=phc_your_public_key_here

```

## Server and Site Configuration

### Development Server (`PORT`)

In [`vite.config.ts`](https://github.com/every-app/open-seo/blob/main/vite.config.ts), this sets the listening port for local development and Docker containers:

```bash
PORT=3000  # default

```

### Site URL (`VITE_SITE_URL` / `SITE_URL`)

Used by [`web/src/lib/seo.ts`](https://github.com/every-app/open-seo/blob/main/web/src/lib/seo.ts) for sitemap generation and SEO utilities. Should match the URL you're auditing:

```bash
VITE_SITE_URL=https://example.com

```

Note: Both variable names are accepted; `VITE_SITE_URL` takes precedence for Vite environments.

## Configuration Loading and Validation

Open SEO uses a runtime helper in [`src/server/lib/runtime-env.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/runtime-env.ts) that mirrors the Cloudflare Workers binding API:

```ts
export function getOptionalEnvValue(name: keyof Cloudflare.Env) {
  return typeof process !== "undefined"
    ? process.env?.[name]
    : undefined;
}

```

All variables are typed in [`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts). During startup, [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) validates:

1. `AUTH_MODE` is a valid enum value
2. Required variables for the selected auth mode are present
3. Database connection variables match exactly one backend (D1 XOR Postgres)

Failure produces a clear error message naming the missing variable.

## Deployment Scenario Reference

| Scenario | Key Variables | File Template |
|----------|-------------|---------------|
| **Docker local dev** | `AUTH_MODE=local_noauth`, `POSTGRES_DATABASE_URL`, `PORT` | `.env.local` |
| **Cloudflare Workers** | `AUTH_MODE=cloudflare_access`, `TEAM_DOMAIN`, `POLICY_AUD`, D1 variables | Dashboard bindings |
| **Hosted SaaS** | `AUTH_MODE=hosted`, `BETTER_AUTH_URL`, `BETTER_AUTH_SECRET` | Generated by platform |
| **Self-hosted Postgres** | `AUTH_MODE=hosted`, `POSTGRES_DATABASE_URL`, optional Turnstile, PostHog | `.env.production` |

### Docker Quick Start

```bash

# .env.local

AUTH_MODE=local_noauth
DATAFORSEO_API_KEY=$(echo -n "email:password" | base64)
POSTGRES_DATABASE_URL=postgresql://postgres:postgres@db:5432/openseo
PORT=3000
OPENSEO_TELEMETRY_DISABLED=true

```

```bash
docker compose up -d

```

### Cloudflare Workers Quick Start

In the Cloudflare dashboard, set these as Worker variables (not secrets, unless marked):

| Variable | Type | Value |
|----------|------|-------|
| `AUTH_MODE` | Plain | `cloudflare_access` |
| `TEAM_DOMAIN` | Plain | `https://yourteam.cloudflareaccess.com` |
| `POLICY_AUD` | Plain | your-access-app-audience-tag |
| `DATAFORSEO_API_KEY` | **Secret** | base64-encoded credentials |
| `CLOUDFLARE_ACCOUNT_ID` | Plain | from dashboard |
| `CLOUDFLARE_API_TOKEN` | **Secret** | with D1:edit permission |
| `CLOUDFLARE_DATABASE_ID` | Plain | D1 database UUID |

## Key Source Files for Configuration

- **[`worker-configuration.d.ts`](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts)** — Complete `Cloudflare.Env` interface definition
- **[`src/lib/auth-mode.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth-mode.ts)** — `AUTH_MODE` enum and validation logic
- **[`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts)** — Startup validation that catches missing variables
- **[`drizzle-prod.config.ts`](https://github.com/every-app/open-seo/blob/main/drizzle-prod.config.ts)** / **[`drizzle-pg.config.ts`](https://github.com/every-app/open-seo/blob/main/drizzle-pg.config.ts)** — Database backend configurations
- **[`docs/SELF_HOSTING_DOCKER.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_DOCKER.md)** — Recommended Docker defaults
- **[`docs/LOCAL_DEVELOPMENT.md`](https://github.com/every-app/open-seo/blob/main/docs/LOCAL_DEVELOPMENT.md)** — Local development configuration guide

## Summary

- **Authentication is mode-driven**: Choose `cloudflare_access`, `local_noauth`, or `hosted` via `AUTH_MODE`, then supply the corresponding variables defined in [`src/lib/auth.ts`](https://github.com/every-app/open-seo/blob/main/src/lib/auth.ts)
- **Database backend is implicit**: Provide D1 variables for Cloudflare Workers, or `POSTGRES_DATABASE_URL` for Docker/VM deployments
- **External features require keys**: `DATAFORSEO_API_KEY` unlocks SEO analysis; `TURNSTILE_SITE_KEY` adds bot protection
- **Validation happens at startup**: The [`selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/selfhost-preflight.ts) script ensures configurations are complete before the service starts
- **Telemetry is opt-out**: Set `OPENSEO_TELEMETRY_DISABLED=true` or `DO_NOT_TRACK=true` to disable usage reporting

## Frequently Asked Questions

### What happens if I don't set `AUTH_MODE`?

Open SEO defaults to `cloudflare_access`, which will fail to validate requests unless `TEAM_DOMAIN` and `POLICY_AUD` are also configured. For local development, explicitly set `AUTH_MODE=local_noauth` to bypass authentication entirely.

### Can I use PostgreSQL with Cloudflare Workers?

No. Cloudflare Workers only support D1 (Cloudflare's native serverless database) due to platform constraints. PostgreSQL is exclusively for Docker or self-hosted VM deployments where Node.js has direct network access to the database server.

### How do I generate the `DATAFORSEO_API_KEY` format?

Run `echo -n "your@email.com:yourpassword" | base64` in any terminal. The `-n` flag is critical—without it, the newline character corrupts the encoding. The resulting string is what you set as the environment variable value.

### Why does my self-hosted deployment fail with "Missing required variables"?

The [`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts) validation runs before the server starts and checks that your `AUTH_MODE` selection has all required dependencies. Common fixes: add `BETTER_AUTH_URL` and `BETTER_AUTH_SECRET` for `hosted` mode, or confirm `TEAM_DOMAIN` and `POLICY_AUD` for `cloudflare_access` mode.