1---
2title: VPC & networking
3description: The Network helper for a production-shaped VPC in one call, and the full set of EC2 networking primitives — VPCs, subnets, gateways, routes, security groups, and endpoints — for explicit control.
4---
5
6Most Alchemy apps on AWS never touch a VPC. Lambda functions
7serve public traffic from Function URLs, and bindings wire
8access to tables, buckets, and queues without a single subnet.
9Networking becomes your problem when you run an
10[ECS Service](/aws/compute/ecs), an [EKS Cluster](/aws/compute/eks), or an
11[EC2 Instance](/aws/compute/ec2) — all
12need a VPC, subnets, and security groups — or when you attach to
13anything that lives inside one.
14
15Alchemy gives you two altitudes: the **`Network` helper**, which
16builds the standard public/private layout in one call, and the
17**canonical primitives** underneath it, for when you need
18explicit control.
19
20## The `Network` helper
21
22[`Network`](/providers/aws/ec2/network) creates a
23production-shaped VPC from the low-level primitives — VPC,
24internet gateway, one public and one private subnet per
25Availability Zone (2 AZs by default), route tables, routes, and
26associations — and returns every underlying resource so you can
27keep composing with raw `AWS.EC2.*` APIs:
28
29```typescript
30const network = yield* AWS.EC2.Network("AppNetwork", {
31 cidrBlock: "10.42.0.0/16",
32});
33```
34
35Subnet CIDRs are derived from the VPC block automatically, and
36the outputs you'll reach for most — `vpcId`, `publicSubnetIds`,
37`privateSubnetIds` — sit right on the result.
38
39Turn on NAT and gateway endpoints when private subnets need a
40path out:
41
42```typescript
43const network = yield* AWS.EC2.Network("AppNetwork", {
44 cidrBlock: "10.42.0.0/16",
45 availabilityZones: 2,
46 nat: "single",
47 gatewayEndpoints: ["s3"],
48});
49
50yield* AWS.ECS.Service("ApiService", {
51 cluster,
52 task: {
53 taskDefinitionArn: apiTask.taskDefinitionArn,
54 containerName: apiTask.containerName,
55 port: apiTask.port,
56 },
57 vpcId: network.vpcId,
58 subnets: network.publicSubnetIds,
59 assignPublicIp: true,
60});
61```
62
63- `nat: "none"` (default) keeps private subnets isolated;
64 `"single"` shares one NAT gateway; `"per-az"` creates one per
65 Availability Zone for AZ-fault isolation.
66- `gatewayEndpoints` attaches free S3/DynamoDB gateway endpoints
67 to the private route tables, so that traffic never crosses the
68 NAT.
69
70If `Network`'s layout fits, use it and stop reading. The rest of
71this page is the same network built by hand.
72
73## Building it by hand
74
75### VPC and internet gateway
76
77A [`Vpc`](/providers/aws/ec2/vpc) is a private IPv4 range; an
78[`InternetGateway`](/providers/aws/ec2/internetgateway) is its
79door to the internet:
80
81```typescript
82const vpc = yield* AWS.EC2.Vpc("AppVpc", {
83 cidrBlock: "10.80.0.0/16",
84 enableDnsSupport: true,
85 enableDnsHostnames: true,
86});
87
88const igw = yield* AWS.EC2.InternetGateway("AppIgw", {
89 vpcId: vpc.vpcId,
90});
91```
92
93The two DNS toggles let instances resolve names and receive
94public DNS hostnames — you'll want both on for anything serving
95public traffic.
96
97### Subnets
98
99[`Subnet`](/providers/aws/ec2/subnet)s carve the VPC block into
100per-AZ ranges. `mapPublicIpOnLaunch` is what makes a subnet
101"public" from the instance's point of view:
102
103```typescript
104const subnetA = yield* AWS.EC2.Subnet("PublicSubnetA", {
105 vpcId: vpc.vpcId,
106 cidrBlock: "10.80.1.0/24",
107 availabilityZone: az1,
108 mapPublicIpOnLaunch: true,
109});
110
111const subnetB = yield* AWS.EC2.Subnet("PublicSubnetB", {
112 vpcId: vpc.vpcId,
113 cidrBlock: "10.80.2.0/24",
114 availabilityZone: az2,
115 mapPublicIpOnLaunch: true,
116});
117```
118
119Two subnets in two AZs is the floor for anything fronted by a
120load balancer.
121
122### Routing
123
124A subnet is only public once its
125[`RouteTable`](/providers/aws/ec2/routetable) sends `0.0.0.0/0`
126to the internet gateway. [`Route`](/providers/aws/ec2/route) adds
127the rule;
128[`RouteTableAssociation`](/providers/aws/ec2/routetableassociation)
129attaches the table to each subnet:
130
131```typescript
132const routeTable = yield* AWS.EC2.RouteTable("PublicRouteTable", {
133 vpcId: vpc.vpcId,
134});
135
136yield* AWS.EC2.Route("PublicInternetRoute", {
137 routeTableId: routeTable.routeTableId,
138 destinationCidrBlock: "0.0.0.0/0",
139 gatewayId: igw.internetGatewayId,
140});
141
142yield* AWS.EC2.RouteTableAssociation("PublicAssocA", {
143 routeTableId: routeTable.routeTableId,
144 subnetId: subnetA.subnetId,
145});
146yield* AWS.EC2.RouteTableAssociation("PublicAssocB", {
147 routeTableId: routeTable.routeTableId,
148 subnetId: subnetB.subnetId,
149});
150```
151
152### Security groups
153
154A [`SecurityGroup`](/providers/aws/ec2/securitygroup) is the
155per-resource firewall — the thing you attach to service ENIs,
156load balancers, and instances. Declare ingress and egress rules
157inline:
158
159```typescript
160const securityGroup = yield* AWS.EC2.SecurityGroup("AppSg", {
161 vpcId: vpc.vpcId,
162 description: "app traffic",
163 ingress: [
164 {
165 ipProtocol: "tcp",
166 fromPort: 80,
167 toPort: 80,
168 cidrIpv4: "0.0.0.0/0",
169 description: "HTTP ingress",
170 },
171 ],
172 egress: [
173 {
174 ipProtocol: "-1",
175 cidrIpv4: "0.0.0.0/0",
176 description: "all outbound",
177 },
178 ],
179});
180```
181
182Rules can also be managed as standalone
183[`SecurityGroupRule`](/providers/aws/ec2/securitygrouprule)
184resources when different parts of your stack contribute rules to
185one group.
186
187## Private egress with NAT
188
189Private subnets reach the internet through a
190[`NatGateway`](/providers/aws/ec2/natgateway) living in a
191*public* subnet, addressed by an [`EIP`](/providers/aws/ec2/eip),
192with the private route table pointing `0.0.0.0/0` at it:
193
194```typescript
195const privateRouteTable = yield* AWS.EC2.RouteTable("PrivateRouteTable", {
196 vpcId: vpc.vpcId,
197});
198
199const eip = yield* AWS.EC2.EIP("NatEip", { domain: "vpc" });
200
201const natGateway = yield* AWS.EC2.NatGateway("NatGateway", {
202 subnetId: subnetA.subnetId, // a public subnet
203 allocationId: eip.allocationId,
204 connectivityType: "public",
205});
206
207yield* AWS.EC2.Route("PrivateInternetRoute", {
208 routeTableId: privateRouteTable.routeTableId,
209 destinationCidrBlock: "0.0.0.0/0",
210 natGatewayId: natGateway.natGatewayId,
211});
212```
213
214This is exactly what `Network`'s `nat: "single"` option builds
215(`"per-az"` repeats it per Availability Zone).
216
217## Gateway endpoints
218
219S3 and DynamoDB traffic from private subnets doesn't need NAT at
220all — a Gateway [`VpcEndpoint`](/providers/aws/ec2/vpcendpoint)
221routes it over AWS's network for free:
222
223```typescript
224yield* AWS.EC2.VpcEndpoint("S3Endpoint", {
225 vpcId: vpc.vpcId,
226 serviceName: "com.amazonaws.us-east-1.s3", // match your region
227 vpcEndpointType: "Gateway",
228 routeTableIds: [privateRouteTable.routeTableId],
229});
230```
231
232`Network`'s `gatewayEndpoints: ["s3", "dynamodb"]` does the same
233against its private route tables.
234
235## The full toolkit
236
237| Resource | What it does |
238| --- | --- |
239| [`Vpc`](/providers/aws/ec2/vpc) | The private IPv4 network everything else lives in |
240| [`Subnet`](/providers/aws/ec2/subnet) | A per-AZ slice of the VPC's address range |
241| [`InternetGateway`](/providers/aws/ec2/internetgateway) | Two-way internet access for public subnets |
242| [`EgressOnlyInternetGateway`](/providers/aws/ec2/egressonlyinternetgateway) | Outbound-only IPv6 internet access |
243| [`NatGateway`](/providers/aws/ec2/natgateway) | Outbound internet for private subnets |
244| [`EIP`](/providers/aws/ec2/eip) | A static public IPv4 address |
245| [`RouteTable`](/providers/aws/ec2/routetable) / [`Route`](/providers/aws/ec2/route) / [`RouteTableAssociation`](/providers/aws/ec2/routetableassociation) | Where each subnet's traffic goes |
246| [`SecurityGroup`](/providers/aws/ec2/securitygroup) / [`SecurityGroupRule`](/providers/aws/ec2/securitygrouprule) | Stateful per-resource firewall |
247| [`VpcEndpoint`](/providers/aws/ec2/vpcendpoint) | Private connectivity to AWS services |
248| [`NetworkAcl`](/providers/aws/ec2/networkacl) / [`NetworkAclEntry`](/providers/aws/ec2/networkaclentry) / [`NetworkAclAssociation`](/providers/aws/ec2/networkaclassociation) | Stateless subnet-level packet filtering |
249
250## Where next
251
252- [ECS](/aws/compute/ecs) — run containers in the network you just
253 built, with an Alchemy-managed public ALB.
254- [EC2](/aws/compute/ec2) — launch instances into it, raw or hosting an
255 Effect program.
256- [`Network` reference](/providers/aws/ec2/network) — every prop
257 and everything the helper returns.