Skip to main content
emnode / learn
Cost

Delete idle VPC endpoints

An interface VPC endpoint bills per hour per AZ even with zero traffic — a 3-AZ endpoint is ~$22/month for nothing. Find the idle ones and delete them (but never the free gateway kind).

13 min·10 sections·AWS

Last reviewed

Idle interface VPC endpoints: the basics

Why an endpoint moving zero bytes still shows up on the bill

An interface VPC endpoint (AWS PrivateLink) is an Elastic Network Interface placed in your subnets that lets resources reach an AWS service privately, without routing over the internet or through a NAT Gateway. It bills in two parts: roughly $0.01 per hour for every Availability Zone the endpoint is provisioned in, plus roughly $0.01 per GB of data processed. The per-AZ hourly charge runs whether the endpoint carries a single byte or none at all. An endpoint spread across three AZs is ~$0.03/hour — about $22/month — before any traffic flows.

An "idle" interface endpoint is one whose CloudWatch and VPC Flow Logs show little or no bytes processed over a meaningful window — typically 30 days. It's still attached to your subnets, still resolving its private DNS name, still billing the per-AZ hours. The usual causes are mundane: an endpoint created during a project or POC and never wired into a workload, the same endpoint duplicated into every account and VPC by a landing-zone template, or an endpoint provisioned in more AZs than the workload that uses it actually spans.

Critically — and this trips people up — gateway VPC endpoints (the kind used only for S3 and DynamoDB) are free. They have no hourly or per-GB charge at all. This lesson is about idle interface endpoints only. Never delete a gateway endpoint to "save money": there's nothing to save, and you may break private S3 access. The check fires on interface endpoints that combine a non-trivial fixed cost with no traffic to justify it.

In this lesson you'll learn the per-AZ-per-hour billing model that makes interface VPC endpoints surprisingly expensive when idle, the critical difference between free gateway endpoints (S3, DynamoDB) and billable interface endpoints, how to tell a genuinely idle endpoint from one carrying intermittent traffic, and the safe pattern for deleting one without silently breaking a service that was routing through its private DNS. You'll see the AWS CLI commands to inventory interface endpoints, check their traffic, and delete the dead ones — plus the edge cases that bite.

Fun fact

The landing-zone endpoint tax

A platform team standardised on a landing-zone template that provisioned a tidy set of 11 interface endpoints — SSM, EC2 Messages, ECR, CloudWatch Logs, KMS, and friends — into every new account, each across all three AZs "for consistency." At ~$0.03/hour per endpoint that's about $240/month per account before any traffic. When they audited 90 accounts, more than half had endpoints with zero bytes processed in 30 days — sandbox and shared-service accounts where the workloads those endpoints were meant to serve never landed. The idle subset alone was billing roughly $13,000 a month for ENIs that resolved DNS and moved nothing.

Hunting an idle endpoint in action

Priya runs a quarterly VPC audit for a fintech with 60-odd AWS accounts. Her wastage tooling flags four interface endpoints in the analytics-sandbox VPC — com.amazonaws.eu-west-1.ecr.api, .ecr.dkr, .ssm, and .kms — each provisioned across three AZs, each with essentially zero bytes processed for 30 days. That's ~$0.03/hour apiece, about $88/month combined, on a sandbox whose container workload was decommissioned a year ago.

Before deleting anything she draws the line that matters: are any of these gateway endpoints? No — the VPC's S3 gateway endpoint is free and stays. The four flagged ones are all interface type, so they're the right targets. She then confirms two things: that the traffic really is flat (a weekly batch could hide in a daily average) and that nothing still depends on the endpoint's private DNS, because deleting one can silently break a service that was resolving the AWS API through it.

She pulls the endpoint inventory, filters to Interface, checks each one's traffic, and confirms the sandbox has no running tasks pointing at them. Then she deletes the four interface endpoints in a single call and leaves the free S3 gateway endpoint untouched. Total time: about five minutes; she logs the deletion so next quarter's audit doesn't refire on a template-recreated copy.

First, list every VPC endpoint and its type. Only Interface endpoints bill — Gateway endpoints (S3, DynamoDB) are free and must be excluded from any cleanup target.

$ aws ec2 describe-vpc-endpoints --filters Name=vpc-endpoint-type,Values=Interface --query 'VpcEndpoints[].{Id:VpcEndpointId,Svc:ServiceName,AZs:length(SubnetIds),State:State}' --output table
-------------------------------------------------------------------------------
| DescribeVpcEndpoints |
+----------------+--------------------------------------+------+------------+
| Id | Svc | AZs | State |
+----------------+--------------------------------------+------+------------+
| vpce-0aa11ecr | com.amazonaws.eu-west-1.ecr.api | 3 | available |
| vpce-0bb22dkr | com.amazonaws.eu-west-1.ecr.dkr | 3 | available |
| vpce-0cc33ssm | com.amazonaws.eu-west-1.ssm | 3 | available |
| vpce-0dd44kms | com.amazonaws.eu-west-1.kms | 3 | available |
+----------------+--------------------------------------+------+------------+
# Four interface endpoints, each across 3 AZs = ~$0.03/hr each, ~$88/mo combined.

