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
corepackenabled pnpmpackage 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 integrationSAM_ENABLED=true— Enable in-app SAM agentsTELEMETRY_DISABLED=1— Disable analytics collectionACCESS_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_EMAILSis 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 storageR2— Object storage for crawl archives and report exportsONBOARDING_CHAT/SAM_CHAT— Durable Object namespaces for persistent agent conversations- Workflow bindings —
site-audit-workflowandrank-check-workflowfor 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 --yesfor one-command provisioning via Alchemy - Configure
DATAFORSEO_API_KEYandACCESS_ALLOWED_EMAILSin.env.selfhost - Reference
wrangler.jsoncfor all resource bindings and cron schedules - Read
docs/SELF_HOSTING_CLOUDFLARE.mdfor detailed walkthroughs - Maintain
docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.mdfor 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →