routed: egress from your route tables
OVERCAST_VPC_EGRESS=routed decides egress per subnet from the route table associated with it, so a missing NAT gateway fails locally the way it would deployed.
OVERCAST_VPC_EGRESS=routed decides egress per subnet, from the route table
associated with it. LocalStack, Moto and SAM CLI give a VPC-attached function
full egress and model no VPC networking at all, so this is where local emulation
can catch a missing NAT gateway before a deploy does.
The subnet’s 0.0.0.0/0 route | What its containers get |
|---|---|
| → an internet gateway attached to this VPC | A route out |
| → a NAT gateway that exists and is available | A route out |
| → a gateway that is detached, deleted or gone | Nothing — a blackhole on AWS, and here |
| → anything else (a virtual private gateway, a peering connection, an instance) | Nothing — none of those reaches the internet through anything Overcast runs |
| absent | Nothing. Outbound connections fail with ENETUNREACH |
A subnet with no explicit route-table association uses its VPC’s main table, exactly as on AWS. A container placed in several subnets gets a route out when any of them grants one: on AWS such a function reaches the internet from some of its ENIs and not others, which is not a state one container can be in, and granting is the reading that does not fail a working stack locally.
Resources outside a VPC are unaffected. They sit on the shared data plane,
which routed leaves routable, because that is what they get on AWS. So does
anything in a default VPC, whose subnets are public on AWS.
Important
Run Overcast in a container for this mode. On Docker Desktop with Overcast
running natively — and on any native Windows or macOS host — routed cannot
withhold egress: a subnet with no 0.0.0.0/0 route still reaches the
internet. Two warnings in the startup log and the
vpc-egress-not-withheld advisory on the console’s Metrics & Health page
say so, naming whichever mode you set. Running Overcast in a container, or
against a native Linux Docker daemon, is what makes the mode enforceable.
Two host limits cause it, and both the warnings and the advisory name whichever applies:
- The control plane stays routable, so containers keep a route out — the same
limit
nonehas. - VPC placement is not enforced where Overcast’s DNS resolver cannot start (no
/etc/resolv.confon those hosts), so a VPC-placed container also joins the routable shared data plane — see what VPC membership does not restrict.
How the route out is carried
The VPC’s plane stays one --internal bridge that every container in the VPC
joins, whatever its subnets route to: an isolated database and a NAT-routed
function in one VPC reach each other on AWS, and they could not if each egress
class were its own bridge. A container whose subnet grants egress also joins a
second, routable bridge, overcast-vpc-<vpc-id>-egress, and takes its default
route from there.
That shape is what makes a route-table change safe on a hot path:
- A container placed after the change gets the answer its route table gives now. Nothing to restart.
- A container already running is moved on or off the egress network in place,
by one
docker network connectordisconnect. Its plane, its address, its DNS names and its control-plane connection are untouched, so an in-flight invocation keeps its Runtime API. That is the AWS shape too: a route-table change reroutes an ENI in place.
CreateRoute, DeleteRoute, AssociateRouteTable, DisassociateRouteTable,
DeleteRouteTable, CreateNatGateway, DeleteNatGateway,
AttachInternetGateway and DetachInternetGateway each revisit every container
in the VPC, and each move is logged with the subnet and the route table that
decided it. A move Docker refuses does not fail the API call — AWS never refuses
a route for a reason like a daemon’s — but is logged at error, raised as an
advisory in GET /_overcast/debug/metrics (with OVERCAST_DEBUG=true), and
retried at the next start.
Note
routed isolates the control plane, as none does. A routable control plane
would hand every container a route out whatever its route table said — which is
what the measurements behind
#1571 found.
The address-pool ceiling
routed needs a second Docker network per VPC, and Docker’s own default address
pools stretch to about 31 networks in total on a stock daemon — shared with
every other tool on the machine. Doubling the per-VPC count against that ceiling
is how a run ends in all predefined address pools have been fully subnetted.
So the egress networks never draw on those pools. Each is pinned to a /24
carved from OVERCAST_VPC_EGRESS_POOL, which defaults to 198.18.0.0/16 — the
RFC 2544 benchmarking range, never routed on the internet, and untouched by both
Docker’s defaults and the remapped VPC strategy’s 100.64.0.0/10.
| VPCs with egress the default pool supports | 256 |
| Address per VPC | One /24, allocated once and kept on the VPC’s record, so a restart brings the network back at the same range |
| When one is created | On the first placement whose subnet grants a route out. A VPC whose subnets all route nowhere never gets one |
| When one is removed | With its VPC, and at startup when no VPC names it — including every one left behind by a routed run after you switch back to open or none |
Set a wider range if you need more:
OVERCAST_VPC_EGRESS_POOL=198.18.0.0/15 # 512 VPCs
It must be an IPv4 CIDR between /8 and /24, and is validated at startup in
every mode, so a pool written for a routed deployment is not found to be
malformed on the day you switch to it. Running out names the pool and how to
widen it, and fails the placement rather than quietly starting a container
without the egress its template grants.
Related
- Egress modes —
openandnone, and what the setting covers - The Docker networks Overcast uses — the bridges this mode adds
- Networking troubleshooting — when a container has the wrong answer
- Networking and host-based addressing — the rest of the addressing story