Security Groups vs NACLs
Security groups are stateful firewalls around instances; network ACLs are stateless firewalls around subnets.
AWS gives you two layers of network firewall. Using both is defense in depth.
Security Groups
- Operate at the instance / ENI level.
- Stateful — return traffic is automatically allowed.
- Support allow rules only.
- All rules are evaluated together.
Network ACLs (NACLs)
- Operate at the subnet level.
- Stateless — you must allow both inbound and outbound explicitly.
- Support both allow and deny rules.
- Rules are evaluated in numbered order, lowest first.
| Security Group | NACL | |
|---|---|---|
| Applies to | Instance | Subnet |
| State | Stateful | Stateless |
| Rules | Allow only | Allow & deny |
Example
# Security group: allow HTTPS inbound
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp --port 443 --cidr 0.0.0.0/0
# NACL: explicitly DENY a bad IP range on the subnet
aws ec2 create-network-acl-entry \
--network-acl-id acl-0123456789abcdef0 \
--rule-number 90 --protocol -1 --rule-action deny \
--ingress --cidr-block 198.51.100.0/24When to use it
- A security group allows port 3306 only from the application server's security group ID, creating an automatic whitelist as new app instances launch.
- A network ACL explicitly denies traffic from a known malicious IP range at the subnet boundary before it ever reaches any instance.
- A compliance team uses NACLs as a coarse subnet-level block and security groups for fine-grained instance-level rules, applying both layers.
More examples
Security Group Allow App to DB
Configures a stateful security group rule that allows PostgreSQL traffic only from instances in the app security group.
# Allow only the app security group to reach the DB on port 5432
aws ec2 authorize-security-group-ingress \
--group-id sg-db-0abc123 \
--protocol tcp \
--port 5432 \
--source-group sg-app-0abc123Create Subnet-Level NACL Rule
Adds a stateless NACL deny rule at subnet level blocking all TCP traffic from a specific CIDR, evaluated before security groups.
# Deny inbound traffic from a blocked IP range at rule 100
aws ec2 create-network-acl-entry \
--network-acl-id acl-0abc123 \
--ingress \
--rule-number 100 \
--protocol tcp \
--port-range From=0,To=65535 \
--cidr-block 198.51.100.0/24 \
--rule-action denyCompare Effective Rules for Instance
Shows how to inspect both the security groups and NACL rules that apply to an EC2 instance for traffic troubleshooting.
# Check security groups on an instance
aws ec2 describe-instances \
--instance-ids i-0abc123 \
--query 'Reservations[].Instances[].SecurityGroups' \
--output table
# Check NACLs on the instance's subnet
aws ec2 describe-network-acls \
--filters Name=association.subnet-id,Values=subnet-0abc123 \
--query 'NetworkAcls[].Entries[].{Rule:RuleNumber,Proto:Protocol,Action:RuleAction}' \
--output table
Discussion