Skip to main content
Oct 12, 2026geo distributed load testing

By Lucas Yoris · Performate

Geo-Distributed Load Testing: DNS, Latency, and Realistic User Journeys

Run generators close to users—or emulate latency—and avoidDNS shortcuts that fake performance.

Your k6 runner sits in us-east-1, your users open the app in São Paulo and Madrid, and the dashboard shows green p95—until Black Friday exposes cross-region database hops and a CDN cache key that only warms in Virginia. Geo distributed load testing is how you prove latency and routing match customer reality instead of your CI network shortcut.

In this guide you will learn why single-region generators lie, how to tag k6 runs by region, when to deploy distributed agents vs emulate RTT, and which DNS choices belong in the scenario—not in assumptions.

Why geography changes performance—not just ping charts

Aggregating all virtual users through one regional generator hides problems that only appear on the wire path customers use:

  • GeoDNS steers names to different anycast edges; wrong resolver in the test means wrong POP.
  • TLS and HTTP/3 negotiation differs by distance; CPU on the client side is not the story—RTT amplifies tail latency on chatty APIs.
  • CDN cache hit ratio varies by region; a warm cache in Virginia does not help Chile on first byte.
  • Database followers may sit in one region while writes stay primary elsewhere—read-heavy journeys look fast only near the writer.
  • Compliance routing forces EU traffic through EU clusters; a US-only generator never exercises that path.

Think of performance as a map, not a single number. One dot on the map is a lab result; a contour plot is what your SLO should defend.

Pair regional tags with k6 observability metrics and tags so merged summaries still split http_req_duration by region:eu-west vs region:sa-east.

Practical k6 implementation: regional tags and parallel scenarios

You can run distributed k6 (k6 Cloud or self-hosted runners per distributed execution) or run one script with explicit region tags per scenario when each process executes in a different locale. Emulating RTT with Linux tc is a fallback—it approximates delay, not loss, jitter, or path MTU issues.

Example script (illustrative—not production-ready). Uses fictional hosts and thresholds. Adapt URLs, auth, and rates to your topology.

What this example demonstrates:

  • Parallel regional scenarios: journey_eu and journey_sa run at the same RPS split as analytics—not 100% from one POP.
  • Region tags: Every request carries region:eu or region:sa for percentile comparison in one summary export.
  • DNS realism: BASE_EU and BASE_SA env vars point at geo-steered hostnames your customers resolve—not a single global staging CNAME shortcut.
  • Journey shape: Multi-step flow (catalog + cart) exposes chatty APIs that RTT amplifies.
  • Thresholds per region: Separate p(99) gates when product SLOs differ by market.
import http from 'k6/http';
import { check, sleep } from 'k6';

const BASE_EU = __ENV.API_EU || 'https://api-eu.staging.example.com';
const BASE_SA = __ENV.API_SA || 'https://api-sa.staging.example.com';
const TOKEN = __ENV.API_TOKEN || 'replace-me';

export const options = {
  scenarios: {
    journey_eu: {
      executor: 'constant-arrival-rate',
      rate: Number(__ENV.EU_RPS || 20),
      timeUnit: '1s',
      duration: '8m',
      preAllocatedVUs: 50,
      maxVUs: 150,
      exec: 'journeyEu',
    },
    journey_sa: {
      executor: 'constant-arrival-rate',
      rate: Number(__ENV.SA_RPS || 12),
      timeUnit: '1s',
      duration: '8m',
      preAllocatedVUs: 40,
      maxVUs: 120,
      exec: 'journeySa',
    },
  },
  thresholds: {
    'http_req_duration{region:eu}': ['p(95)<600', 'p(99)<1200'],
    'http_req_duration{region:sa}': ['p(95)<900', 'p(99)<1800'],
    http_req_failed: ['rate<0.02'],
  },
};

function catalog(base, regionTag) {
  const res = http.get(`${base}/v1/catalog?page=1`, {
    headers: { Authorization: `Bearer ${TOKEN}` },
    tags: { name: 'catalog', region: regionTag },
  });
  check(res, { 'catalog 200': (r) => r.status === 200 });
  return res;
}

