Skip to main content
The account owner must complete a successful Cloudflare Access sign-in on the custom app domain before the deployment is ready. Back up D1, Git history, and R2 before serving customer data. The Cloudflare deployment uses two Workers and three origins. The app Worker serves the Access-protected browser origin and a public API origin for API-key requests. The public, cookieless usercontent Worker serves raw file bytes. Both Workers use the same D1, KV, R2, and Artifacts resources.

Prerequisites

You need:
  • a Cloudflare account with Workers, D1, KV, R2, Artifacts, Durable Objects, and Zero Trust;
  • a Cloudflare zone for the app, API, and usercontent custom domains;
  • Node, pnpm, and authenticated wrangler access to your Cloudflare account; and
  • a clean checkout of the container-v0.2.0 source tag.
Enable R2 and Artifacts in the Cloudflare dashboard before you start. Without Artifacts, document storage and history operations fail even if /healthz returns ok: true. Install the locked dependencies from the repository root:

Understand the three origins

The app and API origins route to the same app Worker. Protect only the browser app origin with Access. You must also deploy the usercontent Worker. If a raw URL reaches the app Worker, it returns the browser app HTML instead of the requested file. The self-host templates do not include the rate limits used by hosted Workspaces. If the public API origin allows anonymous creation, add a Cloudflare WAF rule to its custom domain to limit abuse.

Create the shared resources

Run every command block on this page from the repository root. The examples use the resource names shown below. If you change a name, update every later command and both Worker configs. Record resource IDs outside the repository.
The Artifacts create command can print a bearer token. Treat all command output from that step as secret. Do not paste it into a log, ticket, or screenshot.

Configure and deploy the app Worker

1

Copy the app template

Git ignores the copied file. Keep only placeholders in the tracked template.
2

Set the app bindings

Fill in the app Worker name, D1 ID, KV ID, both R2 bucket names, Artifacts namespace, source commit, app origin, API origin, usercontent origin, instance organization ID, and organization name. Set database_name to the exact D1 name you created, such as workspaces-db, in addition to setting database_id. Keep separate custom-domain routes for the app and API origins.Set AUTH_MODE to cloudflare_access. Add the Access team domain and application audience from Configure Cloudflare Access.
3

Apply D1 migrations

Re-running this command applies only pending migrations.
4

Set the app secrets

Store the values only as Worker Secrets. Do not place them in the config.
5

Build the browser app

From the repository root, run:
6

Deploy the app Worker

Configure and deploy the usercontent Worker

1

Copy the usercontent template

2

Use the same data resources

Set a different Worker name and a different custom domain. Copy the exact D1 name, D1 ID, KV ID, both R2 bucket names, Artifacts namespace, and source commit used by the app Worker. The database_name and database_id values must match the app Worker config.Do not add app secrets, Durable Objects, sessions, or D1 migrations to this Worker.
3

Deploy the usercontent Worker

4

Set the usercontent origin

Set USERCONTENT_ORIGIN in the app Worker config to the usercontent origin. Redeploy the app Worker so new raw links use that host.

Verify the deployment

  1. Request /healthz on the app, API, and usercontent origins. Confirm all three report the same source commit.
  2. Complete Cloudflare Access setup and sign in on the app origin.
  3. Create a Markdown document and copy its raw link.
  4. Request the living and exact-version raw URLs from the usercontent origin. Both should return 200 with text/markdown and the document bytes.
  5. Confirm the same raw path on the app origin does not serve the file.
  6. Create a test API key after sign-in. Use it for a request to the public API origin, revoke it, and confirm the next request returns 401 unauthorized.
  7. Open the document in two browser windows and confirm live editing connects.
A new Worker origin can return a short-lived error immediately after its first deploy. Retry for up to one minute before changing the configuration.
Cloudflare Access and custom domains require dashboard setup. The account owner must complete a successful human sign-in before the deployment is ready.

Optional product telemetry

The self-host template has no TELEMETRY binding, so Workspaces records no product telemetry and reports nothing to Plannotator. To opt in, add an Analytics Engine dataset binding named TELEMETRY to the app Worker. The data then stays in your Cloudflare account. It contains event names and broad categories, not account IDs, workspace IDs, document IDs, paths, or URLs. Reviewed July 22, 2026. Maintained by the Plannotator documentation team.
Last modified on July 24, 2026