bunny.net — Edge¶
OWNER: karel
ALSO_USED_BY: stef (DNS/certs)
LAST_VERIFIED: 2026-03-26
GE_STACK_VERSION: bunnynet Terraform provider (BunnyWay/bunnynet) latest
Overview¶
bunny.net edge capabilities: Edge Scripting for compute@edge,
Edge Storage for globally replicated file storage, and Bunny DNS
for authoritative DNS with scripting. These complement the CDN
and security layers for GE client projects.
Edge Scripting¶
Edge Scripting runs JavaScript (Deno runtime) at bunny.net's 119+ PoPs.
Near-zero cold start. Supports WebAssembly (WASM) for Rust, C++, Go.
Use Cases in GE¶
| Use Case | Description |
|---|---|
| Request routing | A/B testing, canary deployments at edge |
| Auth pre-check | JWT validation before hitting origin |
| Geo-redirect | Redirect users based on country |
| Header injection | Add security/compliance headers |
| URL rewriting | Clean URL mapping, legacy redirects |
Creating an Edge Script¶
resource "bunnynet_compute_script" "router" {
name = "ge-${var.client}-router"
type = 0 # Standalone script
code = <<-JS
export default {
async fetch(request) {
const url = new URL(request.url);
const country = request.headers.get("cf-ipcountry") || "NL";
// EU-only access check
const euCountries = ["NL", "DE", "FR", "BE", ...];
if (!euCountries.includes(country)) {
return new Response("Access restricted to EU", { status: 403 });
}
return fetch(request);
}
}
JS
}
IF: logic is simple (header injection, redirect)
THEN: use bunny.net Edge Rules instead of Edge Scripting — lower cost
IF: logic requires computation (JWT validation, A/B routing)
THEN: use Edge Scripting
CHECK: edge scripts handle errors gracefully — never expose stack traces
CHECK: edge scripts have timeout handling — max execution 50ms
CHECK: edge scripts are deployed via Terraform, not dashboard
Edge Storage¶
Edge Storage provides globally replicated object storage.
Files are replicated across bunny.net's network for low-latency access.
No egress fees — ideal for static assets served via CDN.
Storage Zones¶
resource "bunnynet_storage_zone" "assets" {
name = "ge-${var.client}-assets"
region = "DE" # Primary region (EU)
zone_tier = 0 # Standard
replication_regions = ["NY", "LA", "SG"] # Optional global replication
}
IF: client is EU-only
THEN: set region to DE or NL, skip replication
IF: client has global audience
THEN: enable replication to relevant regions
CHECK: primary region is always EU (DE or NL)
CHECK: storage zone name follows ge-{client}-{purpose} pattern
Linking Storage to CDN¶
resource "bunnynet_pullzone" "storage_cdn" {
name = "ge-${var.client}-assets-cdn"
origin_url = "https://${bunnynet_storage_zone.assets.hostname}"
origin_type = 2 # Storage zone origin
}
File Operations¶
Upload via HTTP API or bunny.net SDK:
# Upload file
curl -X PUT "https://storage.bunnycdn.com/{zone}/{path}/{filename}" \
-H "AccessKey: ${STORAGE_API_KEY}" \
--data-binary @file.png
# Delete file
curl -X DELETE "https://storage.bunnycdn.com/{zone}/{path}/{filename}" \
-H "AccessKey: ${STORAGE_API_KEY}"
CHECK: storage API key comes from Vault
CHECK: file paths are deterministic (content-hash or semantic) — never random UUIDs
Bunny DNS¶
bunny.net provides authoritative DNS with scripting capabilities.
GE uses Bunny DNS for client domains routed through bunny.net CDN.
DNS Zone Configuration¶
resource "bunnynet_dns_zone" "client" {
domain = var.client_domain
nameserver1 = "kiki.bunny.net"
nameserver2 = "coco.bunny.net"
}
resource "bunnynet_dns_record" "app" {
zone_id = bunnynet_dns_zone.client.id
name = "app"
type = "CNAME"
value = "${bunnynet_pullzone.client_project.cname}"
ttl = 300
}
resource "bunnynet_dns_record" "root" {
zone_id = bunnynet_dns_zone.client.id
name = ""
type = "FLATTEN" # CNAME flattening for root domain
value = "${bunnynet_pullzone.client_project.cname}"
ttl = 300
}
CHECK: DNS records managed via Terraform
CHECK: root domain uses CNAME flattening (FLATTEN type)
CHECK: TTL is 300s for records that may change, 3600s for stable records
CHECK: stef (DNS/certs) coordinates DNS changes
IF: migrating a client domain to bunny.net DNS
THEN: coordinate with stef
THEN: verify all existing records are replicated before nameserver switch
THEN: lower TTLs 24 hours before migration
DNS Scripting¶
Bunny DNS supports programmable DNS responses (Smart DNS):
// Example: Geo-weighted DNS
export default {
async resolve(query) {
const geo = query.clientGeo;
if (geo.continent === "EU") {
return { records: [{ type: "A", value: "10.0.1.1" }] };
}
return { records: [{ type: "A", value: "10.0.2.1" }] };
}
}
IF: need geographic load balancing
THEN: use DNS scripting instead of external GSLB
CHECK: DNS scripts have fallback responses for errors
CHECK: DNS script latency stays under 5ms
Compute@Edge Patterns¶
Pattern: Auth Gate¶
Validate JWT at the edge before origin request:
export default {
async fetch(request) {
const token = request.headers.get("Authorization")?.replace("Bearer ", "");
if (!token) {
return new Response("Unauthorized", { status: 401 });
}
// Validate JWT (use imported crypto library)
// If valid, forward to origin
return fetch(request);
}
}
Pattern: Rate Limiting¶
// Use bunny.net KV store for distributed rate limiting
export default {
async fetch(request, env) {
const ip = request.headers.get("x-forwarded-for");
const key = `rate:${ip}`;
const count = await env.KV.get(key) || 0;
if (count > 100) {
return new Response("Rate limited", { status: 429 });
}
await env.KV.put(key, count + 1, { expirationTtl: 60 });
return fetch(request);
}
}
Cross-References¶
READ_ALSO: wiki/docs/stack/bunnynet/index.md
READ_ALSO: wiki/docs/stack/bunnynet/cdn.md
READ_ALSO: wiki/docs/stack/bunnynet/security.md
READ_ALSO: wiki/docs/stack/bunnynet/pitfalls.md
READ_ALSO: wiki/docs/stack/bunnynet/checklist.md