Skip to content

Repository files navigation

HelloWorld-Latency

Apples-to-apples latency test of the same API on AWS and Linode, exposed through increasing infrastructure layers (DNS → WAF → compute), measured from multiple regions.

Status: Built, deployed, and measured on AWS (US vantages). Scope reduced to AWS-only (Linode deferred — see Open / Deferred); the comparison is layer-for-layer on AWS (T0 → T1 → T2).

Results

Deploy / redeploy / teardown

Everything is reproducible from code (infra/aws/). No local Terraform needed — tf.sh falls back to Docker.

cd infra/aws
cp terraform.tfvars.example terraform.tfvars   # set your domain (see REGISTER.md to register one)
make all        # ECR repo -> build & push image -> EC2 + Route53 + ACM + CloudFront + WAF
make destroy    # tear it all down (keeps the registered domain)

Compute is a CPU instance (c7i.large) — the API is near-zero compute, so a GPU would add cost without changing the latency results. instance_type is a variable, so a different class can be set if ever needed. Measure: bench/run.sh (Dallas/local) and bench/globalping.py (US cities).

Goal

Measure the latency cost each layer adds, and compare AWS vs. Linode layer-for-layer, using managed services on both sides.

The App

GET /v1/personalization — TLS-only, stateless, no DB. Returns a ~4 KB bank-style personalization payload (made-up customer + dashboard config). Compute is near-zero so we measure infra + network, not app logic.

Payload: customer (name, segment, locale), greeting, ordered dashboard tiles, theme, feature flags, personalized offers, notifications, entitlements, experiments. Padded to a fixed byte length so both clouds return identical-length bodies. Emits Server-Timing + X-Tier/X-Served-By headers.

Complexity Tiers

Tier Path AWS Linode
T0 Direct Client → Compute Yes Yes
T1 + DNS Client → DNS → Compute Yes Yes
T2 + WAF Client → DNS → WAF (edge) → Compute Yes (CloudFront + AWS WAF) No (contract-only)

All tiers TLS-only; each tier = its own hostname (t{0,1,2}.{aws,linode}.<domain>) so we measure them independently. (No API Gateway — dropped by design.)

Comparison scope:

  • Cross-cloud apples-to-apples: T0 and T1 (exist on both).
  • WAF: AWS-only → a with-vs-without-WAF comparison (T1 vs T2) on AWS. Linode's WAF tier is a documented gap (see below).
  • L3/L4 firewall tier intentionally skipped: AWS Security Groups ≈ Linode Cloud Firewall (both free packet filters, ~0 latency) — an equivalent exists on both, but it's not a meaningful latency comparison.

Component Mapping (all managed)

Capability AWS Linode / Akamai
Compute CPU instance (c7i.large) (deferred — would match the same CPU class)
DNS Route 53 Linode DNS Manager
WAF (L7) AWS WAF on CloudFront (edge) Akamai App & API Protector — contract-only, no trial
L3/L4 firewall Security Group (free) Cloud Firewall (free) — skipped, see Tiers
TLS ACM (on CloudFront) Let's Encrypt

Why CloudFront on AWS: AWS WAF can't attach to a raw EC2 instance — it needs CloudFront, ALB, or API Gateway. We use CloudFront so AWS's WAF tier is edge-shaped — matching Akamai's edge WAF for when/if it's added later → fairest future comparison.

Firewall (L3/L4) vs. WAF (L7) — the key distinction

A Cloud Firewall / Security Group decides who can connect; a WAF inspects what they send.

Cloud Firewall / Security Group (L3/L4) WAF (L7 — AWS WAF / Akamai)
Sees source/dest IP, port, protocol full request: URL, headers, query, cookies, body
Inside TLS? No (blind to payload) Yes — terminates TLS, parses HTTP
Stops port scans, bad IPs, L3/L4 noise SQLi, XSS, RCE, bad bots, OWASP threats
Latency ~0 (packet ACL) real — TLS termination + inspection per request

Comparing AWS WAF (L7) to Linode Cloud Firewall (L3/L4) would not be apples-to-apples — the firewall does far less work and would look artificially faster. Fair pairings are L3/L4↔L3/L4 (SG ↔ Cloud Firewall) and L7↔L7 (AWS WAF ↔ Akamai).

