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-serverimage plus the D1 SQLAlchemy dialect, a shim that re-registers it as a proper SQLite dialect, andboto3(this directory'sDockerfile). - D1 is the database. The server reaches it through the
sqlalchemy-cloudflare-d1dialect, which speaks D1's HTTP API — soDATABASE_URLiscloudflare_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 withOMNIGENT_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 deploybuilds the image). - Node (for
wrangler). wrangler login(or aCLOUDFLARE_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.