How to Set Up Self-Hosted OpenSEO with Cloudflare Access: Complete Deployment Guide

Self-hosted OpenSEO runs as a Cloudflare Worker protected by Cloudflare Access, using a single deployment command that provisions D1, KV, R2, and an Access application gated by the ACCESS_ALLOWED_EMAILS environment variable.

This guide walks you through deploying OpenSEO as a secure, self-hosted SEO analytics platform on Cloudflare's edge infrastructure. The setup combines Cloudflare's serverless compute with enterprise-grade authentication, giving you full control over your data without infrastructure maintenance overhead.

Architecture Overview

The self-hosted OpenSEO deployment consists of four core components working together on Cloudflare's platform:

  • Worker – The application logic runs directly on Cloudflare's edge network via pnpm deploy:selfhost; requests execute close to users for minimal latency
  • Data LayerD1 (SQLite) stores project metadata, KV handles configuration and caching, and R2 stores binary assets and exports
  • AuthenticationCloudflare Access creates an allow-policy from ACCESS_ALLOWED_EMAILS; users authenticate through Cloudflare's identity proxy before reaching the Worker
  • Managed OAuth – Optional integration for MCP (Model Context Protocol) clients, enabling CLI and desktop agents to authenticate dynamically through the same Access application

The deployment logic lives in the alchemy infrastructure-as-code scripts. The alchemy.run.ts file creates the Access application using your configured email allow-list (lines 170-250), while alchemy.access.ts validates that ACCESS_ALLOWED_EMAILS is properly set at runtime (lines 45-48).

Prerequisites

Before deploying, ensure you have:

  • Node.js 22.6+ installed
  • pnpm enabled via corepack
  • A Cloudflare account with Workers and Zero Trust Access enabled
  • DataForSEO API key for SEO data retrieval

Step-by-Step Deployment

1. Clone and Prepare the Repository

Fork the repository if you need a personal copy for modifications, then clone and install dependencies:

git clone https://github.com/every-app/open-seo.git   # or your fork URL

cd open-seo
corepack enable
pnpm install

2. Authenticate with Cloudflare

The alchemy CLI manages Cloudflare resource provisioning. Run the login flow once to authorize deployment permissions:

pnpm alchemy login                # answer "yes" to customize OAuth scopes

pnpm alchemy cloudflare bootstrap   # deploys the alchemy state-store Worker

The bootstrap command creates a state-store Worker that tracks your infrastructure state across deployments.

3. Configure Environment Variables

Create your self-hosting configuration from the example template:

cp .env.selfhost.example .env.selfhost

Edit .env.selfhost with your required values:


# Critical: comma-separated list of authorized email addresses

ACCESS_ALLOWED_EMAILS="alice@example.com,bob@example.com"

# Required: SEO data provider credentials

DATAFORSEO_API_KEY="your-key"

# Optional: manual Access management (skips auto-provisioning)

# TEAM_DOMAIN="your-team.cloudflareaccess.com"

# POLICY_AUD="your-access-policy-audience"

The ACCESS_ALLOWED_EMAILS variable directly drives the Access allow-policy. If you omit TEAM_DOMAIN and POLICY_AUD, alchemy automatically creates and manages the Access application for you.

4. Deploy OpenSEO

A single command provisions all resources and deploys the application:

pnpm deploy:selfhost --yes

This execution:

  • Creates the D1 database and runs schema migrations
  • Provisions KV namespaces for caching and configuration
  • Sets up the R2 bucket for asset storage
  • Deploys the Worker with bundled application code
  • Creates or updates the Cloudflare Access application with your email allow-policy

5. Validate the Deployment

Open the printed Worker URL in your browser:

open https://YOUR_WORKER_HOSTNAME

You'll be redirected to the Cloudflare Access login page. After authenticating with an email from your ACCESS_ALLOWED_EMAILS list, the Access proxy forwards your request to the Worker with a signed JWT. The OpenSEO dashboard should load, confirming successful deployment.

Post-Deployment Configuration

Enable MCP Client Access (Optional)

To allow CLI and desktop agents to connect through Cloudflare Access:

  1. Visit Cloudflare Zero Trust dashboardAccessApplications
  2. Select your OpenSEO application → Edit
  3. Navigate to OAuth settings
  4. Enable Managed OAuth
  5. Add redirect URIs (e.g., http://localhost:3000/callback)

This configuration is documented in the operations guide under "Connect the MCP server through Cloudflare Access."

Add Team Members

Expand access by appending new emails to ACCESS_ALLOWED_EMAILS:


# Edit .env.selfhost

ACCESS_ALLOWED_EMAILS="alice@example.com,bob@example.com,charlie@example.com"

# Redeploy to update the Access policy

pnpm deploy:selfhost --yes

Redeployment applies the updated allow-policy without data loss—D1, KV, and R2 resources persist across updates.

Upgrade to Latest Version

Pull upstream changes and redeploy:

git pull        # or fetch + merge from your upstream remote

pnpm install    # install any new dependencies

pnpm deploy:selfhost --yes

Your existing data and Access configuration remain intact.

Key Configuration Files

File Purpose Location
.env.selfhost.example Template for environment variables including ACCESS_ALLOWED_EMAILS GitHub
docs/SELF_HOSTING_CLOUDFLARE.md Complete deployment instructions GitHub
docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md MCP setup, telemetry, and maintenance GitHub
alchemy.run.ts Core deployment script; creates Access application GitHub
alchemy.access.ts Runtime validation of Access configuration GitHub

Summary

  • Self-hosted OpenSEO deploys to Cloudflare Workers with pnpm deploy:selfhost, provisioning D1, KV, R2, and Access in one command
  • Authentication uses Cloudflare Access with an email allow-policy from ACCESS_ALLOWED_EMAILS in .env.selfhost
  • MCP clients connect through Managed OAuth enabled on the same Access application
  • Upgrades are non-destructive: pull, install, and redeploy to update while preserving data

Frequently Asked Questions

What happens if I don't set ACCESS_ALLOWED_EMAILS?

The deployment fails. The alchemy.access.ts validation enforces this variable at runtime (lines 45-48). You must provide at least one authorized email address for the Access allow-policy.

Can I use my existing Cloudflare Access application?

Yes. Set TEAM_DOMAIN and POLICY_AUD in .env.selfhost to skip automatic Access provisioning and use your pre-configured application instead. The Worker will still validate the JWT from your existing Access deployment.

Is my SEO data stored in Cloudflare's infrastructure?

Project metadata resides in your provisioned D1 database, while cached results and configuration use KV. Binary exports and larger assets store in R2. All data stays within your Cloudflare account—you retain full ownership and can export or delete it at any time.

How do I troubleshoot Access authentication failures?

Check three common issues: (1) your email is exactly matched in ACCESS_ALLOWED_EMAILS (case-sensitive), (2) your browser session isn't caching an old JWT—try incognito mode, and (3) the Access application shows as "Active" in the Zero Trust dashboard. Redeploy with pnpm deploy:selfhost --yes to sync any configuration changes.

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 →