What VPC Flow Logs actually cost, and the setting AWS overrides (2026)

Akal Cloud Updated 10 min read

Quick answer

There is no charge for the feature: you pay CloudWatch Vended Logs ingestion, tiered from $0.50 per GB for the first 10 TB down to $0.05 per GB above 50 TB, with tiers resetting monthly. The biggest lever is the aggregation interval, except that AWS documents it as always 1 minute or less on Nitro-based instances regardless of the 10-minute setting you chose. Records dropped as SKIPDATA are still billed, so Cost Explorer counts more flow logs than VPC published.

VPC Flow Logs is free to switch on and is not free to run. There is no charge for the feature itself: everything you pay is for the log volume it generates, billed through a meter most people have never looked at. And the largest cost lever is not traffic. It is a setting that, on modern instances, AWS overrides anyway.

What does AWS actually charge for flow logs?

Nothing for the flow log. The VPC Flow Logs documentation states: "data ingestion and archival charges for vended logs apply when you publish flow logs. For more information about pricing when publishing vended logs, open Amazon CloudWatch Pricing, select Logs and find Vended Logs."

"Vended Logs" is the meter to know. It is the CloudWatch Logs category for logs natively published by AWS services, and it is tiered by monthly volume rather than flat. The CloudWatch pricing page works a 72 TB example in US East (N. Virginia) against these tiers:

Monthly volume tierRate per GB
First 10 TB$0.50
Next 20 TB (10–30 TB)$0.25
Next 20 TB (30–50 TB)$0.10
Over 50 TB$0.05

Two consequences follow from the shape rather than the numbers. First, AWS states that "volume tiers reset at the start of every month," so there is no annual accumulation working in your favour. Second, the tiering is steeply regressive: the first terabyte costs ten times what the sixtieth does. A low-volume estate is paying top rate on everything, which is precisely the estate where turning down volume pays best.

Rates vary by Region and by destination, and the destination adds its own charges on top: the pricing page notes that for S3 destinations "apart from vended logs delivery charges, customers are charged based on S3 storage costs," and for Firehose destinations "besides vended logs delivery charges, customers are charged based on Firehose ingestion charges per GB processed." Read the current per-destination rates on the pricing page itself rather than from any blog, this one included: they change, and a stale rate is worse than no rate.

What is the biggest volume lever?

The aggregation interval, by a wide margin. Per the flow log records reference: "the aggregation interval is the period of time during which a particular flow is captured and aggregated into a flow log record. By default, the maximum aggregation interval is 10 minutes. When you create a flow log, you can optionally specify a maximum aggregation interval of 1 minute."

AWS's own comparison is qualitative, not numeric: "flow logs with a maximum aggregation interval of 1 minute produce a higher volume of flow log records than flow logs with a maximum aggregation interval of 10 minutes." It does not publish a ratio, and you should not assume ten times. A long-lived TCP connection produces one record per interval either way; a burst of short connections may already be one record per interval at both settings. The multiplier is workload-shaped, so measure it on your own logs rather than modelling it.

Does choosing 10 minutes actually get you 10 minutes?

Often not, and this is the single most expensive sentence in the flow logs documentation:

"When a network interface is attached to a Nitro-based instance, the aggregation interval is always 1 minute or less, regardless of the specified maximum aggregation interval."

Nitro is not an exotic corner of the fleet; it is essentially the whole modern instance catalogue. So on most estates, the 10-minute setting you selected to control cost is silently not in effect, and the record volume you are paying for is the 1-minute volume. Anyone forecasting flow log spend from the configured interval is forecasting the wrong number.

Note the phrasing is "1 minute or less". The interval is a maximum, not a guarantee of one record per minute.

This is not a stray sentence in one guide. The FlowLog API reference repeats it on the maxAggregationInterval field itself, which it documents as taking only two values, 60 or 600 seconds: "when a network interface is attached to a Nitro-based instance, the aggregation interval is always 60 seconds (1 minute) or less, regardless of the specified value." Two independent AWS references, same override.

What is the second lever?

Record width. The default format is fixed: "with the default format, the flow log records include the version 2 fields… You cannot customize or change the default format." That is fourteen fields.

A custom format lets you pick: "you specify which fields are included in the flow log records and in which order… You can specify any number of the available flow log fields, but you must specify at least one." The available field list now runs to version 11 and includes ECS task metadata, instance and Auto Scaling group tag values, next-hop interface details and encryption status. Every one of those is bytes on every record.

The temptation with a rich field list is to enable everything "in case we need it later". On a meter charged per GB ingested, that decision is a recurring monthly bill for data nobody queries. Pick fields against the questions you actually ask.

Some fields also cost you permissions rather than money: the ECS fields require ecs:ListClusters and friends, and the tag fields require ec2:DescribeTags plus iam:CreateServiceLinkedRole. The Auto Scaling tag fields carry an extra operational dependency worth reading before you rely on them: "you must have at least one enabled CloudTrail trail in your account for Auto Scaling group tag values to be properly tracked and updated in real time."

Do you pay for records that were never delivered?

Yes, and AWS says so in a place almost nobody reads: the definition of the log-status field. SKIPDATA means "some flow log records were skipped during the aggregation interval. This might be because of an internal capacity constraint, or an internal error." Then:

"If you are using AWS Cost Explorer to view VPC flow log charges and some flow logs are skipped during the flow log aggregation interval, the number of flow logs reported in AWS Cost Explorer will be higher than the number of flow logs published by Amazon VPC."

