Skip to content

Deploy with Cloudflare

Cloudflare integrates with Pondoknusa as optional modules — pick only what you need. Pondoknusa 1.x still runs on Node.js (Fly, Railway, Docker, VPS). Full Pondoknusa on Workers is not supported yet.

New apps ship a copy-paste guide at deploy/cloudflare.md with the same module layout.

Pick your modules

I want…ModuleNeeds Node origin?
TLS, DDoS, hide origin IPModule 1: DNS + proxyYes
Cache public GET at the edgeModule 2: CDN + edge cacheYes (usually with Module 1)
S3-compatible file storageModule 3: R2Yes for uploads; no proxy required
Host Vite/client buildModule 4: PagesAPI/SSR on separate host
Staging without public portsModule 5: TunnelYes (local or remote)
WAF, rate limits, botsModule 6: WAF + securityYes (usually with Module 1)
D1 as the app databaseModule 7: D1Yes (REST from Node); binding for Workers wrappers

Deploy the Node origin first — Fly.io, Railway, or Docker.

Common combinations

StackModules
CDN in front of full-stack SSR1 + 2
API + R2 uploads1 + 3 (or 3 alone for storage)
SPA on Pages, API on Fly1 + 4 (+ 3 optional)
Production + staging tunnel1 + 2 + 5
Hardened public API1 + 2 + 6

Product status

Cloudflare productPondoknusa integrationModule
DNS + proxyOrigin on Node1
Cache / CDNETag middleware + cache rules2
R2@pondoknusa/storage-r23
PagesVite/client dist/4
TunnelPreview/staging5
WAF / DDoS / TLSIn front of origin1, 6
D1@pondoknusa/database-d17
Workers (full Pondoknusa)Not supported (roadmap)
Queues (CF)Use pondoknusa queue:work on origin

Module 1: DNS + proxy

When: Custom domain, free TLS, DDoS protection, orange-cloud origin masking.

Prerequisites: Pondoknusa running with a public hostname.

  1. Add the domain in Cloudflare DNS.
  2. Create a proxied record — CNAME to Fly/Railway hostname, or A/AAAA to a VPS IP.
  3. SSL/TLSFull (strict).
  4. Origin env:
bash
APP_URL=https://your-domain.example
PONDOKNUSA_HOST=0.0.0.0
SESSION_SECURE=true

Use TRUST_PROXY=true when the app reads X-Forwarded-*. WebSocket upgrades pass through; broadcasting still terminates on the Node origin (Redis fan-out for multiple instances).

Standalone: TLS + DDoS without edge caching.


Module 2: CDN + edge cache

When: Repeat traffic to public, cacheable GET routes.

Prerequisites: Module 1 or another CDN in front of the same origin.

typescript
import { createHttpCacheMiddleware } from '@pondoknusa/http';

Route.get('/posts/:slug', showPost, {
  middleware: [createHttpCacheMiddleware({ maxAge: 300 })],
});

