Create security groups
Three chained firewalls so only the load balancer reaches the API and only the API reaches the database.
About 15 min · Verified 8 October 2026
A security group is a stateful firewall attached to a resource. The trick in this design is that each group only accepts traffic from the previous group, not from an IP range. The internet can reach the load balancer; only the load balancer can reach the API; only the API can reach the database.
Internet ──:80──► shortlink-alb-sg ──:3000──► shortlink-api-sg ──:5432──► shortlink-db-sgCreate them in this order, because each rule refers to the group before it, and the group has to exist before you can pick it.
Create the three groups#
Load balancer group: shortlink-alb-sg#
Open VPCSecurity groupsCreate security group (or EC2Security Groups).
| Field | Value |
|---|---|
| Security group name | shortlink-alb-sg |
| Description | Public HTTP to the load balancer |
| VPC | shortlink-vpc (not the default VPC) |
Inbound rules → Add rule:
| Type | Protocol | Port | Source |
|---|---|---|---|
| HTTP | TCP | 80 | Anywhere-IPv4 (0.0.0.0/0) |
Leave Outbound rules at the default (all traffic). Choose Create security group.
API group: shortlink-api-sg#
Create another group in shortlink-vpc:
| Field | Value |
|---|---|
| Security group name | shortlink-api-sg |
| Description | API server, reachable only from the load balancer |
| VPC | shortlink-vpc |
Inbound rules → Add rule:
| Type | Protocol | Port | Source |
|---|---|---|---|
| Custom TCP | TCP | 3000 | Custom → start typing shortlink-alb-sg and pick the group |
Leave outbound at the default. The instance needs outbound access for apt, npm, Systems Manager and the database.
Database group: shortlink-db-sg#
| Field | Value |
|---|---|
| Security group name | shortlink-db-sg |
| Description | PostgreSQL, reachable only from the API |
| VPC | shortlink-vpc |
Inbound rules → Add rule:
| Type | Protocol | Port | Source |
|---|---|---|---|
| PostgreSQL | TCP | 5432 | Custom → shortlink-api-sg |
Outbound can stay at the default.
Verify the rules#
aws ec2 describe-security-groups --region ap-south-1 \
--filters Name=group-name,Values='shortlink-*' \
--query 'SecurityGroups[].{name:GroupName,id:GroupId,inbound:IpPermissions[].{port:FromPort,cidr:IpRanges[0].CidrIp,fromGroup:UserIdGroupPairs[0].GroupId}}' \
--output jsonCheck three things:
shortlink-alb-sg: port80, source CIDR0.0.0.0/0.shortlink-api-sg: port3000,fromGroupis the ID ofshortlink-alb-sg, and there is no CIDR.shortlink-db-sg: port5432,fromGroupis the ID ofshortlink-api-sg.
Why is a rule showing an ID like sg-0abc… instead of a name?
Rules that refer to another group always store its ID. The console shows the ID with the name next to it. Compare the IDs with the output of the command above.
Next: create the database.
Found a mistake? Edit this page on GitHub.