---
title: 'Internet and Address Translation Gateways'
source: 'https://academia.sh/en/courses/compute-and-networking-in-the-cloud/internet-and-address-translation-gateways'
course: 'Compute and Networking in the Cloud'
language: en
updated: '2026-09-13T05:07:38+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Names the jobs the no-gateway layout blocks and works out the instance count able to exit in each layout'
    - "Measures the single gateway's two separate limits — the concurrent connection bottleneck and the failure radius — and counts how far a gateway per zone divides each"
    - "Sweeps the four layouts' periodic cost across eight periods and measures the cheapest layout's cost of reversal in external record count"
---

# Internet and Address Translation Gateways

A private subnet's instances' relationship with the outside is counted in four layouts: which five jobs stall by name in the no-gateway layout, how many instances' traffic a single gateway routes through one point and in which period it crosses the connection limit, how far a gateway per zone divides the failure radius and what it raises the cost to, and how many items and how many external records a public address per instance opens.

In the previous two lessons, the gateway appeared as both the fix and the bill: every outbound
flow left through it, every flow dropped onto the wrong path fell there, and every new zone asked
for one more. The gateway itself was never counted. Once every subnet was made private, the
externally reachable item count dropped to one — leaving the question in the opposite direction:
getting from inside to outside.

This question does not look like a security question, and it is one. An outbound path creates a
dependency even without opening an inbound one: the gateway's externally visible address gets
recorded by the other side, and those records make up the hardest item to reverse. Four layouts
are compared under the same workload.

- **CN19.** The four layouts, the work list, and the costs belong to the course's fictional
  regional measurement network. No real gateway is set up; what is measured is the model built
  with `node`.
- **CN20.** The gateway's concurrent connection limit is written by hand as 55,000, and peak-time
  outbound connections per instance as 90. In reality both vary by provider and workload; it shifts
  the saturation period without changing whether the bottleneck exists.
- **CN21.** Costs are in fictional units, not currency. The fixed component is written per
  gateway and per address, the flow component per hundred volume units.
- **CN22.** Three external services are assumed to write the egress address into their own
  records. In reality this number grows with the dependency count; it raises the reversal cost
  without changing its direction.
- **CN23.** In the single-gateway layout, three zones' traffic crosses the zone boundary and is
  billed separately for it.
- **CN24.** The gateway's own redundancy is counted as the provider's. "If the gateway drops"
  means a zone-level loss; it does not change how the failure radius is computed.

## The Model of Four Layouts

The model defines a layout with three numbers: how many gateways there are, whether every
instance has been given a public address, and which results follow from these. The `measure`
function returns six separate items at once — instances able to exit, externally reachable items,
stalled work, peak connections, failure radius, and external records. Cost splits into fixed and
flow, because the two grow at different rates with each period.

```js
// gateway.mjs — a private subnet's relationship with the outside; no real gateway is set up, the model is counted
export const INSTANCES = 616, ZONES = 4, PORTS = 4;   // the previous lesson's third layout: everything in private subnets
export const LIMIT = 55000;                     // gateway's concurrent connection limit (fictional, CN20)
export const PEAK_PER_INSTANCE = 90;             // peak-time outbound connections per instance (CN20)
export const VOLUME = 1960;                      // periodic external egress volume (from the previous lesson)
export const EXTERNAL_PARTIES = 3;               // external services that record the gateway's address (CN22)
// Costs are fictional units (CN21); written per 100 volume units, they carry a ratio, not a currency.
export const GATEWAY_FIXED = 40, ADDRESS_RENT = 0.5, EGRESS = 2, PROCESSING = 1, CROSS_ZONE = 1;

export const WORK = ['patching and package retrieval', 'calls to the external measurement service', 'time source',
  'certificate renewal', 'log shipping'];

// [name, gateway count, public address per instance?]
export const LAYOUT = [['no gateway', 0, false], ['single translation gateway', 1, false],
  ['gateway per zone', ZONES, false], ['public address per instance', 0, true]];

export const measure = ([name, gateways, addressed], instances = INSTANCES, volume = VOLUME) => {
  const addresses = addressed ? instances : 0;                       // one public address per instance
  const canExit = gateways > 0 || addresses > 0;
  const crossZone = gateways === 1 ? volume * (ZONES - 1) / ZONES : 0;   // a single gateway pulls three zones' traffic
  const flow = canExit ? (volume * EGRESS + (gateways ? volume * PROCESSING + crossZone * CROSS_ZONE : 0)) / 100 : 0;
  const fixed = gateways * GATEWAY_FIXED + addresses * ADDRESS_RENT;
  return { name, gateways, addresses, canExit: canExit ? instances : 0, item: addresses * PORTS,
    stalled: canExit ? 0 : WORK.length, peak: gateways ? Math.round(instances * PEAK_PER_INSTANCE / gateways) : 0,
    radius: gateways ? Math.round(instances / gateways) : 0, records: (gateways || addresses) * EXTERNAL_PARTIES,
    flow: Math.round(flow * 10) / 10, fixed, total: Math.round((flow + fixed) * 10) / 10 };
};
```

