Stack B · 45 min · 0 EUR to start (free tier)

Stack B: Cloudflare Pages, Workers and D1 from zero to deployed

No server to patch, no systemd units, generous free tier, one command to deploy. Pages + Workers + D1 with wrangler, end to end.

Stack B: Cloudflare Pages, Workers, D1

Choose your stack covers when this beats a VPS: you don’t want to think about a server at all, and your traffic fits Cloudflare’s generous free tier. This guide goes from an empty folder to a deployed frontend + API + database.

1. Install wrangler and log in

npm install -g wrangler
wrangler login

This opens a browser to authorize the CLI against your Cloudflare account. No credit card needed to start.

2. Create the D1 database

wrangler d1 create my-app-db

This prints a database_id - copy it. D1 is Cloudflare’s SQLite-compatible database, replicated at the edge.

3. Configure the Worker (API)

Create wrangler.toml at the repo root:

name = "my-app-api"
main = "worker/index.ts"
compatibility_date = "2024-04-03"

[vars]
ENVIRONMENT = "production"

[[d1_databases]]
binding = "DB"
database_name = "my-app-db"
database_id = "PASTE-THE-ID-FROM-STEP-2"

[dev]
port = 8788

Secrets (API keys, OAuth credentials) never go in [vars] - they go through:

wrangler secret put DEEPSEEK_API_KEY

Run the migration to create your tables:

wrangler d1 execute my-app-db --remote --file=./migrations/001_init.sql

Deploy the Worker:

wrangler deploy

4. Configure Pages (frontend)

Create wrangler-pages.toml (or use the Pages dashboard directly):

name = "my-app-web"
compatibility_date = "2024-09-23"
compatibility_flags = ["nodejs_compat_v2"]

[vars]
PUBLIC_WORKER_URL = "https://my-app-api.<your-subdomain>.workers.dev"

[build]
command = "npm ci && npm run build"

Deploy:

wrangler pages deploy dist --project-name=my-app-web

Point your custom domain at the Pages project from the Cloudflare dashboard - TLS is handled automatically, no origin cert to manage (unlike Stack A, see domain, DNS, Cloudflare).

5. Local development

wrangler dev            # runs the Worker + local D1 on port 8788

Local D1 is a separate SQLite file under .wrangler/ - it does not touch production data. Run your migrations against it the same way, without --remote.

6. What you get for free, and what you don’t

Free tier covers: 100k Worker requests/day, 5GB D1 storage, unlimited Pages requests, automatic edge scaling, zero servers to patch or reboot.

You give up: shell access (no SSH, no tail -f on a log file - you read logs through wrangler tail or the dashboard), direct SQLite file access (D1 is an HTTP API to SQLite, not a local file), and the mental model of “one machine” - a Worker cold-starts on every edge location it runs from, which changes how you think about caching and state.

7. Debugging a live Worker

wrangler tail my-app-api      # stream live logs

There’s no journalctl here - wrangler tail is the closest equivalent, but it only shows what happens after you start it (nothing retroactive).

Verdict

If step 3 (wrangler.toml + secrets + D1 migration) felt more abstract than editing a text file over SSH, that’s the real tradeoff of Stack B: less to operate, less to see. Pick it when you want zero operational surface and your app fits the free tier’s shape. If you’d rather have one box you can poke at directly, stay on Stack A.

New models & deals, once a month

New proprietary and open-source models worth trying, price drops, and the best deals on AI coding plans. No spam.