AWS EC2 and VPC Basics: Instances, Subnets and Security Groups
Cloud ComputingLesson 2 of 45 min read
What you'll learn
- Name the four conditions that let a browser reach an EC2 instance
- Describe a VPC with public and private subnets and an internet gateway
- Compare security groups with network ACLs
- Launch, stop and terminate an instance and know what each one costs
On this page
You launch a server, copy its public address into a browser, and the page just hangs. Four separate things have to be right for a request to reach a web server on AWS, and the browser gives the same symptom, a timeout, when any one of them is wrong. Knowing the four, and the order to check them, turns an hour of guessing into a two-minute checklist. This lesson uses a bike-rental shop's booking site as the example.
The four things between a browser and your instance
- The instance is running and the software on it is listening on the port (a web server on 80 or 443).
- The instance has a public IP address (or sits behind something that does).
- The subnet's route table sends internet-bound traffic to an internet gateway.
- The instance's security group allows the incoming port from the source you are connecting from.
Items 2 to 4 are networking, which on AWS lives in a Virtual Private Cloud (VPC).
A VPC in one picture
A VPC is your own private network inside AWS, defined by an address range written in CIDR notation. 10.0.0.0/16 means a block of about 65,000 private addresses. You carve it into subnets, each living in one availability zone (a data-centre group within a region), such as 10.0.1.0/24 for 256 addresses. A subnet is called public if its route table has a route to an internet gateway, and private if it does not.
Internet
|
[Internet gateway]
|
+-- VPC 10.0.0.0/16 ------------------------+
| | |
| Public subnet 10.0.1.0/24 (zone a) |
| route: 0.0.0.0/0 -> internet gateway |
| [ EC2: bike-site web server ] |
| | |
| Private subnet 10.0.2.0/24 (zone a) |
| route: local only (no internet) |
| [ database, workers ] |
+--------------------------------------------+Private subnets are where databases and background workers belong, because nothing on the internet can start a connection to them. If those machines need to download updates, a NAT gateway in the public subnet lets them make outbound connections only. NAT gateways are billed while they exist, which is a classic surprise on a learning account, so check current pricing and delete them when you are done.
Security groups versus network ACLs
| Security group | Network ACL | |
|---|---|---|
| Attached to | An instance's network interface | A whole subnet |
| Rule types | Allow rules only | Allow and deny rules |
| State | Stateful: a reply to allowed traffic is allowed automatically | Stateless: you must allow both directions |
| Typical use | Your main firewall, one per role (web, database) | A broad extra layer, usually left at defaults |
Security groups can refer to other security groups, which is the tidy way to say only the web servers may talk to the database: allow the database port from the web group, not from an IP range.
Launching an instance from the CLI
The console is fine for a first click-through, but the CLI shows what the console hides. These commands create a firewall group that allows HTTPS from anywhere, then start a small instance with a startup script. They use placeholder IDs and were written from the documented syntax; they were not run here because they need a real account and cost money. ami-... is an image ID, which differs by region, so look one up in your region.
# All IDs below are placeholders. Replace them with values from your own account.
aws ec2 create-security-group \
--group-name bike-web-sg \
--description "Web access for the bike shop site" \
--vpc-id vpc-0123456789abcdef0
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 443 --cidr 0.0.0.0/0
aws ec2 run-instances \
--image-id ami-0123456789abcdef0 \
--instance-type t3.micro \
--subnet-id subnet-0123456789abcdef0 \
--security-group-ids sg-0123456789abcdef0 \
--iam-instance-profile Name=bike-web-profile \
--user-data file://bootstrap.sh \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Project,Value=bike-shop}]'#!/bin/bash
# Runs once as root on first boot (Amazon Linux 2023 uses dnf).
dnf install -y nginx
systemctl enable --now nginx
echo "Bike shop booking site: placeholder page" > /usr/share/nginx/html/index.htmlNote what is missing: there is no rule for port 22. Opening SSH to the world invites constant login attempts. The instance profile named in the launch command can carry the managed policy AmazonSSMManagedInstanceCore, which lets you open a shell through AWS Systems Manager Session Manager without exposing any port. Session Manager needs a plugin installed for the CLI; look at its documentation for setup.
aws ec2 describe-instances \
--query 'Reservations[].Instances[].{Id:InstanceId,State:State.Name,Type:InstanceType}' \
--output tableOutput
----------------------------------------------- | DescribeInstances | +----------------------+----------+-----------+ | Id | State | Type | +----------------------+----------+-----------+ | i-0123456789abcdef0 | running | t3.micro | +----------------------+----------+-----------+
That output is illustrative: a placeholder instance ID in the shape the CLI prints. The --query option filters the JSON response with a JMESPath expression, a habit worth building because raw responses are long.
Instance types and the lifecycle
A name like t3.micro is a family (t, burstable general purpose), a generation (3) and a size (micro). Pick by what the workload is short of: general purpose for most web apps, compute-optimised for heavy CPU, memory-optimised for large in-memory data. Start small; resizing later is a stop, change and start.
| State | Billed for compute? | Notes |
|---|---|---|
| pending / running | Yes (on-demand, billed for the time it runs) | Public IPv4 addresses also carry a charge |
| stopped | No instance charge | Attached EBS volumes are still billed |
| terminated | No | Gone for good; root volume is deleted by default |
Common mistakes
- Debugging the wrong layer. Symptom:
Connection timed outusually means a packet was dropped (security group, route table, no public IP).Connection refusedmeans the packet arrived and nothing is listening, so check the service. - Opening SSH to `0.0.0.0/0`. Symptom: authentication logs full of
Failed passwordorInvalid userlines within hours. Use Session Manager or restrict the source to your own IP. - Launching in the wrong region. Symptom: the instance seems to vanish after you reopen the console. The region selector in the top bar filters everything.
- Losing the key pair. Symptom:
Permission denied (publickey). A private key cannot be re-downloaded; this is another reason to prefer Session Manager. - Forgetting a NAT gateway or volume. Symptom: a bill for a project you thought was off. Tag resources with a
Projectkey so you can find them all.
How this shows up in projects and interviews
Interview questions here are about the network model: what makes a subnet public, security group versus network ACL, stop versus terminate, and why the database belongs in a private subnet. A strong answer draws the picture above. On a team you will rarely hand-build a VPC; infrastructure code does it, but you will debug connectivity using exactly the four-item checklist. Pair this with System Design basics to see where load balancers fit in front of instances like these.
Try it yourself
Draw (on paper) a VPC with one public subnet for a web server and one private subnet for a database. For every arrow of traffic, name the security group rule that must allow it, using the web server's security group as the source for the database rule. Then write the order in which you would check a timeout from a browser to the web server.
Show a hint
The database rule should reference the web server's security group instead of a CIDR range. For the timeout order, follow the four-item list from this lesson.
Key takeaways
- A public subnet is one whose route table sends 0.0.0.0/0 to an internet gateway.
- Security groups are stateful allow-lists attached to instances; network ACLs are stateless and attached to subnets.
- Timeout and connection refused point at different layers; use that to debug quickly.
- Stopped instances still pay for disks, and terminated ones are gone, so clean up on purpose.
Test yourself
A trick from this lesson's ride: Public or private? Follow the route table, not the IP range. Four more tricks like it wait along the route, each followed by a question.
Frequently asked questions
What is the difference between a public and a private subnet in AWS?
A public subnet has a route to an internet gateway, so instances with public IPs there can be reached from and reach the internet. A private subnet has no such route; its instances can only reach the internet outbound through something like a NAT gateway. Databases and workers normally go in private subnets.
What is the difference between a security group and a network ACL?
A security group is a stateful firewall attached to an instance's network interface and contains only allow rules. A network ACL applies to a whole subnet, is stateless so both directions must be allowed, and supports allow and deny rules. Most setups rely on security groups and leave network ACLs at their defaults.
Does a stopped EC2 instance cost money?
A stopped instance stops incurring compute charges, but its attached EBS volumes continue to be billed, and some addresses or related resources may also cost money. A terminated instance is deleted and stops costing, though snapshots or volumes you kept still bill. Check current pricing for your region.