## The Same Workload, Four Layouts

```js
// run1.mjs — four layouts under the same workload: surface, bottleneck, failure radius, and cost
import { LAYOUT, WORK, LIMIT, INSTANCES, VOLUME, measure } from './gateway.mjs';

console.log(`${INSTANCES} private instances, periodic external volume ${VOLUME}, gateway concurrent connection limit ${LIMIT}`);
console.log(`${'layout'.padEnd(29)} ${'exits'.padStart(6)} ${'open item'.padStart(11)} ${'stalled'.padStart(9)} ` +
  `${'peak conn'.padStart(14)} ${'failure radius'.padStart(15)} ${'records'.padStart(10)} ${'total cost'.padStart(13)}`);
for (const d of LAYOUT) {
  const m = measure(d);
  const peak = m.gateways ? `${m.peak} (${Math.round(m.peak * 100 / LIMIT)}%)` : '-';
  console.log(`${m.name.padEnd(29)} ${String(m.canExit).padStart(6)} ${String(m.item).padStart(11)} ` +
    `${String(m.stalled).padStart(9)} ${peak.padStart(14)} ${String(m.radius ? `${m.radius} instances` : '-').padStart(15)} ` +
    `${String(m.records).padStart(10)} ${String(m.total).padStart(13)}`);
}
console.log(`\nstalled work in the no-gateway layout (${WORK.length} items): ${WORK.join(', ')}`);
const [single, perZone] = [measure(LAYOUT[1]), measure(LAYOUT[2])];
console.log(`cost breakdown  single gateway: fixed ${single.fixed} + flow ${single.flow}   ` +
  `per zone: fixed ${perZone.fixed} + flow ${perZone.flow}`);
console.log(`moving from single gateway to per zone: ${perZone.records - single.records} new external records, the same count on the way back`);
```

```
616 private instances, periodic external volume 1960, gateway concurrent connection limit 55000
layout                         exits   open item   stalled      peak conn  failure radius    records    total cost
no gateway                         0           0         5              -               -          0             0
single translation gateway       616           0         0   55440 (101%)   616 instances          3         113.5
gateway per zone                 616           0         0    13860 (25%)   154 instances         12         218.8
public address per instance      616        2464         0              -               -       1848         347.2

stalled work in the no-gateway layout (5 items): patching and package retrieval, calls to the external measurement service, time source, certificate renewal, log shipping
cost breakdown  single gateway: fixed 40 + flow 73.5   per zone: fixed 160 + flow 58.8
moving from single gateway to per zone: 9 new external records, the same count on the way back
```

The first row is full of zeros, and that does not make it good. The no-gateway layout has no open
item, no external record, no cost — and no instance able to exit either. The five items in the
stalled work column are written by name: patching and package retrieval, calls to the external
measurement service, time source, certificate renewal, log shipping. The first means machines stay
unpatched, the third means the clock drifts, the fourth means certificates expire. The zero-cost
layout is the layout that gives back every gain made at the machine level, a few periods later.