Interface-only inventory. The AZ count is the multiplier — each endpoint bills ~$0.01/hr per AZ regardless of traffic.

Before deleting, confirm the endpoint is genuinely idle. Pull 30 days of bytes processed at daily resolution — a weekly batch would show up as a spike here.

$ aws cloudwatch get-metric-statistics --namespace AWS/PrivateLinkEndpoints --metric-name BytesProcessed --dimensions Name=VPC\ Endpoint\ Id,Value=vpce-0aa11ecr --start-time $(date -u -d '30 days ago' +%FT%TZ) --end-time $(date -u +%FT%TZ) --period 86400 --statistics Sum Maximum
{
"Datapoints": [
{ "Timestamp": "2026-04-28T00:00:00Z", "Sum": 0.0, "Maximum": 0.0, "Unit": "Bytes" },
{ "Timestamp": "2026-05-05T00:00:00Z", "Sum": 0.0, "Maximum": 0.0, "Unit": "Bytes" },
{ "Timestamp": "2026-05-12T00:00:00Z", "Sum": 0.0, "Maximum": 0.0, "Unit": "Bytes" }
]
}
# 30 days, zero bytes processed — no batch, no cron, nothing depends on it.

Flat zeros over 30 days. If any day shows traffic, drill into VPC Flow Logs to find the consumer before deleting.

How VPC endpoint billing actually worksdeep dive

There are two kinds of VPC endpoint and they bill completely differently. A gateway endpoint is a route-table entry for S3 or DynamoDB only — it has no ENI, no hourly charge, and no per-GB charge. It is free. An interface endpoint (PrivateLink) is one or more Elastic Network Interfaces, one per subnet you enable, each backed by AWS-managed infrastructure. AWS bills roughly $0.01 per hour for every AZ the endpoint is provisioned in the moment it goes available, plus roughly $0.01 per GB processed. Spread an interface endpoint across three AZs and the fixed charge is ~$0.03/hour ≈ ~$22/month before any traffic — the per-AZ multiplier is the whole story.

The waste pattern is almost always fixed cost, not data. An endpoint created for a POC and never wired in, a landing-zone template that drops the same dozen endpoints into every account, or an endpoint provisioned in three AZs for a workload that only ever ran in one — all of these accrue the per-AZ hourly charge on zero traffic. This is the mirror image of the common optimisation advice: adding an interface endpoint to keep S3/ECR/API traffic off the NAT Gateway saves money only when real traffic flows through it. With no traffic, the endpoint is pure overhead and the NAT comparison never applies.

Two details matter before you delete. First, an interface endpoint can have private DNS enabled, which overrides the AWS service's public DNS name inside the VPC so that, say, ssm.eu-west-1.amazonaws.com resolves to the endpoint's private IPs. Delete it and that name reverts to public resolution — fine if the subnet has another egress path, but a silent break if it relied solely on the endpoint. Second, the endpoint policy and attached security group can quietly be the only thing permitting a call; confirm via Flow Logs that nothing is mid-flight before removing it.

# Inspect a single interface endpoint: type, AZ count (the cost multiplier),
# whether private DNS is enabled, and which security groups gate it.
aws ec2 describe-vpc-endpoints \
  --vpc-endpoint-ids vpce-0aa11ecr \
  --query 'VpcEndpoints[0].{Type:VpcEndpointType, Svc:ServiceName, AZs:length(SubnetIds), PrivateDns:PrivateDnsEnabled, SGs:Groups[].GroupId}'

# Sanity-check you are NOT about to touch a free gateway endpoint (S3/DynamoDB).
aws ec2 describe-vpc-endpoints \
  --filters Name=vpc-endpoint-type,Values=Gateway \
  --query 'VpcEndpoints[].{Id:VpcEndpointId, Svc:ServiceName}'

What's the impact of leaving idle interface endpoints running?

The direct cost is the per-AZ hourly charge on endpoints moving no traffic. A single-AZ idle endpoint is ~$7.20/month; a 3-AZ one is ~$22/month. Modest individually — but a landing-zone template that drops a dozen 3-AZ endpoints into every account is ~$240/month per account on provisioning alone, and across an estate of dozens of accounts where many endpoints never see traffic, the idle fraction lands in the low-to-mid thousands a month. The meter runs every hour regardless of whether anything ever connects.

