Quick Answer
resource "aws_security_group_rule" "allow_ping" {
type = "ingress"
from_port = 8 # ICMP type 8 = Echo Request
to_port = 0 # ICMP code 0
protocol = "icmp"
cidr_blocks = ["10.0.0.0/16"]
security_group_id = aws_security_group.web.id
}
This allows ping (ICMP Echo Request) from 10.0.0.0/16 to any instance using the web security group.
Why Ping Doesn’t Work by Default
AWS security groups block all inbound traffic by default. ICMP (ping) is no exception. You must explicitly allow it.
Common scenario: you launch an EC2 instance, SSH works, but ping times out:
$ ping 10.0.1.50
PING 10.0.1.50 (10.0.1.50): 56 data bytes
# ...hangs forever
Fix: add an ICMP ingress rule to the security group.
Complete Security Group with Ping
resource "aws_security_group" "web" {
name = "web-server-sg"
description = "Allow SSH, HTTP, and ICMP"
vpc_id = aws_vpc.main.id
# SSH
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = [var.my_ip]
}
# HTTP
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# ICMP Ping
ingress {
from_port = 8 # ICMP type 8 (Echo Request)
to_port = 0 # ICMP code 0
protocol = "icmp"
cidr_blocks = ["10.0.0.0/16"]
}
# All outbound
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-server-sg"
}
}
Understanding ICMP Types and Codes
ICMP doesn’t use ports like TCP/UDP. Instead, it uses types and codes. In Terraform’s aws_security_group_rule:
from_port= ICMP typeto_port= ICMP code
| ICMP Type | Code | Name | Use |
|---|---|---|---|
| 8 | 0 | Echo Request | Ping (outgoing) |
| 0 | 0 | Echo Reply | Ping (response) |
| 3 | 0-15 | Destination Unreachable | Network errors |
| 11 | 0 | Time Exceeded | Traceroute |
| -1 | -1 | All ICMP | Everything |
Allow Only Ping
# Allow ping requests IN
ingress {
from_port = 8 # Echo Request
to_port = 0
protocol = "icmp"
cidr_blocks = ["10.0.0.0/16"]
}
Allow All ICMP
# Allow ALL ICMP (ping, traceroute, unreachable, etc.)
ingress {
from_port = -1
to_port = -1
protocol = "icmp"
cidr_blocks = ["10.0.0.0/16"]
}
Allow Traceroute
ingress {
from_port = 11 # Time Exceeded
to_port = 0
protocol = "icmp"
cidr_blocks = ["10.0.0.0/16"]
}
Using aws_security_group_rule (Separate Resource)
For complex security groups, use separate rule resources:
resource "aws_security_group" "app" {
name = "app-sg"
vpc_id = aws_vpc.main.id
tags = {
Name = "app-sg"
}
}
# Separate ping rule
resource "aws_security_group_rule" "allow_ping" {
type = "ingress"
from_port = 8
to_port = 0
protocol = "icmp"
cidr_blocks = [aws_vpc.main.cidr_block]
security_group_id = aws_security_group.app.id
description = "Allow ICMP ping from VPC"
}
# Allow ping from another security group
resource "aws_security_group_rule" "allow_ping_from_bastion" {
type = "ingress"
from_port = 8
to_port = 0
protocol = "icmp"
source_security_group_id = aws_security_group.bastion.id
security_group_id = aws_security_group.app.id
description = "Allow ICMP ping from bastion"
}
IPv6 ICMP (ICMPv6)
For IPv6, use protocol icmpv6:
ingress {
from_port = 128 # ICMPv6 Echo Request
to_port = 0
protocol = "icmpv6"
ipv6_cidr_blocks = ["::/0"]
}
Bidirectional Ping
Security groups are stateful — if you allow the Echo Request inbound, the Echo Reply outbound is automatically allowed. You only need:
# This is enough for ping to work both ways
ingress {
from_port = 8
to_port = 0
protocol = "icmp"
cidr_blocks = ["10.0.0.0/16"]
}
You do NOT need a separate egress rule for the reply.
However, if you want your instance to initiate pings to external hosts, you need an outbound rule:
# Allow instance to ping external hosts
egress {
from_port = 8
to_port = 0
protocol = "icmp"
cidr_blocks = ["0.0.0.0/0"]
}
Network ACLs vs Security Groups
If ping still doesn’t work after adding security group rules, check Network ACLs:
| Security Group | Network ACL | |
|---|---|---|
| Stateful | Yes (reply auto-allowed) | No (need explicit inbound + outbound) |
| Default | Deny all inbound | Allow all |
| Level | Instance (ENI) | Subnet |
Network ACL ICMP rules:
resource "aws_network_acl_rule" "allow_ping_in" {
network_acl_id = aws_network_acl.main.id
rule_number = 100
egress = false
protocol = "icmp"
rule_action = "allow"
cidr_block = "0.0.0.0/0"
icmp_type = 8
icmp_code = 0
}
resource "aws_network_acl_rule" "allow_ping_out" {
network_acl_id = aws_network_acl.main.id
rule_number = 100
egress = true
protocol = "icmp"
rule_action = "allow"
cidr_block = "0.0.0.0/0"
icmp_type = 0
icmp_code = 0
}
NACLs are stateless — you must add both inbound and outbound rules.
Troubleshooting: Ping Still Not Working
- Security group: Is ICMP type 8 allowed inbound?
- Network ACL: Are both ICMP type 8 (inbound) and type 0 (outbound) allowed?
- Route tables: Can the source reach the destination subnet?
- OS firewall: Is
iptables/firewalld/ufwblocking ICMP on the instance? - Public vs private: Are you pinging a private IP from outside the VPC?
- Internet Gateway: For public IPs, does the VPC have an IGW and correct route?
Quick debug on the instance:
# Check if OS firewall blocks ICMP
sudo iptables -L -n | grep icmp
# Allow ICMP in iptables
sudo iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
Hands-On Courses
Learn by doing with interactive courses on CopyPasteLearn:
Conclusion
Add from_port = 8, to_port = 0, protocol = "icmp" to your security group ingress rules to allow ping. Security groups are stateful, so you don’t need a separate egress rule for the reply. If ping still fails, check Network ACLs (stateless — need both directions), route tables, and OS firewalls. Use from_port = -1, to_port = -1 to allow all ICMP types.