The single-gateway row carries three numbers at once. It has the lowest cost: 113.5 units, less than
half the per-zone gateway. The peak connection column shows the first limit — 55,440, 101% of the
limit. In this period the gateway fills, and what happens when it fills shows up not as a failure
but as slowdown: new connections queue. The failure radius is the second limit: if the zone the
gateway sits in is lost, all 616 instances become unable to exit, because the 462 instances in the
other three zones were also routing their traffic through it.

A gateway per zone fixes both at once and flips the bill. The fixed cost rises from 40 to 160, and
the flow cost drops from 73.5 to 58.8 — because no traffic crosses a zone boundary anymore. The
total rises from 113.5 to 218.8, roughly double. In exchange, peak fill drops from 101% to 25%,
and the failure radius from 616 instances to 154.

The 14.7 units difference in flow cost is small on its own, and where it comes from matters. A single
gateway pulls three of the four zones' traffic into its own zone; that traffic crosses the zone
boundary twice, and each crossing is billed separately. This item shows up in no route table,
shows up in no gateway count, and shows up only on the bill — and because it depends on which zone
the gateway is placed in, it is the result of a single-row decision.

The last row is the fourth layout, and it is a warning on its own. Giving every instance a public
address removes the gateway problem entirely: no bottleneck, no failure radius, the lowest flow
cost. In exchange, two surfaces open up. The first is 2,464 open items — the number the previous
lesson cut to one comes back, and more than comes back. The second is 1,848 external records:
every instance's address sits in three external services' records.

## Across Eight Periods

```js
// run2.mjs — across eight periods: when the single gateway fills, and when each layout turns expensive
import { LAYOUT, LIMIT, VOLUME, INSTANCES, measure } from './gateway.mjs';

// The previous lessons' growth series; period 8 is the value rounded per zone
const SERIES = [[1, 18], [2, 26], [3, 44], [4, 71], [5, 118], [6, 196], [7, 340], [8, 616]];
console.log(`${'period'.padEnd(7)} ${'instances'.padStart(10)} ${'single gw fill'.padStart(18)} ${'per zone'.padStart(12)} ` +
  `${'single gw'.padStart(10)} ${'per zone'.padStart(12)} ${'per instance'.padStart(13)} ${'open item'.padStart(11)}`);
for (const [d, n] of SERIES) {
  const h = Math.round(VOLUME * n / INSTANCES);
  const [single, perZone, perAddr] = [1, 2, 3].map((i) => measure(LAYOUT[i], n, h));
  const pct = (m) => `${Math.round(m.peak * 100 / LIMIT)}%`;
  console.log(`${String(d).padEnd(7)} ${String(n).padStart(10)} ` +
    `${`${single.peak} ${pct(single)}${single.peak > LIMIT ? ' over' : ''}`.padStart(18)} ${pct(perZone).padStart(12)} ` +
    `${String(single.total).padStart(10)} ${String(perZone.total).padStart(12)} ${String(perAddr.total).padStart(13)} ` +
    `${String(perAddr.item).padStart(11)}`);
}
const cheapest = SERIES.map(([d, n]) => {
  const h = Math.round(VOLUME * n / INSTANCES);
  const m = [1, 2, 3].map((i) => measure(LAYOUT[i], n, h));
  return [d, m.reduce((a, b) => (b.total < a.total ? b : a)).name];
});
const records = (n) => measure(LAYOUT[3], n).records;
console.log(`\nthe way back from the public-address-per-instance layout: ${records(71)} external records in period 4, ${records(616)} in period 8`);
console.log('period by period, the cheapest layout:');
for (const [name, periods] of Object.entries(cheapest.reduce((a, [d, name]) => ({ ...a, [name]: [...(a[name] || []), d] }), {})))
  console.log(`  ${name.padEnd(29)} period ${periods.join(', ')}`);
```

```
period   instances     single gw fill     per zone  single gw     per zone  per instance   open item
1               18            1620 3%           1%       42.1        161.7          10.1          72
2               26            2340 4%           1%       43.1        162.5          14.7         104
3               44            3960 7%           2%       45.3        164.2          24.8         176
4               71           6390 12%           3%       48.5        166.8            40         284
5              118          10620 19%           5%       54.1        171.3          66.5         472
6              196          17640 32%           8%       63.4        178.7         110.5         784
7              340          30600 56%          14%       80.6        192.5         191.6        1360
8              616    55440 101% over          25%      113.5        218.8         347.2        2464

the way back from the public-address-per-instance layout: 213 external records in period 4, 1848 in period 8
period by period, the cheapest layout:
  public address per instance   period 1, 2, 3, 4
  single translation gateway    period 5, 6, 7, 8
```