There's a second-order cost in the AZ count specifically. Teams enable an interface endpoint in every AZ "for consistency" or because a template defaulted to it, even when the workload only spans one or two. Each extra AZ is another ~$7.20/month for redundancy nothing uses. Right-sizing the AZ set on the endpoints you do keep is often a bigger lever than deleting the fully-idle ones, and it's invisible unless someone looks at the subnet list per endpoint.

The mirror-image risk is deleting the wrong thing. Gateway endpoints for S3 and DynamoDB are free, and an interface endpoint that carries real traffic is usually saving you NAT processing charges at $0.045/GB. Treating "delete VPC endpoints" as a blanket cost play can increase your bill — by pushing AWS-bound traffic back onto a NAT Gateway — or break private connectivity. The finding is narrow on purpose: idle interface endpoints only, confirmed by 30 days of flat traffic.

Finally, idle endpoints make the architecture harder to reason about. Six months on, someone debugging a connectivity issue finds five interface endpoints in a VPC and has no idea which are load-bearing and which are template cruft. Cleaning them up isn't only a cost story — it's a hygiene story. A short, intentional list of endpoints is far easier to defend at audit and to reason about during an incident than a long, accidental one.

How do you safely clean up idle interface endpoints?

Deleting an interface endpoint is cheap; deleting one that something still resolves through is how you silently break a service. Run the four-step loop below for every flagged endpoint — and confirm the type first.

1. Confirm it's an interface endpoint, not a free gateway one

Filter the inventory to VpcEndpointType=Interface before anything else. Gateway endpoints (S3, DynamoDB only) cost nothing and never belong on a cleanup list — deleting one saves zero dollars and can break private S3 access. Only interface endpoints carry the per-AZ hourly charge, so they're the only valid targets.

2. Confirm zero traffic over a real window

Pull BytesProcessed for the endpoint at daily granularity for at least 30 days. A daily average can still hide a weekly or monthly batch, so eyeball the per-day points for any non-zero spike. If you see any traffic, drill into VPC Flow Logs filtered to the endpoint's ENIs to identify the consumer before doing anything destructive.

3. Confirm nothing depends on the endpoint's private DNS

If the endpoint has private DNS enabled, deleting it reverts the AWS service hostname to public resolution inside the VPC. That's harmless if the subnet has another egress path (NAT or a different endpoint), but a silent break if the workload relied solely on this endpoint. Check the endpoint policy and attached security groups too — sometimes the only thing permitting a call is the endpoint itself. Confirm there's a working alternative path before deleting.

4. Delete, then fix the template that created it

Delete the idle interface endpoints — multiple IDs in one call is fine — and log the deletion in your change record so the next audit doesn't refire on a recreated copy. Then go upstream: if a landing-zone or account-vending template provisions these by default, that's where the real fix is. Change the template to provision endpoints on demand (or scope them to AZs the workload actually uses) so new accounts stop arriving pre-loaded with idle ones.

# Delete one or more idle INTERFACE endpoints in a single call.
# (Gateway endpoints would be excluded by the step-1 type filter.)
aws ec2 delete-vpc-endpoints \
  --vpc-endpoint-ids vpce-0aa11ecr vpce-0bb22dkr vpce-0cc33ssm

# Confirm they're gone and nothing in the VPC still references them.
aws ec2 describe-vpc-endpoints \
  --filters Name=vpc-endpoint-type,Values=Interface \
  --query 'VpcEndpoints[].{Id:VpcEndpointId, Svc:ServiceName, State:State}'

# Optional: list endpoints with their AZ count to spot over-provisioned ones
# (more AZs than the workload uses = ~$7.20/mo each in avoidable fixed cost).
aws ec2 describe-vpc-endpoints \
  --filters Name=vpc-endpoint-type,Values=Interface \
  --query 'VpcEndpoints[].{Id:VpcEndpointId, AZs:length(SubnetIds)}'

Quick quiz

Question 1 of 5

A wastage check flags five VPC endpoints in one VPC with near-zero traffic over 30 days. One is a gateway endpoint for S3; four are interface endpoints (SSM, KMS, two ECR), each across 3 AZs. What's the right next move?

You've completed Delete idle VPC endpoints. You now know why an interface endpoint bills per AZ per hour even with zero traffic, the critical line between free gateway endpoints and billable interface ones, why this is the mirror image of the create-endpoints-to-cut-NAT advice, and the four-step loop — confirm type, confirm idle, confirm DNS, delete and fix the template. The next time the wastage tool flags one, you'll have the type check and the AZ math ready before you reach for delete-vpc-endpoints.

Back to the library