Deployment Environments
Deployment Environments
The Bloqr Compiler uses a three-environment model: local, dev, and production. Both the
API Worker (wrangler.toml) and the Angular frontend Worker (frontend/wrangler.toml) follow
the same model.
Overview
flowchart LR
LOCAL["Local\n─────────────────\nwrangler dev :8787\nng serve :4200\nSecrets from .dev.vars\nNo Cloudflare deployment"]
DEV["Dev\n─────────────────\nwrangler deploy --env dev\n*.workers.dev subdomain\nENVIRONMENT=development\nAngular DevTools enabled"]
PROD["Production\n─────────────────\nwrangler deploy\nCustom domain via [[routes]]\nENVIRONMENT=production\nlogpush=true"]
LOCAL -->|"ready to preview on workers.dev"| DEV
DEV -->|"validated, ready to ship"| PROD
| Local | Dev | Production | |
|---|---|---|---|
| Deploy command (API) | deno task wrangler:dev | deno task wrangler:deploy:dev | deno task wrangler:deploy |
| Deploy command (frontend) | pnpm --filter bloqr-frontend run start | deno task ui:deploy:ng:dev | sh scripts/deploy-frontend.sh |
| URL | http://localhost:8787 | https://<worker-name>.workers.dev | custom domain via [[routes]] |
| Angular build | dev server (HMR) | ng build --configuration development | ng build (production config) |
| Angular DevTools | ✅ (dev server always unoptimised) | ✅ (sourceMap, no optimisation) | ❌ (minified, tree-shaken) |
| logpush | N/A (not deployed) | false | true |
| ENVIRONMENT var | set in .dev.vars | "development" | "production" |
Local Development
No Cloudflare deployment is involved. Both workers run locally.
# API Worker → http://localhost:8787deno task wrangler:dev
# Angular dev server → http://localhost:4200 (hot module reload)pnpm --filter bloqr-frontend run start
# Angular Workers preview → http://localhost:4200 (mirrors production SSR)pnpm --filter bloqr-frontend run previewSecrets and local URL overrides live in .dev.vars (gitignored — copy from .dev.vars.example).
Dev Environment
Deploys to the auto-generated *.workers.dev subdomain without touching the production
custom domain. Use this environment to:
- Preview changes live on Cloudflare infrastructure
- Debug with Angular DevTools (source maps enabled, no minification)
- Test before merging to production
Deploy the API Worker to dev
deno task wrangler:deploy:dev# equivalent: deno run -A npm:wrangler deploy --env devDeploy the Angular frontend to dev
deno task ui:deploy:ng:dev# equivalent: pnpm --filter bloqr-frontend run build:dev# + pnpm --filter bloqr-frontend run deploy:devOr step by step:
# Build with development configuration (sourceMap=true, optimization=false)pnpm --filter bloqr-frontend run build:dev # ng build --configuration development
# Deploy to *.workers.dev (--env dev skips the [[routes]] custom domain block)pnpm --filter bloqr-frontend run deploy:dev # wrangler deploy --env devWhat [env.dev] does
# wrangler.toml (API Worker) — and frontend/wrangler.toml mirrors this[env.dev]logpush = false # disable Logpush for the dev subdomain
[env.dev.vars]ENVIRONMENT = "development"- Routes are NOT inherited — the dev environment is accessible only via the
*.workers.devsubdomain. The[[routes]]custom domain block in the top-level config is not applied. - Bindings ARE inherited — KV namespaces, D1, R2, Queues, and service bindings
are all shared with production by default. Create separate dev resources and
override them inside
[env.dev]when isolation is needed. - Secrets are shared —
wrangler secret putapplies to all environments. To set an env-specific secret, usewrangler secret put <KEY> --env dev.
Production Environment
The top-level wrangler.toml / frontend/wrangler.toml IS the production configuration.
No [env.production] block is needed.
# API Workerdeno task wrangler:deploy
# Angular frontend (preferred — builds, injects CF Analytics token, deploys)sh scripts/deploy-frontend.shProduction deploys to the custom domains declared in the [[routes]] block of each
wrangler.toml. See URL Management for how to change
these URLs.
URL Configuration
All public-facing URLs are managed as [vars] entries and kept in sync across both
wrangler.toml files. Replace the placeholder values below with your real domains:
| Variable | Purpose | Example value |
|---|---|---|
URL_FRONTEND | Angular frontend worker | https://app.<your-domain> |
URL_API | API / backend worker | https://api.<your-domain> |
URL_DOCS | Documentation site | https://docs.<your-domain> |
URL_LANDING | Marketing landing page | https://<your-domain> |
CANONICAL_DOMAIN | Domain used for crawl-protection noindex logic | <your-domain> |
CANONICAL_DOMAIN controls the X-Robots-Tag: noindex, nofollow header. Any request
arriving at a hostname that is neither <CANONICAL_DOMAIN> nor a subdomain of it will
receive the noindex header. This prevents *.workers.dev and other temporary hostnames
from being indexed by search engines while a custom domain is active.
Important:
CANONICAL_DOMAINmust be the full domain you use — not just the registrable root. Forapi.bloqr.dev, setCANONICAL_DOMAIN = "bloqr.dev"so that all*.bloqr.devsubdomains are treated as canonical. Setting it todevwould match every.devTLD hostname, which is wrong.
To swap all URLs at once, run:
# Interactive — prompts for root domain and (optionally) canonical domaindeno task domain:swap
# Non-interactive examplesdeno task domain:swap -- --domain bloqr.devdeno task domain:swap -- --domain bloqr.dev --canonical bloqr.dev
# Preview changes without writing any filesdeno task domain:swap -- --domain bloqr.dev --dry-runThe script updates both [vars] and [env.dev.vars] in each wrangler.toml, so
both environments stay in sync after a single run.
For detailed URL change steps, see URL Management.
The [env.dev.vars] block intentionally repeats these values. The dev environment
runs on *.workers.dev, but the app still references canonical production URLs (e.g.
for OpenGraph meta tags and API base URLs). Override them if you also have a dev
custom domain.
TOML Scoping Rules
Two Wrangler-specific TOML gotchas apply to both wrangler.toml files:
1. Array-of-tables must come AFTER all bare top-level keys
In TOML, once you open an array-of-tables header ([[routes]], [[rules]], etc.), every
bare key = value that follows — until the next [table] or [[table]] header — is parsed
as a field of that array entry, not as a top-level key. This means if main, no_bundle,
compatibility_date, etc. appear after [[routes]], they become route fields and Wrangler
rejects the config.
Correct structure:
name = "my-worker"main = "worker.ts"workers_dev = truelogpush = true# ... all other bare top-level keys ...
[[routes]] # array-of-tables only AFTER all bare keyspattern = "api.<your-domain>"custom_domain = true
[[rules]] # same rule applies to [[rules]]type = "ESModule"globs = ["**/*.mjs"]
[vars] # standard tables can followENVIRONMENT = "production"2. Custom domain routes must be bare hostnames
For custom_domain = true route entries, Cloudflare only accepts a bare hostname — no
paths or wildcards:
# ✅ Correct[[routes]]pattern = "api.<your-domain>"custom_domain = true
# ❌ Wrong — path/wildcard not allowed with custom_domain = true[[routes]]pattern = "api.<your-domain>/*"custom_domain = trueFurther Reading
- Cloudflare Workers Architecture — two-worker split, bundled vs independent SSR modes, CI/CD deploy flow
- URL Management — single source of truth for service URLs
- Environment Configuration — two-track env system
(shell vs Wrangler),
.dev.vars, secrets management - Wrangler Environments docs
Bloqr AI™ — The privacy you didn't know you needed.
© 2026 Bloqr AI™, a trademark of Bloqr Systems™. Created by Bloqr Systems™, founded by Jayson Knight.
Internet Hygiene (n.) — the ongoing practices that keep your digital life clean, private, and safe.
Our product repos live in the BloqrAI org, part of the Bloqr Systems GitHub Enterprise. Product repos are internal-visibility — enterprise membership is required to view them.