The sweep shows two curves. The gateway layouts' cost starts from a fixed base and grows slowly;
the address-per-instance layout's cost starts from zero and grows linearly with the instance
count. They cross in period five. For the first four periods, the cheapest layout is the layout
with the highest open item count, and in those periods the open item count runs between 72 and
284 — large against the gateway layouts' zero, but not large enough to trigger an alarm.

The single gateway column's fill value is the real warning. 56% in period seven, 101% in period
eight. Between the two periods the instance count grew 81%, and fill jumped 45 points; on a linear
curve, the surprise is not in the curve itself but in where the limit sits. The gateway-per-zone
stays at 25% in the same period, meaning it has three more periods of headroom. Moving from a
single gateway to per zone costs nine new external records, and that is work that cannot be done
at the moment it needs to be done — it depends on the external parties' calendar.

Fill crossing 100% does not produce a failure record of its own, and it does not show up where it
gets looked for either. A gateway pressed against its limit does not reject a connection, it holds
it; the side looking from inside sees this as a slowing external call. Three of the five jobs —
calls to the external measurement service, certificate renewal, and log shipping — start timing
out, and all three read as a failure of their own component. The number measured sits at the
gateway; the symptom sits in five other places.

The irreversible item in this lesson is not the gateway itself, but the trace the egress address
leaves outside. The gateway gets deleted, rebuilt, its count increased — but once an address is
written into three external services' records, those records are not in hand. This is exactly the
reversal cost of the address-per-instance layout, and it grows with the period: 213 records in
period four, 1,848 in period eight. The layout that is cheapest for the first four periods is the
layout most expensive to abandon in period five.

The item staying with the team is four pieces: gateway count, which zone the gateway sits in,
which instances get a public address, and which address the exit is seen from. Three items are
handed off — the gateway's concurrent connection limit, its own redundancy, and the translation
operation's latency. The first of the three handed off decides the entire table for period eight,
and it is asked nowhere.

## Summary

- The no-gateway layout produces 0 open items, 0 external records, and 0 cost; in exchange none of
  the 616 instances can exit, and 5 jobs stall by name: patching and package retrieval, calls to
  the external measurement service, time source, certificate renewal, log shipping.
- The single translation gateway is the cheapest gateway layout — 113.5 units — and it carries two
  limits at once: peak connections at 55,440, 101% of the limit, and a failure radius of 616
  instances. The 462 instances in three zones route their traffic through another zone.
- A gateway per zone drops fill to 25% and the failure radius to 154 instances. The fixed cost
  rises from 40 to 160, and the flow cost drops from 73.5 to 58.8 since no zone boundary is
  crossed; the total rises from 113.5 to 218.8, roughly double.
- A public address per instance zeroes out the bottleneck and the failure radius, and opens 2,464
  open items and 1,848 external records in exchange. This is the cheapest layout for the first 4
  periods; in period 5 the single gateway takes over.
- The irreversible item is the trace the egress address leaves outside. Moving from a single
  gateway to per zone costs 9 new external records; leaving the address-per-instance layout costs
  213 records in period 4 and 1,848 in period 8, and none of these are on our own calendar.

## Next Step

In all four of the four layouts, outbound traffic was counted, inbound connections were counted —
but nowhere was it written which connection is allowed. The open item column assumed every
instance listens on four ports, and how many of those ports actually need to stay open was never
asked. The next lesson's question writes the same policy in two separate mechanisms and runs it
against a known flow set: how many rules a filter that recognizes return traffic on its own needs,
how many rules the same policy needs from a filter that does not recognize it, which flows get
caught, which get away, and which get closed by mistake — and when the two mechanisms are used
together, which one is evaluated first, and in which case that order produces a result that cannot
be understood just by reading what is written.
