How to Self-Host OpenSEO on Cloudflare Workers: A Complete Deployment Guide
Deploy OpenSEO to Cloudflare Workers by cloning the every-app/open-seo repository, configuring .env.selfhost, and running pnpm deploy:selfhost --yes via the Alchemy CLI, which automatically provisions D1, KV, R2, and Cloudflare Access protections.
OpenSEO is an open-source SEO dashboard designed to run entirely on Cloudflare's serverless edge. When you self-host OpenSEO on Cloudflare Workers, you retain full control over your data while leveraging native services like D1 for relational storage, KV for caching, and R2 for object storage. This guide walks through the exact deployment steps, file paths, and commands defined in the every-app/open-seo source code.
Architecture of Self-Hosted OpenSEO on Cloudflare Workers
The OpenSEO architecture is built around a single Worker bundle that binds to multiple Cloudflare data stores and is gated by Cloudflare Access. All resources are namespaced with the stage name selfhost, allowing isolated deployments within the same Cloudflare account.
Worker Runtime and TypeScript Bindings
The core application logic is implemented in TypeScript and compiled into a single Worker bundle. The environment bindings and variables are typed in [worker-configuration.d.ts](https://github.com/every-app/open-seo/blob/main/worker-configuration.d.ts), ensuring type-safe access to D1, KV, and R2 inside the Worker runtime.
Cloudflare Data Stores
OpenSEO relies on three managed data services provisioned during deployment:
- D1 database – Persistent relational storage for projects, users, and audit data. Migrations live in
drizzle-pg/*.sqland are applied automatically. - KV namespaces – Low-latency key/value storage for cached data and telemetry flags. Definitions are stored in
wrangler.jsonc. - R2 bucket – Object storage for large assets such as screenshots and crawled pages. This is also declared in
wrangler.jsoncand attached as a Worker binding.
Zero-Trust Security with Cloudflare Access
Access to the deployed Worker is protected by a Cloudflare Access application created during deployment. Allowed emails are read from the ACCESS_ALLOWED_EMAILS variable in .env.selfhost, enforcing Zero-Trust gating at the edge before any request reaches the Worker.
Prerequisites for Self-Hosting OpenSEO
Before you begin, ensure you have the following:
- Node.js 22.6+ and PNPM enabled via Corepack.
- A Cloudflare account with R2 enabled (requires a payment method on file).
- An active DataForSEO account and API key (required for the SEO data source).
Step-by-Step Deployment Guide
Clone the Repository and Install Dependencies
Start by cloning the open-source repository and installing dependencies:
git clone https://github.com/every-app/open-seo.git
cd open-seo
corepack enable
pnpm install
Authenticate with Cloudflare via Alchemy
OpenSEO uses the Alchemy CLI to manage infrastructure. Log in and bootstrap the Cloudflare state store:
pnpm alchemy login
pnpm alchemy cloudflare bootstrap
If you previously logged in without the access:write scope, re-authenticate with:
pnpm alchemy login --configure
Configure Environment Variables
Copy the example environment file and edit it:
cp .env.selfhost.example .env.selfhost
Fill in the required values inside .env.selfhost:
DATAFORSEO_API_KEY– Your DataForSEO API key.ACCESS_ALLOWED_EMAILS– Comma-separated list of emails allowed through Cloudflare Access.
You can view the full template in .env.selfhost.example. Optional variables include TEAM_DOMAIN and POLICY_AUD if you prefer to manage the Access application manually.
Run the Deploy Command
Deploy the entire stack with one command defined in [alchemy.run.ts](https://github.com/every-app/open-seo/blob/main/alchemy.run.ts):
pnpm deploy:selfhost --yes
This command performs the following actions:
- Provisions a D1 database, KV namespaces, and an R2 bucket (all suffixed with
selfhost). - Runs the SQLite migrations located in
drizzle-pg/. - Deploys the Worker bundle to Cloudflare.
- Creates a Cloudflare Access application restricted to the emails listed in
.env.selfhost.
After completion, the CLI prints your Worker URL (for example, https://my-openseo.workers.dev).
Verify the Deployment
Open the printed URL in your browser. You will be prompted to authenticate through Cloudflare Access. Then confirm runtime health by calling the health endpoint:
curl https://<your-worker-hostname>/api/health
This endpoint reports runtime checks and database connectivity status. If you encounter issues, stream live Worker logs:
pnpm exec wrangler tail --env selfhost
Post-Deployment Operations
Updating Your Instance
To pull in upstream changes and redeploy:
git pull
pnpm install
pnpm deploy:selfhost --yes
If you forked the repository, replace git pull with the appropriate fetch and merge workflow.
Managing Teammate Access
Add new emails to ACCESS_ALLOWED_EMAILS in .env.selfhost and redeploy. The Alchemy script updates the Access policy automatically. If you manage the Access application manually through the Zero-Trust dashboard, subsequent deploys will not overwrite your custom policy.
Disabling Telemetry
OpenSEO sends anonymized usage telemetry by default. To disable it, add the following to .env.selfhost and redeploy:
OPENSEO_TELEMETRY_DISABLED=1
For details on MCP server telemetry and operations, refer to [docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md).
Teardown and Cleanup
To completely remove the self-hosted instance and all associated data, run:
pnpm alchemy destroy --env-file .env.selfhost --stage selfhost
This destroys the Worker, D1 database, KV namespaces, R2 bucket, and the Cloudflare Access application. All stored data is permanently deleted.
Summary
- OpenSEO runs as a single Cloudflare Worker with bindings to D1, KV, and R2, all configured in
wrangler.jsonc. - The Alchemy CLI automates provisioning, migration, and deployment via
pnpm deploy:selfhost --yes. - Access is enforced through Cloudflare Access, with allowed emails defined in
.env.selfhost. - Health is verified at
/api/health, and logs are tailed withpnpm exec wrangler tail --env selfhost. - Destroy the entire stack cleanly using
pnpm alchemy destroy --env-file .env.selfhost --stage selfhost.
Frequently Asked Questions
What Cloudflare services does OpenSEO require?
OpenSEO requires a D1 database for relational data, KV for key-value caching, R2 for object storage, and Cloudflare Access for Zero-Trust authentication. These are all provisioned automatically during deployment according to the [alchemy.run.ts](https://github.com/every-app/open-seo/blob/main/alchemy.run.ts) script.
How do I add new users after deployment?
Add the user's email to the ACCESS_ALLOWED_EMAILS variable in .env.selfhost and run pnpm deploy:selfhost --yes. If you manually configured the Access application in the Zero-Trust dashboard, you can edit the Allow policy there instead; the deploy script will not overwrite manual changes.
Can I disable telemetry in self-hosted OpenSEO?
Yes. Set OPENSEO_TELEMETRY_DISABLED=1 inside .env.selfhost and redeploy. The telemetry endpoint is documented further in [docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_CLOUDFLARE_OPERATIONS.md).
How do I completely remove a self-hosted OpenSEO instance?
Run pnpm alchemy destroy --env-file .env.selfhost --stage selfhost. This command removes the Worker, D1, KV, R2 resources, and the Access application, permanently deleting all stored data associated with the selfhost stage.
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 →