So a count of delivered records will not reconcile to the billed quantity, by design, and the direction is against you. If you are building a cost model on record counts, count SKIPDATA too, and expect a residual. This is its own instance of the broader pattern in why Cost Explorer and your CUR disagree: the billed unit and the observable unit are not always the same unit.

Which destination and format should you pick?

Three destinations are supported: "Amazon CloudWatch Logs, Amazon S3, or Amazon Data Firehose." All three bill the same Vended Logs ingestion meter, plus their own charges downstream.

Where AWS does publish concrete numbers is the file format for S3 delivery. Per the flow log files documentation, "each file is compressed into a single Gzip file," and of the two format options, plain text is the default while for Parquet: "queries on data in Parquet format are 10 to 100 times faster compared to queries on data in plain text. Data in Parquet format with Gzip compression takes 20 percent less storage space than plain text with Gzip compression."

And the limitation, stated by AWS rather than discovered by you: "if data in Parquet format with Gzip compression is less than 100 KB per aggregation period, storing data in Parquet format may take up more space than plain text with Gzip compression due to Parquet file memory requirements."

That inverts the usual advice for exactly the estates that most want to save money. A quiet VPC producing small files per interval is a case where Parquet costs more storage, not less. Parquet wins on volume; on a low-traffic account, check before assuming.

OptionWhat AWS documentsWhen it helps
Text (default)Plain text, Gzip compressedSmall volumes; simplest to grep
Parquet10–100x faster queries, 20% less storage than text+GzipHigh volume, queried through Athena
Hive-compatible prefixesKeys become aws-account-id=…/year=…; run MSCK REPAIR TABLE firstAthena and Glue tooling
Hourly partitions"You can get faster results and save on query costs by partitioning logs on an hourly basis"Large volumes queried at a specific hour

Note that hourly partitioning is a saving on query cost, not on the ingestion meter. The two are separate bills and the partitioning decision only touches one of them. Athena pricing charges on data scanned, at $5 per TB in its worked example, and demonstrates the same two levers AWS recommends for flow logs: its example shows "3x savings from compression and 4x savings for reading only one column" after converting to Parquet. Partitioning narrows what a query has to scan; Parquet narrows it again.

Where do flow logs and network cost meet?

Usefully, in the interface-type field added in version 11, whose supported values are nat_gateway, network_load_balancer, regional_nat_gateway, transit_gateway and vpc_endpoint. Those are the expensive middleboxes, and being able to filter traffic to the interface type that bills for it is what turns a flow log into a cost tool rather than a security one.

Two field-level gotchas if you work with NAT Gateways, especially since the regional availability mode added in November 2025. For a regional NAT gateway, interface-id "returns a '-' symbol", as does subnet-id; the identifier you want is the version 9 resource-id field, "the ID of the regional NAT gateway that contains the network interface for which the traffic is recorded." A pipeline keyed on interface-id will drop regional NAT gateway traffic entirely rather than erroring.

Also remember flow logs cover more than instances. You can create them for interfaces created by other services, AWS naming "Elastic Load Balancing, Amazon RDS, Amazon ElastiCache, Amazon Redshift, Amazon WorkSpaces, NAT gateways" and transit gateways. And for pod-level attribution, the pkt-srcaddr field exists precisely because "the IP address of a pod in Amazon EKS is different from the IP address of the network interface of the instance node on which the pod is running", which is the network-side companion to EKS split cost allocation data.

How do you attribute the flow log bill itself?

Not through the flow log. AWS's instruction is to tag the destination: "to track charges from publishing flow logs, you can apply cost allocation tags to your destination resource. Thereafter, your AWS cost allocation report includes usage and costs aggregated by these tags."

So the log group, the S3 bucket or the Firehose stream is the thing that carries team or cost-center, and one shared log group for the whole estate is a decision to have no attribution at all. If the tags come back empty afterwards, the causes are enumerated in why cost allocation tags show up empty.

What deleting a flow log does and does not do

"Deleting a flow log disables the flow log service for the resource, so that no new flow log records are created or published. Deleting a flow log does not delete any existing flow log data."

Ingestion stops immediately; storage does not. Years of Gzip in S3 keeps billing until a lifecycle policy removes it, and that is a separate cleanup from the one you think you just did.

A checklist that reflects what is actually billed

  1. Find every flow log first, across all Regions. They can be attached to a VPC, a subnet, or an individual network interface, and the subnet and ENI ones are the ones nobody remembers creating.
    aws ec2 describe-flow-logs \
      --query 'FlowLogs[].[FlowLogId,ResourceId,LogDestinationType,MaxAggregationInterval]' \
      --output table
  2. Check for overlap. A VPC-level log and a subnet-level log inside it capture the same interfaces and you pay for both.
  3. Do not trust MaxAggregationInterval as a cost input on Nitro-based instances. It is overridden.
  4. Trim the field list to fields you query. Every field is bytes on every record, every month.
  5. Consider whether every VPC needs ALL traffic rather than REJECT. The traffic type is chosen per flow log at creation.
  6. Tag the destination resource, not the flow log, if you want the charge attributed.
  7. Set a lifecycle policy on the destination bucket. Ingestion is the headline charge; archival is the one that never stops. Check the storage class you transition to against object size first: gzipped flow log files are small, and the 128 KB and 40 KB minimums can make a cheaper class cost more.

Flow logs are worth running. They are the only record of what actually traversed your network, and network charges are otherwise close to impossible to attribute. The point is to pay for the records you will read, at the width you will read them, in a place you can tie back to a team.

See this on your own bill

Akal Cloud connects in about two minutes and shows the same numbers against your real AWS accounts.

Get started on AWS Marketplace

Related reading