VPC Flow Logs don’t actually tell you what happened on the network, but rather the metadata of network packets that were accepted or rejected.

Let’s see this in action. Imagine a web server running on an EC2 instance with private IP 10.0.1.10 and a public IP 54.123.45.67. It’s listening on port 80. A user from the internet (203.0.113.5) tries to access it.

Here’s what a successful flow log entry might look like in Amazon CloudWatch Logs:

{
    "version": 2,
    "account": "123456789012",
    "interfaceId": "eni-0abcdef1234567890",
    "srcaddr": "203.0.113.5",
    "dstaddr": "10.0.1.10",
    "srcport": 54321,
    "dstport": 80,
    "protocol": 6,
    "packets": 15,
    "bytes": 1230,
    "start": 1678886400,
    "end": 1678886460,
    "action": "ACCEPT",
    "logStatus": "OK"
}

And here’s a rejected one:

{
    "version": 2,
    "account": "123456789012",
    "interfaceId": "eni-0abcdef1234567890",
    "srcaddr": "203.0.113.5",
    "dstaddr": "10.0.1.10",
    "srcport": 54321,
    "dstport": 80,
    "protocol": 6,
    "packets": 0,
    "bytes": 0,
    "start": 1678886400,
    "end": 1678886460,
    "action": "REJECT",
    "logStatus": "OK"
}

The core problem VPC Flow Logs solve is providing visibility into network traffic patterns that would otherwise be opaque. You can see what’s trying to get in, where it’s coming from, where it’s going, and whether the security groups or network ACLs are letting it through or blocking it. This isn’t about inspecting the content of the packets, but rather the decision made about them.

The mental model for Flow Logs is simple: every Elastic Network Interface (ENI) in your VPC generates a log entry for each IP packet that either enters or leaves it. You can choose to log accepted traffic, rejected traffic, or both. These logs are typically sent to Amazon CloudWatch Logs or Amazon S3 for analysis.

The key levers you control are:

  • Traffic Type: ACCEPT or REJECT. Logging both is usually best for debugging.
  • Log Destination: CloudWatch Logs (for real-time analysis and alarms) or S3 (for long-term storage and bulk analysis).
  • VPC/Subnet/ENI Scope: You can enable flow logs at the VPC level (default for all ENIs), or specifically for certain subnets or even individual ENIs.

Here’s how you’d enable flow logs for a VPC using the AWS CLI. This command logs both accepted and rejected traffic and sends it to CloudWatch Logs:

aws ec2 create-flow-logs \
    --resource-type VPC \
    --resource-ids vpc-0123456789abcdef0 \
    --traffic-type ALL \
    --log-destination-type cloud-watch-logs \
    --log-destination arn:aws:logs:us-east-1:123456789012:log-group:/aws/vpc/flowlogs

Once logs are flowing into CloudWatch Logs, you can query them using CloudWatch Logs Insights. For example, to find all rejected traffic destined for your web server’s private IP (10.0.1.10) on port 80:

fields @timestamp, srcaddr, dstaddr, dstport, protocol, action
| filter action = "REJECT" and dstaddr = "10.0.1.10" and dstport = 80
| sort @timestamp desc
| limit 50

This query would reveal if, for instance, a Network ACL is blocking incoming traffic on port 80. If the action is ACCEPT, you know the network path is open and the issue lies within the EC2 instance itself (e.g., the web server application isn’t running or isn’t listening on that port).

The protocol field uses numbers: 6 for TCP, 17 for UDP, and 1 for ICMP. The srcport and dstport are crucial for pinpointing application-level communication. A REJECT on dstport 80 with protocol 6 (TCP) is a strong indicator that a firewall rule is misconfigured.

It’s important to remember that flow logs are aggregated. A single flow log record can represent thousands of packets or a significant amount of data transferred, summarizing a period of time (the start and end timestamps). This means you won’t see individual packet contents, but rather the aggregate behavior and the system’s decision for that traffic flow.

The logStatus field can sometimes indicate issues with the logging mechanism itself, such as IMCOMPLETE if the log delivery was interrupted.

A common pitfall is enabling flow logs after a network issue has occurred and expecting to see logs for the problematic period. Flow logs only capture traffic from the moment they are enabled. Another is assuming REJECT means a security group is blocking it; it could be a Network ACL, an iptables rule on the instance, or even a route table misconfiguration leading to an unroutable destination.

The next challenge you’ll often face after debugging network connectivity is understanding the performance characteristics of that traffic, which is where packet mirroring solutions come into play.

Want structured learning?

Take the full Aws course →