function addToCart(base, regionTag) {
  const res = http.post(
    `${base}/v1/cart/items`,
    JSON.stringify({ sku: 'SKU-100', qty: 1 }),
    {
      headers: {
        Authorization: `Bearer ${TOKEN}`,
        'Content-Type': 'application/json',
      },
      tags: { name: 'add_to_cart', region: regionTag },
    }
  );
  check(res, { 'cart 201': (r) => r.status === 201 });
}

export function journeyEu() {
  catalog(BASE_EU, 'eu');
  addToCart(BASE_EU, 'eu');
  sleep(0.5);
}

export function journeySa() {
  catalog(BASE_SA, 'sa');
  addToCart(BASE_SA, 'sa');
  sleep(0.8);
}

When you cannot deploy runners abroad yet, document that gap explicitly in the report—and pair with CDN cache headers load testing so at least edge behavior is not invisible.

Decision framework: distributed runners vs latency emulation

SituationRecommended action
Global product, SLOs per marketRunners in target regions; parallel scenarios with analytics-weighted RPS
Pre-prod budget, one staging stackEmulate RTT cautiously + tag region:*; label results as approximate
CDN or geoDNS change this sprintResolver and hostname per region in env; compare before/after with same tags
Mobile backends with regional shardsSeparate scenarios per shard URL; do not fold shards into one BASE
CI smoke onlySingle region low RPS; full geo matrix nightly or weekly

Use distributed runners when release gates depend on market-specific p99—not lab ping.

Use RTT emulation when you need directional signal before infra budget lands—never treat it as certification.

Use per-region hostnames when geoDNS is part of the product contract—internal staging.global shortcuts fake the journey.

Observability and pre-release checklist

Geo tests fail silently when tags and DNS setup drift. Before you scale traffic:

  • Document RPS split per region and the analytics source (gateway logs, mesh, or CDN).
  • Record which resolver and hostname each runner used—screenshot or env export in run notes.
  • Set alerts when p(99){region:sa} exceeds epsilon vs baseline while region:eu stays flat.
  • Correlate k6 region tags with APM geo or availability_zone dimensions during incidents.
  • Archive scenario JSON, regional env vars, and git SHA per release (load testing in CI/CD for smoke; full matrix scheduled).

How Performate coordinates multi-region scenario packs

Duplicating entire repos per locale does not scale. Concrete workflow for the EU + South America journey above:

  1. Import one collection with catalog and cart requests duplicated into folders EU and SA (different base URLs or host headers). Problem solved: one source of truth for journey steps.
  2. Create two scenarios—journey_eu at 20 req/s, journey_sa at 12 req/s—matching last week’s regional mix. Problem solved: honest weights without hand-editing executor blocks per tuning pass.
  3. Apply tags: region:eu and region:sa on each scenario plus shared route:checkout_path. Problem solved: reports match the k6 tag model in this article.
  4. Run both and open comparison view—filter by region and check whether SA p99 still meets the looser SLO while EU carries higher volume. Problem solved: one export for product and SRE.
  5. When marketing shifts traffic, change RPS in the editor and re-run—same requests, new mix. Problem solved: geo shifts are parameter changes, not repo forks.
  6. Export k6 for scheduled multi-region jobs on runners your infra team provides.

That maps to the cta: coordinate multi-region scenario packs with consistent tagging.

Closing takeaway

Geo distributed load testing is a routing and residency problem disguised as a latency chart. Run generators where users are—or be explicit when you emulate—and tag every request family by region so tail divergence blocks releases that look fine from one POP.

Run this week’s analytics-weighted regional mix before your next CDN or geoDNS change—and note which region still owns the p99 your SLO names.

Try Performate free | Book a demo | k6 scenarios

Ready to optimize your API performance?

Use Performate to coordinate multi-region scenario packs with consistent tagging.

← Back to all posts