Files
2026-07-13 13:12:00 +08:00

6.4 KiB

Omnigent on Cloudflare (Containers + D1 + R2)

Run the Omnigent server on Cloudflare Containers, with D1 as the database and R2 as the durable artifact store. This is the serverless, scale-to-zero option: no VM or Postgres to manage, a public *.workers.dev URL (or your domain), and the container sleeps when idle.

Note

This is not the same as deploy/trycloudflare/, which is a quick tunnel that exposes a server running on your laptop. Here the server itself runs on Cloudflare.

Note

This path uses a small SQLAlchemy dialect shim (sitecustomize.py) because Cloudflare D1 isn't yet first-class in Omnigent. It works end to end — it's how this directory was validated — and the normal on-boot migrations run unmodified. The R2 artifact store, by contrast, already uses a first-class backend (S3ArtifactStore) added alongside this directory.

How it works

        HTTPS / WebSocket
browser ───────────────►  Worker (src/index.js)
                              │   getContainer("singleton").fetch(req)
                              ▼
                          Container  ──►  the omnigent server (port 8000)
                          (1 instance)        │            │
                          DATABASE_URL ───────┘            │  S3 API (boto3)
                          cloudflare_d1://…                ▼
                                 │                  OMNIGENT_ARTIFACT_URI
                                 ▼                  s3://omnigent-artifacts
                          Cloudflare D1                    │
                          (SQLite, the DB)                 ▼
                                                    Cloudflare R2
                                                    (artifact store)
  • Worker — a thin front that proxies every request to one container instance (Omnigent keeps an in-memory runner registry, so it's single-replica).
  • Container — the official ghcr.io/omnigent-ai/omnigent-server image plus the D1 SQLAlchemy dialect, a shim that re-registers it as a proper SQLite dialect, and boto3 (this directory's Dockerfile).
  • D1 is the database. The server reaches it through the sqlalchemy-cloudflare-d1 dialect, which speaks D1's HTTP API — so DATABASE_URL is cloudflare_d1://<account>:<api-token>@<database-id>.
  • R2 is the artifact store. Cloudflare container disk is ephemeral, so artifacts (agent bundles, user files) go to R2 over its S3 API via Omnigent's native S3ArtifactStore, selected with OMNIGENT_ARTIFACT_URI=s3://<bucket>. No FUSE mount, no sidecar.

What's in here

File Purpose
Dockerfile derived image: server + D1 dialect + shim + boto3
sitecustomize.py shim re-registering cloudflare_d1 as a SQLite dialect (auto-loaded)
src/index.js the Worker that proxies to the container
wrangler.jsonc Worker + Container + Durable Object config
package.json wrangler + @cloudflare/containers

Prerequisites

  • A Cloudflare account on the Workers Paid plan — Containers require it.
  • Docker running locally (wrangler deploy builds the image).
  • Node (for wrangler).
  • wrangler login (or a CLOUDFLARE_API_TOKEN).
cd deploy/cloudflare
npm install
npx wrangler login

Deploy

1. Create the D1 database

npx wrangler d1 create omnigent
# note the "database_id" it prints — call it <DATABASE_ID>

2. Create the R2 bucket

npx wrangler r2 bucket create omnigent-artifacts

3. A D1 API token (for DATABASE_URL)

The dialect authenticates to D1's REST API with a Cloudflare API token. Create one at dash.cloudflare.com → My Profile → API Tokens → Create Token → Custom, with permission Account → D1 → Edit. Your DATABASE_URL is then:

cloudflare_d1://<ACCOUNT_ID>:<D1_API_TOKEN>@<DATABASE_ID>

4. R2 S3 credentials (for the artifact store)

The artifact store uses R2's S3 API, which needs an Access Key ID + Secret Access Key. Create them at dash.cloudflare.com → R2 → Manage R2 API Tokens → Create API Token → Object Read & Write. It shows an Access Key ID and Secret Access Key once — save both.

Alternative: derive S3 keys from an existing API token

Any API token with R2 permissions can be used as S3 credentials without minting a separate R2 token (docs): Access Key ID = the token's id, Secret Access Key = sha256(token value).

python3 -c 'import hashlib,sys; print(hashlib.sha256(sys.argv[1].encode()).hexdigest())' "<TOKEN_VALUE>"

5. Configure and set secrets

In wrangler.jsonc, set AWS_ENDPOINT_URL_S3 to your account's R2 endpoint (https://<ACCOUNT_ID>.r2.cloudflarestorage.com). Then set the four secrets:

# DATABASE_URL — the cloudflare_d1:// string from step 3
npx wrangler secret put DATABASE_URL

# Session cookie secret — any 64-hex string
openssl rand -hex 32 | npx wrangler secret put OMNIGENT_ACCOUNTS_COOKIE_SECRET

# R2 S3 credentials from step 4
npx wrangler secret put AWS_ACCESS_KEY_ID
npx wrangler secret put AWS_SECRET_ACCESS_KEY

6. Deploy

npx wrangler deploy
# -> https://omnigent.<your-subdomain>.workers.dev

The container cold-starts on the first request (~10s), then stays warm:

curl https://omnigent.<your-subdomain>.workers.dev/health   # {"status":"ok"}

On a brand-new D1, the first boot runs all migrations before the server starts listening (~1 minute against D1's REST API), so the first few requests may return a 5xx while it migrates — just retry. Later boots are fast.

7. First admin + connect a host

Open the URL and the Setup screen claims the first admin (username + password). Then connect a machine to actually run agents (the server is just the control plane):

omnigent login https://omnigent.<your-subdomain>.workers.dev
omnigent host  --server https://omnigent.<your-subdomain>.workers.dev

Verifying durability

The point of R2 is that state survives the ephemeral container. To prove it, note your data, force a fresh container (npx wrangler deploy again, or let it idle to sleep), and confirm it's still there — agents still load, sessions still exist. The database lives in D1 and the artifacts in R2; the container holds nothing durable.