Skip to content
AWS aws ec2 5 min read

Security Groups (The Instance Firewall)

A security group is a virtual firewall that sits directly in front of your EC2 (Elastic Compute Cloud — AWS’s service for renting virtual servers) instances. It decides which network traffic is allowed in and out. Every EC2 instance must have at least one security group attached, and getting these rules right is usually the difference between “my server is reachable” and “why can’t I connect?”. This page explains how security groups work, how to add rules in both the Console and the CLI (Command Line Interface), and the surprising rules that trip people up.

What a security group actually is

Think of a security group as a list of “allow” rules wrapped around one or more instances. It works at the instance level — the rules are applied to the network interface attached to each instance, not to the whole subnet. A subnet is a slice of IP addresses inside your VPC (Virtual Private Cloud — your private network in AWS).

Two ideas define how security groups behave:

  • Allow-only. There is no such thing as a “deny” rule. A security group lists only the traffic you permit. Anything not explicitly allowed is blocked by default. You make something more restrictive by removing allow rules, never by adding deny rules.
  • Stateful. If you allow an inbound request, the response is automatically allowed back out — you do not write a matching outbound rule for the reply. The security group “remembers” the connection.

By default a new security group allows all outbound traffic and no inbound traffic. So a fresh instance can reach the internet, but nothing on the internet can reach it until you add an inbound rule.

Gotcha — stateful vs. NACLs. A Network ACL (Access Control List) is a separate, stateless firewall that operates at the subnet level. “Stateless” means you must explicitly allow both the request and its return traffic (often on the ephemeral high port range 1024-65535). Security groups never need that — return traffic is automatic. If you only use security groups (the common case), you can ignore return-traffic rules entirely.

When to use security groups (and when not)

Use security groups as your primary, day-to-day firewall for EC2, RDS (Relational Database Service), load balancers, and most other resources that live in a VPC. They are simple, stateful, and attached right where the traffic lands.

Reach for NACLs only when you need a coarse, subnet-wide guardrail — for example, to block a specific malicious IP range across every instance in a subnet, or to add an explicit deny (which security groups cannot express). For everything else, prefer security groups: they are easier to reason about because you never juggle return traffic.

Anatomy of a rule

FieldMeaningExample
TypeA preset that fills in protocol and portSSH, HTTP, HTTPS, Custom TCP
ProtocolTCP, UDP, ICMP, or AllTCP
Port rangeThe port(s) the rule covers22, 443, 8080
Source (inbound)Where traffic is allowed from203.0.113.10/32, sg-0a1b2c3d
Destination (outbound)Where traffic is allowed to0.0.0.0/0

A /32 after an IP means “exactly this one address”. 0.0.0.0/0 means “anywhere on the internet (IPv4)”.

Adding an inbound rule (Console)

Say you want to allow web browsers to reach a site on your instance over HTTPS (port 443) from anywhere.

  1. Open the EC2 console and choose Security Groups in the left menu (under Network & Security).
  2. Select your security group (for example sg-0a1b2c3d) and open the Inbound rules tab.
  3. Click Edit inbound rules, then Add rule.
  4. Set Type to HTTPS — this auto-fills Protocol TCP and Port 443.
  5. Set Source to Anywhere-IPv4 (0.0.0.0/0). For SSH, set Source to My IP instead, never Anywhere.
  6. Add a short Description like Public HTTPS so future you knows why the rule exists.
  7. Click Save rules. The change takes effect within seconds — no restart needed.

Adding an inbound rule (CLI)

The same HTTPS rule with AWS CLI v2:

aws ec2 authorize-security-group-ingress \
  --group-id sg-0a1b2c3d \
  --ip-permissions 'IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges=[{CidrIp=0.0.0.0/0,Description="Public HTTPS"}]'

Output:

{
    "Return": true,
    "SecurityGroupRules": [
        {
            "SecurityGroupRuleId": "sgr-0c1d2e3f4a5b6c7d8",
            "GroupId": "sg-0a1b2c3d",
            "IsEgress": false,
            "IpProtocol": "tcp",
            "FromPort": 443,
            "ToPort": 443,
            "CidrIpv4": "0.0.0.0/0",
            "Description": "Public HTTPS"
        }
    ]
}

To remove a rule, use revoke-security-group-ingress with the same parameters (or the SecurityGroupRuleId).

Referencing another security group as a source

Instead of pinning rules to IP addresses, you can list another security group as the source. Any instance that belongs to that group is then allowed — no matter what its IP is.

This is the cleanest pattern for tiered applications. Imagine web servers in sg-web that must talk to a database in sg-db on port 3306 (MySQL). Rather than tracking every web server’s private IP, you allow the group:

aws ec2 authorize-security-group-ingress \
  --group-id sg-db \
  --ip-permissions 'IpProtocol=tcp,FromPort=3306,ToPort=3306,UserIdGroupPairs=[{GroupId=sg-web,Description="Allow web tier to DB"}]'

Now if you launch ten more web servers into sg-web, they instantly get database access — and nothing else does. This self-maintaining approach is far less error-prone than hardcoded /32 IP ranges.

Tip. Group-to-group references keep working even as instances scale up and down behind an Auto Scaling group. Use them for any internal traffic between your own tiers, and reserve IP-based rules for clients you do not control (like the public internet or a partner’s office).

Defining a security group as code

Here is the database group above expressed in CloudFormation, so the rules live in version control:

Resources:
  DbSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Database tier
      VpcId: vpc-0a1b2c3d
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 3306
          ToPort: 3306
          SourceSecurityGroupId: sg-web
          Description: Allow web tier to DB

Cost note

Security groups themselves are free — there is no charge for creating them or for the rules. You only pay for the data transfer that the allowed traffic generates (for example, data leaving AWS to the internet). The default soft limits are generous: 60 inbound and 60 outbound rules per group, and 5 security groups per network interface, all adjustable via a quota increase request.

Best practices

  • Open the narrowest port range and source you can. Never expose SSH (port 22) or RDP (port 3389) to 0.0.0.0/0; scope them to your IP or use Session Manager instead.
  • Prefer group-to-group references over IP ranges for traffic between your own tiers — they scale automatically.
  • Add a Description to every rule so the intent survives long after you’ve forgotten it.
  • Use one purpose-built group per tier (web, app, db) rather than one giant catch-all group shared by everything.
  • Remember security groups are stateful: do not add outbound rules just to permit replies — they are already allowed.
  • Define groups as code (CloudFormation or Terraform) so rule changes are reviewed and reversible.
Last updated June 15, 2026
Was this helpful?