How to Deploy OpenSEO on Cloudflare Workers: A Complete Self-Hosting Guide

Deploy OpenSEO on Cloudflare Workers using Alchemy CLI with one command that provisions D1, KV, R2, Durable Objects, and optional Cloudflare Access protection.

OpenSEO is an open-source SEO platform that runs as a Cloudflare Workers service. This guide walks you through deploying it on your own Cloudflare account, from initial setup to production operations, using the exact configuration and tooling maintained in the every-app/open-seo repository.

Architecture Overview

OpenSEO's Cloudflare deployment relies on a coordinated stack of edge services. The entry point is src/server.ts, which inspects incoming requests and routes them to the appropriate handler:

  • MCP server for tool-based integrations
  • Agent Durable Objects for stateful chat sessions (ONBOARDING_CHAT, SAM_CHAT)
  • React-Start application for the main UI

The deployment configuration lives in wrangler.jsonc, which declares bindings for D1 database (DB), KV namespaces (KV, OAUTH_KV), R2 bucket (R2), and Durable Object classes. It also defines two Workflow bindings (site-audit-workflow, rank-check-workflow) and a cron schedule for background jobs.

Prerequisites

Before deploying, ensure you have:

  • A Cloudflare account with Workers Paid plan (required for Durable Objects and D1)
  • DataForSEO API key — this is mandatory for core functionality
  • Node.js 20+ with corepack enabled
  • pnpm package manager (bootstrapped via corepack)

Step-by-Step Deployment

1. Clone and Install

git clone https://github.com/every-app/open-seo.git
cd open-seo
corepack enable
pnpm install

Fork the repository first if you plan to maintain custom modifications.

2. Authenticate with Cloudflare

The Alchemy provisioning framework handles infrastructure-as-code deployment:

pnpm alchemy login                # Grants access:write scope

pnpm alchemy cloudflare bootstrap # Creates Alchemy state-store Worker

The bootstrap command creates a dedicated Worker that tracks Alchemy's deployment state.

3. Configure Environment Variables

Copy the template and provide your secrets:

cp .env.selfhost.example .env.selfhost

Edit .env.selfhost with at minimum these values:

Variable Required Purpose
DATAFORSEO_API_KEY DataForSEO API access for rank tracking and audits
ACCESS_ALLOWED_EMAILS Comma-separated emails authorized via Cloudflare Access

Optional variables include:

  • GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET — Google Search Console integration
  • SAM_ENABLED=true — Enable in-app SAM agents
  • TELEMETRY_DISABLED=1 — Disable analytics collection
  • ACCESS_DOMAIN — Custom Cloudflare Access application domain

4. Deploy with One Command

pnpm deploy:selfhost --yes

This single command performs the complete OpenSEO Cloudflare Workers deployment:

  • Creates D1 database and applies migrations
  • Provisions KV namespaces and R2 bucket
  • Deploys the main Worker and auxiliary workflow Workers
  • Creates Cloudflare Access application (if ACCESS_ALLOWED_EMAILS is set)
  • Injects all secrets as Worker environment bindings

5. Verify Installation

open "https://$(grep 'name' wrangler.jsonc | head -1 | sed 's/.*"name": "\([^"]*\)".*/\1/').workers.dev"

You should encounter a Cloudflare Access login screen, then the OpenSEO dashboard upon successful authentication.

Managing Your Deployment

Updating OpenSEO

Pull latest changes and redeploy:

git pull
pnpm install
pnpm deploy:selfhost --yes

If working from a fork, sync with upstream first:

git fetch upstream
git merge upstream/main

Adding Team Members

Modify ACCESS_ALLOWED_EMAILS in .env.selfhost and redeploy:


# Edit .env.selfhost with additional emails

pnpm deploy:selfhost --yes

No manual Cloudflare dashboard changes required.

Complete Teardown

Remove all provisioned resources:

pnpm alchemy destroy --env-file .env.selfhost --stage selfhost

Warning: This permanently deletes the D1 database, KV data, R2 objects, and all Workers.

Configuration Deep Dive

wrangler.jsonc Bindings

The deployment declares these Cloudflare resource bindings:

  • DB — D1 database for relational data (migrations, user accounts, audit history)
  • KV / OAUTH_KV — Separate KV namespaces for app state and OAuth token storage
  • R2 — Object storage for crawl archives and report exports
  • ONBOARDING_CHAT / SAM_CHAT — Durable Object namespaces for persistent agent conversations
  • Workflow bindingssite-audit-workflow and rank-check-workflow for background job orchestration

Cron triggers execute at configured intervals to:

  • Refresh rank tracking data
  • Reconcile audit queue state
  • Expire stale OAuth tokens in OAUTH_KV

Security Model

Cloudflare Access provides zero-trust authentication for both web UI and MCP server access. The Access application is automatically configured when ACCESS_ALLOWED_EMAILS is present. For production hardening, consult docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md which covers:

  • MCP server access via Cloudflare Access service tokens
  • Disabling telemetry with TELEMETRY_DISABLED=1
  • Custom domain configuration

Troubleshooting Common Issues

Symptom Likely Cause Fix
DB binding not found Missing D1 migration Run pnpm wrangler d1 migrations apply DB --env selfhost
Access login loop Email not in ACCESS_ALLOWED_EMAILS Add email to .env.selfhost and redeploy
MCP tools unreachable Cloudflare Access blocking requests Configure service token authentication per operations doc
Workflow failures Cron trigger misconfiguration Verify wrangler.jsonc triggers.crons array

Summary

To deploy OpenSEO on Cloudflare Workers:

  • Use pnpm deploy:selfhost --yes for one-command provisioning via Alchemy
  • Configure DATAFORSEO_API_KEY and ACCESS_ALLOWED_EMAILS in .env.selfhost
  • Reference wrangler.jsonc for all resource bindings and cron schedules
  • Read docs/SELF_HOSTING_CLOUDFLARE.md for detailed walkthroughs
  • Maintain docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md for security and monitoring guidance

The entire stack — D1, KV, R2, Durable Objects, Workflows, and Access — deploys automatically without manual Cloudflare dashboard configuration.

Frequently Asked Questions

Does OpenSEO require a paid Cloudflare plan?

Yes. Durable Objects and D1 free tiers have strict limits that OpenSEO exceeds during normal operation. The Workers Paid plan ($5/month minimum) is required for production deployments. Alchemy provisions resources within your existing account billing.

Can I use a custom domain instead of workers.dev?

Absolutely. Set ACCESS_DOMAIN in .env.selfhost to your custom hostname before deployment. Alchemy configures the Cloudflare Access application and DNS records automatically. If omitted, the deployment uses the default workers.dev subdomain derived from the Worker name in wrangler.jsonc.

How do I back up the D1 database?

Standard D1 backup procedures apply. Use wrangler d1 export for on-demand dumps, or configure periodic exports via Cloudflare API. The Alchemy state-store Worker does not include automated backup logic — implement external backup workflows for production data protection.

What happens if my DataForSEO API key expires?

Rank checking and site audits will fail silently in background workflows, with errors logged to the Worker's tail logs. The UI continues functioning for historical data review. Renew the key in DataForSEO dashboard, update DATAFORSEO_API_KEY in .env.selfhost, and redeploy — no data migration required.

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 →