In Cloudflare: enable Origin Cache Control, add Cache Rules for public GET paths, and bypass authenticated routes (/dashboard/*, anything with Set-Cookie). See edge cache cookbook.

RouteEdge cache?
Public HTML / JSONYes — short max-age + ETag
Session / dashboardBypass
/build/* fingerprinted assetsYes — long max-age
WebSocketBypass

Skip if all routes are personalized or you only need TLS (Module 1).


Module 3: R2 object storage

When: Uploads, exports, or static blobs — independent of proxy/CDN.

Prerequisites: Node origin. Works without Module 1.

bash
npm install @pondoknusa/storage-r2 @pondoknusa/storage

Register R2StorageServiceProvider and configure config/storage.ts. Full config: Storage guide.

bash
R2_BUCKET=pondoknusa
R2_ENDPOINT=https://<account_id>.r2.cloudflarestorage.com
R2_ACCESS_KEY_ID=...
R2_SECRET_ACCESS_KEY=...
R2_PUBLIC_URL=https://files.your-domain.example  # optional

Standalone: Storage only — no Cloudflare proxy required.


Module 4: Pages (static assets)

When: SPA front-end; Pondoknusa API or SSR on a subdomain.

HostServes
www.example.com (Pages)Vite client dist/
api.example.com (Node)Pondoknusa (headless or full stack)

Connect Pages to Git or wrangler pages deploy dist. Point api.example.com through Module 1 at the Pondoknusa origin. Set VITE_API_URL on the client build.

Subdomain split is simpler than single-hostname origin rules. Headless API origins skip view:cache.


Module 5: Tunnel (previews)

When: Staging, PR previews, local demos.

bash
# Quick (no account)
cloudflared tunnel --url http://127.0.0.1:3000

# Named tunnel
cloudflared tunnel create pondoknusa-staging
cloudflared tunnel route dns pondoknusa-staging staging.example.com
cloudflared tunnel run pondoknusa-staging

Set APP_URL to the tunnel hostname. Not a production origin replacement.


Module 6: WAF + security

When: Public API or SSR behind Module 1.

Prerequisites: Module 1.

  • WAF managed rulesets
  • Rate limiting on /api/* or login routes
  • Bot Fight Mode (test against legitimate API clients)
  • Disable Rocket Loader on WebSocket or strict CSP paths

Pair with Pondoknusa auth throttling and APP_DEBUG=false.


Module 7: D1 database

When: Use Cloudflare D1 as the Pondoknusa database from a Node origin (or inject a Workers binding in a custom adapter).

Prerequisites: Node origin for REST mode. Full Pondoknusa on Workers is still unsupported — the binding config is for wrappers / a future Workers adapter.

bash
npm install @pondoknusa/database-d1 @pondoknusa/database

@pondoknusa/database is a peer dependency — install it alongside the driver so DatabaseManager.extend('d1') shares one registry with core. Register D1DatabaseServiceProvider before DatabaseServiceProvider:

typescript
import { D1DatabaseServiceProvider } from '@pondoknusa/database-d1';

app.register(D1DatabaseServiceProvider);
app.register(DatabaseServiceProvider);

Remote (Node) config:

typescript
import type { D1ConnectionConfig } from '@pondoknusa/database-d1';

d1: {
  driver: 'd1',
  accountId: env('CLOUDFLARE_ACCOUNT_ID'),
  databaseId: env('D1_DATABASE_ID'),
  apiToken: env('CLOUDFLARE_API_TOKEN'),
} satisfies D1ConnectionConfig
bash
DB_CONNECTION=d1
CLOUDFLARE_ACCOUNT_ID=...
D1_DATABASE_ID=...
CLOUDFLARE_API_TOKEN=...  # D1:Edit

Workers binding (when you inject env.DB):

typescript
d1: {
  driver: 'd1',
  binding: env.DB,
} satisfies D1ConnectionConfig

D1 uses the SQLite dialect (SqliteGrammar). Interactive BEGIN/COMMIT may not be available on all D1 backends — prefer short single-statement writes when unsure.

Standalone: Database only — no Cloudflare proxy required.


Not supported on Workers (yet)

Pondoknusa requirementWorkers limitation
Node.js 26+ (node:sqlite, etc.)Subset compat
view:cache, framework diskNo persistent local disk
pondoknusa queue:workNo long-lived processes
pondoknusa start --clusterIsolates, not node:cluster
WebSocket hub + RedisCustom fan-out needed

Planned: headless JSON on Workers + Hyperdrive, then precompiled SSR. See Pondoknusa Cloud and ROADMAP.


Troubleshooting by module

ModuleSymptomFix
1Redirect loopSSL Full (strict); valid origin HTTPS
1Wrong client IPTRUST_PROXY=true
2Stale HTMLShorter max-age; bypass auth; ETag
2Session lostBypass Set-Cookie routes; SESSION_SECURE=true
3R2 403Token permissions; bucket CORS
4CORS errorsAPI allows Pages origin
5Tunnel 502PONDOKNUSA_HOST=0.0.0.0 on origin
6WS dropsBypass Rocket Loader
7D1 401 / authAPI token needs D1:Edit; check account + database IDs

Released under the MIT License.