Production note (Chase): A bank needs an L7 WAF (PCI-DSS, OWASP). AWS ships one managed + self-serve (AWS WAF + CloudFront/ALB). Linode-the-IaaS does not — you'd contract Akamai (App & API Protector) or self-host ModSecurity/Coraza + OWASP CRS. That structural gap is itself a key procurement finding.

Regions

  • AWS: us-east-1 (Ashburn). Linode: Washington DC (us-iad) — closest physical match.
  • Vantages: TBD after deployment (you're in Dallas). Decide self-hosted probes vs. Globalping vs. mix in Phase 5.

Protocol Defaults (bank-standard)

HTTP/2 · TLS 1.3 (min 1.2) · keep-alive on · gzip on · HSTS. Pinned identically across both clouds. The 4 KB is the uncompressed body; we also note on-wire size.

Methodology

  • Capture full waterfall per request: dns / tcp / tls / ttfb / download / total + app time from Server-Timing.
  • Interleave AWS & Linode from the same probe; discard warm-ups; ≥1000 samples per (tier × cloud × vantage).
  • Report p50/p90/p95/p99, not means. Separate cold vs. warm (edge/WAF cold paths).
  • Record exact instance specs, TLS/HTTP versions, keep-alive/compression state per run.

WAF Rules

Use each provider's default / free managed rule set (e.g. AWS Managed Rules baseline, Akamai default policy). Goal is to measure the layer's latency overhead — nothing custom.

Lifetime & Budget

Provision → ~1 day of testing → keep live 48 hdestroy to minimize cost. Keep Terraform/IaC so it can be redeployed later. Cost (CPU origin + AWS edge/WAF over a few days, low tens of USD) is acceptable; teardown keeps it bounded.

Note (only if you switch to a GPU class): AWS G-instance vCPU quota defaults to 0 and needs a Service Quotas increase (can take a day+). Not required for the CPU instance used here.

Stack

Go app (tiny, fast) → Docker (same image both clouds) → Terraform IaC → k6 + curl -w for measurement → Python/pandas for analysis.

Layout

app/            ~4KB personalization API + Dockerfile
infra/aws/      Terraform: EC2 (CPU), Route53, CloudFront + WAF, ACM
infra/linode/   Terraform/Ansible: instance, DNS, certs (deferred; WAF contract-only)
bench/          k6 harness, probe IaC, globalping scripts
results/        raw + aggregated data
analysis/       tables & charts

Phases

  1. Spec sign-off (this doc) → 1. App → 2. AWS infra → 3. Linode infra → 4. Harness → 5. Run & collect (pick vantages) → 6. Analysis → 7. Teardown + cost writeup.

Decisions Locked

  • Domain: buying a dedicated throwaway domain for SSL + per-tier subdomains.
  • WAF: AWS WAF on CloudFront. Akamai (Linode WAF) is contract-only, no trial → Linode WAF tier deferred; WAF comparison is with-vs-without on AWS for now.
  • L3/L4 firewall tier skipped (SG ≈ Cloud Firewall, ~0 latency).
  • Compute: CPU instance (c7i.large). GPU was originally specced for a matched cross-cloud class, but the API is near-zero compute and Linode (the reason for matching) was deferred — so a GPU would add cost with no effect on the latency results. instance_type stays configurable.
  • Protocol: bank-standard (HTTP/2, TLS 1.3, keep-alive, gzip, HSTS).
  • Lifetime: test ~1 day → live 48 h → destroy; keep IaC for redeploy. Cost acceptable.
  • VCS: local project = HelloWorld-Latency, on personal GitHub.

Open / Deferred

  1. Vantages (Phase 5) — self-hosted probes vs. Globalping vs. mix. Decide after deployment.
  2. Linode L7 WAF — revisit if/when an Akamai contract is in play; otherwise self-hosted ModSecurity/Coraza is the fallback to restore symmetry.

About

Apples-to-apples latency benchmark of the same API across AWS infra layers (direct → DNS → WAF/CloudFront), measured with cold/warm waterfalls.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages