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 Layer – D1 (SQLite) stores project metadata, KV handles configuration and caching, and R2 stores binary assets and exports
- Authentication – Cloudflare 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:
- Visit Cloudflare Zero Trust dashboard → Access → Applications
- Select your OpenSEO application → Edit
- Navigate to OAuth settings
- Enable Managed OAuth
- 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_EMAILSin.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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →