SQS queues in a VPC private subnet can talk to the internet without an internet gateway, which is a surprising but powerful security feature.

Let’s see how this actually works. Imagine you have a web application running on EC2 instances in a private subnet. These instances need to send messages to an SQS queue, but you don’t want them to have direct internet access.

Here’s a simplified setup:

  • VPC: vpc-0123456789abcdef0
  • Private Subnet: subnet-abcdef0123456789
  • EC2 Instance: i-0abcdef1234567890 (in the private subnet)
  • SQS Queue: my-secure-queue (accessible from anywhere, but we’ll control access)

Normally, an EC2 instance in a private subnet can’t reach the internet. It has no route to 0.0.0.0/0 via an Internet Gateway. But if you try to send a message to SQS from this instance, it will work. How?

The magic is a VPC endpoint for SQS.

aws ec2 create-vpc-endpoint \
    --vpc-id vpc-0123456789abcdef0 \
    --service-name com.amazonaws.us-east-1.sqs \
    --subnet-ids subnet-abcdef0123456789 \
    --vpc-endpoint-type Interface \
    --security-group-ids sg-0123456789abcdef0 \
    --region us-east-1

When you create an interface VPC endpoint for SQS, AWS provisions Elastic Network Interfaces (ENIs) in your specified subnets. These ENIs get private IP addresses from your subnet’s CIDR range. Crucially, these ENIs are assigned security groups that allow outbound traffic on port 443 to the SQS service.

Your EC2 instance, instead of trying to route SQS traffic over the public internet, now routes it to the private IP address of the SQS VPC endpoint ENI within your VPC. This traffic never leaves AWS’s network.

The service name com.amazonaws.us-east-1.sqs is specific to the region. If your SQS queue is in us-west-2, you’d use com.amazonaws.us-west-2.sqs. The Interface type means it’s an ENI-backed endpoint, which is the most common and flexible type. The security group attached to the endpoint ENI (sg-0123456789abcdef0) is critical: it needs to allow outbound traffic on TCP port 443 to the CIDR blocks that SQS uses within the AWS network. AWS automatically manages these IP addresses for interface endpoints.

Your application code on the EC2 instance doesn’t need to change. It still uses the standard AWS SDK to send messages to my-secure-queue. The AWS SDK, when running within a VPC that has an SQS endpoint configured, will automatically detect and use the VPC endpoint for communication.

The problem this solves is isolating your application’s communication with AWS services. Instead of relying on NAT Gateways or Internet Gateways, which expose your instances to the wider internet (even if egress is restricted), you create a private, direct connection. This significantly reduces your attack surface. You can enforce strict network policies, ensuring your instances can only talk to SQS (and other services for which you’ve created endpoints) via these private IPs.

Here’s how you’d typically send a message from your EC2 instance:

import boto3

sqs = boto3.client('sqs', region_name='us-east-1')
queue_url = 'https://sqs.us-east-1.amazonaws.com/123456789012/my-secure-queue'

response = sqs.send_message(
    QueueUrl=queue_url,
    MessageBody='This is a secure message!'
)

print(f"Message sent: {response['MessageId']}")

This code remains identical whether you’re using a public endpoint or a VPC endpoint. The SDK handles the routing behind the scenes.

The VPC endpoint also allows you to control which SQS queues your instances can access using endpoint policies. By default, an interface endpoint grants access to all SQS queues in the region. You can refine this.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "sqs:SendMessage",
            "Resource": "arn:aws:sqs:us-east-1:123456789012:my-secure-queue"
        }
    ]
}

This policy, attached to the VPC endpoint, restricts actions to SendMessage and only to the my-secure-queue ARN. This is applied at the endpoint level and works in conjunction with the queue’s own access policies. You can also deny specific actions or resources.

A common point of confusion is that while the VPC endpoint allows your instances to reach SQS privately, SQS itself still needs to be configured to allow access from your instances. This means your queue’s IAM policy or resource policy must permit actions from the IAM role associated with your EC2 instance. The VPC endpoint is a network-level control; IAM policies are an identity-level control.

The most surprising aspect for many is how this works without a NAT Gateway or Internet Gateway. The EC2 instance’s route table for 0.0.0.0/0 still points to nowhere (or a blackhole), but when the destination IP is one of the private IPs assigned to the SQS VPC endpoint ENIs, the instance knows to send the traffic locally within the VPC. The VPC itself handles routing to the correct ENI.

Once you have the VPC endpoint configured and your security groups and IAM policies are aligned, your EC2 instances in the private subnet can send messages to SQS without ever touching the public internet.

The next hurdle you’ll likely encounter is needing to access other AWS services like S3 or DynamoDB from the same private subnet, which will require similar VPC endpoints.

Want structured learning?

Take the full Sqs course →