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/*.sql and 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.jsonc and 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:

  1. Provisions a D1 database, KV namespaces, and an R2 bucket (all suffixed with selfhost).
  2. Runs the SQLite migrations located in drizzle-pg/.
  3. Deploys the Worker bundle to Cloudflare.
  4. 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 with pnpm 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:

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 →