Service Reference
EC2 — Elastic Compute Cloud
EC2 uses the AWS Query protocol (form-encoded POST, XML responses). Operations are identified by the Action parameter with API version 2016-11-15.
EC2 — Elastic Compute Cloud
AWS docs: https://docs.aws.amazon.com/AWSEC2/latest/APIReference/Welcome.html
EC2 uses the AWS Query protocol (form-encoded POST, XML responses). Operations are
identified by the Action parameter with API version 2016-11-15.
The default VPC
Each region seeds a default VPC the first time something reads VPCs or subnets
in it — IsDefault: true, 172.31.0.0/16, a default subnet per availability
zone, an attached internet gateway, a main route table with the default route,
and a default security group. That is the set DescribeVpcs --filters Name=isDefault,Values=true and CDK’s Vpc.fromLookup(isDefault: true) read.
Its backing network is not a per-VPC bridge: it is the shared data plane
(OVERCAST_NETWORK), which is where every container that named no VPC already
sits. “No VPC” and “the default VPC” are therefore the same place, by
construction rather than by coincidence.
Two consequences follow from that network being the emulator’s own:
DeleteVpcon the default VPC removes the record and leaves the network. AWS allows the delete, but the network has every container Overcast started attached to it.- Attaching or detaching an internet gateway on it is ignored with a warning. The toggle recreates the network, which would sever every attached container.
When Docker is available, each non-default VPC is backed by a real Docker bridge network.
The VPC’s CIDR block maps to the Docker subnet, and the network’s isolation mode
(--internal) reflects whether an internet gateway is attached. When Docker is
unavailable, VPC operations are metadata-only.
On startup, the EC2 service reconciles its stored VPC state against actual Docker networks — recreating missing networks, updating drifted IDs, and removing orphaned networks that no longer match a stored VPC. Docker network lifecycle events (create, destroy, connect, disconnect) are forwarded through the event bus.
Docker network conventions
| Label | Value | Purpose |
|---|---|---|
overcast.managed | true | Identifies Overcast-managed nets |
overcast.service | ec2 | Service that owns the network |
overcast.resource-id | VPC ID (e.g. vpc-abc) | Links network back to the VPC |
overcast.vpc-id | VPC ID | Additional VPC lookup convenience |
Network naming: overcast-vpc-{vpcID}.
Advanced: VPC networking strategies
Real AWS allows overlapping VPC CIDR blocks in the same account/region. Docker bridge networks on one host do not: overlapping subnets collide at the kernel routing table level. Overcast exposes a strategy switch so users can choose the behavior that best matches their workflow.
Configure with OVERCAST_EC2_VPC_STRATEGY:
| Strategy | Behavior | Best For |
|---|---|---|
shared (default) | Overlapping VPCs reuse a single Docker network; NetworkStatus=shared. | Most local-dev setups where convenience matters more than strict isolation. |
strict | Overlapping CreateVpc requests fail with InvalidVpc.Range; conflicting persisted VPCs are marked NetworkStatus=conflict. | Teams that want loud failures on accidental overlap. |
remapped | Overlapping VPCs get a unique Docker shadow subnet in 100.64.0.0/10; NetworkStatus=remapped and DockerCidrBlock records the shadow CIDR. | Multi-VPC simulations that need overlap without Docker subnet collisions. |
DescribeVpcs includes synthetic tag overcast:network-status=<value> and
/_overcast/debug/ec2/vpcs exposes internal fields (NetworkStatus,
DockerNetworkID, DockerCidrBlock) for diagnostics.
Important caveat for remapped: data-plane packet routing still follows
Docker’s real subnet assignment. API metadata keeps the user-requested
CidrBlock, but workloads that hardcode raw private IPs are less portable than
DNS-based SDK flows.
netns is reserved for future work and is currently rejected at startup with a
configuration error.
Limitations and divergences from AWS
The VPC emulation provides enough structure for CDK deployments and SDK-based workflows, but several aspects differ materially from real AWS networking:
Networking model
- No real IP routing between subnets. On AWS, subnets within a VPC can route to each other via the implicit local route. In Overcast, each VPC is a single flat Docker bridge network — all containers in the same VPC can reach each other, but there is no per-subnet isolation or inter-subnet routing. The CIDR blocks are recorded as metadata but do not partition Docker’s address space.
- No NAT gateway, VPN gateway, or transit gateway data plane. NAT gateways and VPN
gateways are emulated as metadata only (state and associations tracked, but no real NAT
or VPN routing). Only internet gateways affect the Docker network topology. Attaching an
IGW toggles the Docker network between
--internal(isolated) and normal bridge mode (host-routable). - Elastic IPs are metadata-only. EIPs can be allocated, associated, and released, but the synthetic IPs assigned are not routable. Containers receive Docker-assigned IPs only.
- VPC peering is metadata-only. The state machine (
pending-acceptance→active→deleted) is emulated, but no cross-network Docker routing is established. Containers in peered VPCs cannot actually communicate through the peering connection. - Route tables are metadata-only. Routes are stored and returned correctly in API
responses, but they do not affect Docker packet routing. The
CreateRoute,AssociateRouteTable, andDisassociateRouteTableoperations are recorded but have no effect on traffic. - CDK subnet lookup metadata. CloudFormation-created VPC resources preserve EC2 tags,
DescribeSubnetsreturns subnettagSet, andDescribeRouteTablesreturns NAT gateway routes so CDK can classify private subnet groups during VPC lookups. This metadata does not imply NAT data-plane routing.
Filters
Every Describe* refuses a filter name it does not implement, with AWS’s
InvalidParameterValue: The filter '<name>' is invalid. The error goes on to
name every filter that operation does support, and the same sets are in the
Notes column of the endpoint table below.
That is stricter than AWS in one direction: AWS refuses a name it does not
model, and Overcast additionally refuses a name AWS models but Overcast has not
implemented. It is deliberate. A filter that is accepted and then ignored
answers a question the emulator could not answer — a describe-vpcs filtered on
tag:Name that returns every VPC in the region reads as “your VPC exists” to a
find-or-create script, which then adopts the wrong one. An error costs a minute;
a wrong answer costs an afternoon. If you hit one, drop the filter or narrow the
call by resource ID.
A filter name is matched exactly, as AWS matches it — Name=VPC-ID is
refused, because real EC2 refuses it too.
A filter value is a pattern, as on AWS: * stands for any run of characters
including none, ? for exactly one, and a backslash escapes either so you can
ask for a literal one.
aws ec2 describe-vpcs --filters 'Name=tag:Name,Values=overcast-*'
aws ec2 describe-images --filters 'Name=name,Values=Amazon Linux 2*'
aws ec2 describe-subnets --filters 'Name=availability-zone,Values=us-east-1?'
Filters are AND-ed with each other and the values within one are OR-ed, as on
AWS, and a <Resource>Id.N parameter is AND-ed with them.
Security groups
- Security group rules are metadata-only. Ingress/egress rules are stored and returned
in
DescribeSecurityGroups, but they are not enforced at the Docker network level. All containers on the same Docker network can communicate freely regardless of security group rules. This matches the common local-dev use case where you want connectivity, not firewall testing.
Instances
- EC2 instances are metadata-only.
RunInstancescreates state records with asyncpending→runningtransitions, but no actual VMs or containers are launched. Instance metadata (IDs, state, security groups, subnet placement) is tracked for API compatibility with CDK and Terraform, but there is no compute behind it.
Lambda VPC integration
- Lambda containers are connected to the VPC’s Docker network (in addition to the
control plane) when a function has a
VpcConfig. This provides real connectivity between Lambda and other containers on the same VPC network (e.g. RDS, ECS tasks). However, subnet-level and security-group-level isolation is not enforced — a Lambda connected to one subnet can reach resources in any other subnet within the same VPC network.
General
- No DHCP option sets beyond a default stub response.
- No NACLs (Network ACLs). Only security groups are emulated (as metadata).
- No VPC Flow Logs.
- Docker dependency. All networking features degrade gracefully to metadata-only when Docker is not available. API responses remain correct; only actual container-level connectivity is lost.
Advanced: VPC networking strategies
TL;DR — most users can skip this section. The default works unless you’re intentionally creating VPCs with overlapping CIDRs.
The problem
In real AWS, every VPC is an isolated virtual network. Two VPCs in the
same account can legally share or overlap CIDRs (10.0.0.0/16 twice is
perfectly valid) — the only time overlap matters is when you try to
connect them via peering, Transit Gateway, or a VPN.
Overcast backs each VPC with a Docker bridge network so that real
containers (Lambda, ECS, RDS) launched into a VPC can actually talk to
each other. But every Docker bridge on a host shares a single kernel
routing table. The Linux networking stack flat-out refuses to have two
bridges claiming overlapping subnets — it returns
Pool overlaps with other one on this address space. That’s the
fundamental impedance mismatch: AWS’s VPC model assumes per-VPC
isolation, and Docker’s default bridge driver assumes host-global
uniqueness.
Overcast can’t make that go away. Instead it offers a strategy knob
so you can pick how the emulator should behave when the two models
disagree, set via the OVERCAST_EC2_VPC_STRATEGY environment variable.
Strategies
| Strategy | Status | Behaviour on overlapping CIDRs |
|---|---|---|
shared (default) | ✅ Implemented | VPCs with the same CIDR share a single Docker network. Container isolation between sharers is not enforced. |
strict | ⏳ Not yet — falls back to shared | Reject overlapping CIDRs at CreateVpc. Startup always tolerates pre-existing overlaps (first-one-wins, losers marked conflict). |
remapped | ⏳ Not yet — falls back to shared | Allocate a shadow /16 from 100.64.0.0/10 when the requested CIDR collides. API responses still show the user’s CIDR. |
netns | ⏳ Not yet — falls back to shared | Per-VPC Linux network namespace. Real overlap with real isolation. Requires root / CAP_NET_ADMIN. |
Values other than shared are accepted today but log a warning at
startup and fall back to shared. The design for each is captured below.
shared — the default
- What it does. For each distinct CIDR in your stored VPCs, Overcast
creates exactly one Docker bridge network. Additional VPCs requesting
the same CIDR reuse that network and are marked
NetworkStatus=shared. Reconcile on startup deterministically picks one owner per CIDR group (sorted byVpcID), adopts existing networks by label or IPAM subnet before creating anything new, and removes networks that no VPC references. - When it’s fine (the common case — single VPC, or multiple VPCs
with non-overlapping CIDRs):
sharedbehaves byte-identically tostrictbecause no collisions exist to share. You pay zero cost. - When to pick a different one: you’re running workloads that
actually test container-to-container isolation between VPCs that
share a CIDR. Under
shared, a container invpc-A(10.0.0.0/16) can reach a container invpc-B(10.0.0.0/16) because they’re on the same bridge. That’s wrong in real AWS, andshareddoesn’t pretend otherwise. If you care, wait forremappedornetns. - On
CreateVpcfailure modes. If Docker is unavailable the VPC is stored withNetworkStatus=unbackedand reconcile picks it up later. If Docker is available but the create fails, we log and still store the VPC — the API call succeeds, the network is best-effort. - On
DeleteVpc. The Docker network is only torn down when the VPC being deleted was the last one using it. Deleting a sharer leaves the owner’s network alone. - On IGW attach/detach. Toggling a VPC’s
--internalflag requires recreating the backing Docker network.sharedrefuses to do this when the network is shared (it would affect every sharer), logs aWarn, and leaves the existing network in place.
strict (planned)
- What it will do.
CreateVpcrejects any CIDR that overlaps an existing VPC withInvalidVpc.Range. Startup reconcile never fails — VPCs whose CIDR collides with another existing VPC are markedNetworkStatus=conflictand refused for container-backed operations (RunInstances,CreateDbInstance, etc.) with a clear emulator error. - When you’d use it. You want loud, early failure on accidental overlap — ideal for CI pipelines or tests where overlapping CIDRs signal a bug in your IaC, not an intended configuration.
- When not to use it. You’re running CDK apps or CloudFormation templates that legitimately create overlapping CIDRs (multi-account simulation, dev/prod parity tests). They’ll fail at deploy.
remapped (planned)
- What it will do. When a new VPC’s CIDR collides, Overcast
silently carves a shadow
/16out of100.64.0.0/10(CGNAT space), stores it asDockerCidrBlock, and creates the Docker network there.DescribeVpcsand every other API response still reports the user’sCidrBlock. A translation layer converts between fabricated and real IPs forPrivateIpAddressfields, ENI descriptions, etc. - When you’d use it. You’re running CDK or Terraform workloads where overlap is expected and you rely on API responses matching the CIDR you asked for. Highest fidelity.
- When not to use it. Your containers talk to each other by raw private IP (hardcoded in config files, not resolved via DNS). The fabricated IPs will not be reachable — only the shadow addresses are real. Workloads that use service discovery, ENI DNS, or RDS/ELB endpoint DNS are unaffected.
netns (planned, speculative)
- What it will do. Create containers with
--network=noneand move their veth into a per-VPC Linux network namespace with its own bridge and routing table. Each netns has an independent address space, so10.0.0.0/16invpc-Ais genuinely unrelated to the same CIDR invpc-B. - When you’d use it. You need real AWS-grade VPC isolation with real overlap support. The only option that’s faithful to both the AWS model and the network behaviour simultaneously.
- When not to use it. You’re not running overcastd as root
inside a container with
CAP_NET_ADMIN. The netns plumbing Docker doesn’t expose requires elevated privileges that most dev setups don’t grant. Also: it’s a substantially heavier code path than the other three, so the performance overhead is real.
Picking a strategy
| Situation | Use |
|---|---|
| Single VPC, or multiple non-overlapping CIDRs | shared (default) |
| CI that should fail loudly on accidental CIDR collisions | strict (today: fallback to shared) |
| CDK/TF apps with legitimate overlapping CIDRs that care about API-visible IPs | remapped (today: fallback to shared) |
| Testing real container-level VPC isolation with overlapping CIDRs | netns (today: fallback to shared) |
Why shared is the default
The overwhelmingly common Overcast workload is one VPC, or a handful of
VPCs with distinct CIDRs. In both cases shared never triggers the
sharing code path and is indistinguishable from a hypothetical
“perfectly isolated” implementation. Users who don’t hit the edge
case pay nothing. Users who do hit it get silent, working behavior
with a documented isolation compromise — instead of the alternative
(a noisy reconcile error every startup) which is what overcast did
before strategies existed.
Inspecting network state
Each VPC carries a NetworkStatus value that tells you what the
active strategy decided:
| Value | Meaning |
|---|---|
ok | This VPC owns its backing Docker network. |
shared | This VPC reuses a Docker network owned by another VPC (shared mode). |
unbacked | No Docker network (Docker was unavailable, or the last create failed). |
conflict | Reserved for strict mode — CIDR collided with another existing VPC. |
remapped | Reserved for remapped mode — backed by a shadow CIDR. |
NetworkStatus is persisted on each VPC record and written into the
startup reconcile logs (reconcile networks: …). Debug-endpoint and
web UI surfacing is planned alongside the future strategies — see
the strict, remapped, and netns sections above.
Summary
| Category | ✅ Supported |
|---|---|
| General | 69 |
| VPC network states | 3 |
Endpoints
General
| Operation | Status | Notes | AWS Docs |
|---|---|---|---|
AcceptVpcPeeringConnection | ✅ Supported | Transitions from pending-acceptance to active | docs |
AllocateAddress | ✅ Supported | Generates eipalloc- ID and synthetic public IP; Domain honoured; supports TagSpecification | docs |
AssociateAddress | ✅ Supported | Associates EIP with instance; generates eipassoc- ID | docs |
AssociateRouteTable | ✅ Supported | Associates route table with subnet | docs |
AttachInternetGateway | ✅ Supported | Toggles VPC Docker network from --internal to external (bridge) | docs |
AttachVpnGateway | ✅ Supported | Metadata-only VPC attachment | docs |
AuthorizeSecurityGroupEgress | ✅ Supported | docs | |
AuthorizeSecurityGroupIngress | ✅ Supported | IpPermissions with protocol, ports, CIDR ranges | docs |
CreateInternetGateway | ✅ Supported | Generates igw-xxx ID | docs |
CreateKeyPair | ✅ Supported | Generates dummy fingerprint and key material | docs |
CreateNatGateway | ✅ Supported | Requires subnet and EIP; supports TagSpecification | docs |
CreateNetworkInterface | ✅ Supported | Requires subnet; assigns synthetic private IP | docs |
CreateRoute | ✅ Supported | DestinationCidrBlock + GatewayId or NatGatewayId | docs |
CreateRouteTable | ✅ Supported | VPC must exist; auto-creates local route | docs |
CreateSecurityGroup | ✅ Supported | Default egress allow-all rule added on create | docs |
CreateSubnet | ✅ Supported | VPC must exist; honors AvailabilityZone; defaults to region+“a” | docs |
CreateTags | ✅ Supported | Tag any resource by ID; visible to that resource’s own describe | docs |
CreateVpc | ✅ Supported | CidrBlock required; creates Docker bridge network (--internal unless IGW attached) and main route table | docs |
CreateVpcEndpoint | ✅ Supported | Metadata-only; Gateway and Interface types accepted; state always “available” | docs |
CreateVpnGateway | ✅ Supported | Metadata-only; type ipsec.1 with AmazonSideAsn | docs |
CreateVpcPeeringConnection | ✅ Supported | Both VPCs must exist; starts in pending-acceptance state | docs |
DeleteInternetGateway | ✅ Supported | Must be detached first | docs |
DeleteKeyPair | ✅ Supported | Idempotent (no error if not found) | docs |
DeleteNatGateway | ✅ Supported | Marks as deleted | docs |
DeleteNetworkInterface | ✅ Supported | docs | |
DeleteRoute | ✅ Supported | Removes route by RouteTableId + DestinationCidrBlock | docs |
DeleteRouteTable | ✅ Supported | Cannot delete main route table | docs |
DeleteSecurityGroup | ✅ Supported | Fails with DependencyViolation while attached to a running/pending instance; the VPC’s default group answers CannotDelete | docs |
DeleteSubnet | ✅ Supported | Fails with DependencyViolation while ENIs, non-terminated instances, or NAT gateways remain in the subnet | docs |
DeleteTags | ✅ Supported | Remove tags by key; a resource’s tags are removed with the resource | docs |
DeleteVpc | ✅ Supported | Removes Docker network; fails with DependencyViolation while subnets, gateways, endpoints, peering connections, ENIs, or instances remain | docs |
DeleteVpcEndpoints | ✅ Supported | Accepts VpcEndpointId.N; silently skips unknown IDs | docs |
DeleteVpnGateway | ✅ Supported | Requires gateway to be detached | docs |
DeleteVpcPeeringConnection | ✅ Supported | From active or pending-acceptance; transitions to deleted | docs |
DescribeAccountAttributes | ✅ Supported | Hardcoded defaults (supported-platforms, max-instances…) | docs |
DescribeAddresses | ✅ Supported | Selects by AllocationId.N; returns tags; Filters: allocation-id, association-id, domain, instance-id, network-interface-id, private-ip-address, public-ip | docs |
DescribeAvailabilityZones | ✅ Supported | 3 AZs per region (a, b, c); Filters: region-name, state, zone-name | docs |
DescribeDhcpOptions | ✅ Supported | Returns a fabricated default DHCP options set; Filters: none | docs |
DescribeImages | ✅ Supported | Hardcoded set of 4 AMIs (AL2, Ubuntu, Windows, AL2023); selects by ImageId.N; Filters: architecture, description, image-id, image-type, is-public, name, owner-id, root-device-type, state, virtualization-type | docs |
DescribeInstanceTypes | ✅ Supported | Hardcoded set: t3.micro/small/medium, m5.large/xlarge; Filters: current-generation, instance-type, memory-info.size-in-mib, vcpu-info.default-vcpus | docs |
DescribeInstances | ✅ Supported | Selects by InstanceId.N; Filters: availability-zone, image-id, instance-id, instance-state-code, instance-state-name, instance-type, placement.availability-zone, subnet-id, vpc-id, tag: | docs |
DescribeInternetGateways | ✅ Supported | Selects by InternetGatewayId.N; Filters: attachment.state, attachment.vpc-id, internet-gateway-id, tag: | docs |
DescribeKeyPairs | ✅ Supported | Selects by KeyName.N; Filters: fingerprint, key-name, key-pair-id | docs |
DescribeNatGateways | ✅ Supported | Selects by NatGatewayId.N; Filters: nat-gateway-id, state, subnet-id, vpc-id, tag: | docs |
DescribeNetworkInterfaces | ✅ Supported | Selects by NetworkInterfaceId.N; Filters: availability-zone, description, mac-address, network-interface-id, status, subnet-id, vpc-id, tag: | docs |
DescribeRegions | ✅ Supported | Hardcoded list of 8 regions; Filters: endpoint, opt-in-status, region-name | docs |
DescribeRouteTables | ✅ Supported | Selects by RouteTableId.N; includes NAT gateway routes; Filters: association.main, association.route-table-association-id, association.subnet-id, route-table-id, vpc-id, tag: | docs |
DescribeSecurityGroups | ✅ Supported | Selects by GroupId.N; Filters: description, group-id, group-name, vpc-id, tag: | docs |
DescribeSubnets | ✅ Supported | Selects by SubnetId.N; includes tagSet for CDK subnet groups; Filters: availability-zone, cidr-block, state, subnet-id, vpc-id, tag: | docs |
DescribeTags | ✅ Supported | Filters: key, resource-id, resource-type, value | docs |
DescribeVpcAttribute | ✅ Supported | Returns the stored enableDnsSupport or enableDnsHostnames value | docs |
DescribeVpcEndpoints | ✅ Supported | Selects by VpcEndpointId.N; Filters: service-name, vpc-endpoint-id, vpc-endpoint-state, vpc-endpoint-type, vpc-id | docs |
DescribeVpnGateways | ✅ Supported | Selects by VpnGatewayId.N; Filters: amazon-side-asn, attachment.state, attachment.vpc-id, availability-zone, state, type, vpn-gateway-id, tag: | docs |
DescribeVpcPeeringConnections | ✅ Supported | Selects by VpcPeeringConnectionId.N; Filters: accepter-vpc-info.vpc-id, requester-vpc-info.vpc-id, status-code, vpc-peering-connection-id | docs |
DescribeVpcs | ✅ Supported | Selects by VpcId.N; Filters: cidr, isDefault, state, vpc-id, tag: | docs |
DetachInternetGateway | ✅ Supported | Toggles VPC Docker network back to --internal | docs |
DetachVpnGateway | ✅ Supported | Metadata-only VPC detachment | docs |
DisassociateAddress | ✅ Supported | By AssociationId | docs |
DisassociateRouteTable | ✅ Supported | Cannot disassociate main association | docs |
ModifyInstanceAttribute | ✅ Supported | InstanceType.Value persisted; all other attributes accepted | docs |
ModifySubnetAttribute | ✅ Supported | MapPublicIpOnLaunch is persisted and returned by DescribeSubnets | docs |
ModifyVpcAttribute | ✅ Supported | EnableDnsSupport, EnableDnsHostnames are persisted and returned by DescribeVpcAttribute | docs |
ReleaseAddress | ✅ Supported | By AllocationId | docs |
RevokeSecurityGroupEgress | ✅ Supported | docs | |
RevokeSecurityGroupIngress | ✅ Supported | docs | |
RunInstances | ✅ Supported | MinCount/MaxCount, TagSpecifications, async pending→running; each state emits an EC2 Instance State-change Notification to the default EventBridge bus | docs |
StartInstances | ✅ Supported | From stopped state only; each state emits an EC2 Instance State-change Notification to the default EventBridge bus | docs |
StopInstances | ✅ Supported | From running state only; async stopping→stopped; each state emits an EC2 Instance State-change Notification to the default EventBridge bus | docs |
TerminateInstances | ✅ Supported | Async shutting-down→terminated transition; each state emits an EC2 Instance State-change Notification to the default EventBridge bus | docs |
VPC network states
| Operation | Status | Notes | AWS Docs |
|---|---|---|---|
unbacked | ✅ Supported | No Docker network (Docker unavailable, or the last create failed) | docs |
conflict | ✅ Supported | Reserved for strict mode when CIDR collides with another existing VPC | docs |
remapped | ✅ Supported | Reserved for remapped mode and backed by a shadow CIDR | docs |