Skip to main content

natpath

A read-only command that tells you when private subnets send S3 and DynamoDB traffic through a NAT gateway.

NAT data processing is billed for every byte. The gateway endpoints for S3 and DynamoDB are free. They add a route for those two services, so that traffic never reaches the NAT. Traffic to the rest of the internet still does. If the route table behind the NAT does not have those endpoints, S3 and DynamoDB bytes from those subnets are on the NAT bill. The cost page shows the amount. This command shows the route tables and subnets that cause it.

You run it, add the missing gateway endpoints on the printed route tables, and run it again until it prints nothing.

Run

pip install natpath
natpath
natpath --region us-east-1
natpath --profile prod

It reads one region: the region in your AWS configuration, or the one you pass with --region. It uses the credentials you already have. It does not change the network, and it does not read Cost Explorer.

A quiet run prints nothing and exits 0. A missing door prints the finding and exits 1. Exit 2 means AWS could not be read.

Example

NAT gateway nat-abc (prod)
  These private subnets send all outside traffic through it:
    app-a (subnet-a)
    app-b (subnet-b)
  Route table:
    private (rtb-111)
  There is no free private door for S3.
  There is no free private door for DynamoDB.
  12 Lambdas run in these subnets.
  Their S3 and DynamoDB traffic is on the NAT bill.
  Adding the free doors takes that traffic off the bill.

For each printed route table, add a gateway endpoint for S3 and a gateway endpoint for DynamoDB, then run natpath again. Choose the gateway endpoint type. The interface type is a separate PrivateLink service, and this traffic stays on the NAT until the gateway type is on the route.

The door covers S3 and DynamoDB in this region. Calls to another region still use the NAT. The hourly NAT charge is unchanged. The command cannot see how many bytes moved. It sees the path: anything in these subnets that calls those services in this region is paying the NAT data charge for those bytes.

Permissions

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeNatGateways",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSubnets",
        "ec2:DescribeVpcEndpoints",
        "lambda:ListFunctions"
      ],
      "Resource": "*"
    }
  ]
}

What it treats as a free door

A route table sends outside traffic through a NAT when it has an active 0.0.0.0/0 route to that NAT gateway. The subnets on that table are the ones listed. A subnet with an explicit association uses that table. Every other subnet in the VPC uses the main route table.

A free door is an active route to an available gateway endpoint for that service. An endpoint that exists only on some other route table does not count. A blackhole route does not count. The Lambda line is how many functions have a VPC subnet in that set. The finding is the missing door, including when the count is zero, because other workloads in those subnets use the same path.

Development

python -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
ruff check src tests && ruff format --check src tests && mypy && pytest

Metadata

Release files for natpath 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for natpath 0.1.1
File Size Uploaded
natpath-0.1.1.tar.gz 16.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for natpath 0.1.1
File Interpreter ABI Platform
natpath-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 28.3 kB

Release files / natpath-0.1.1.tar.gz

Download URL natpath-0.1.1.tar.gz
Size 16.9 kB
Tags Source
SHA-256 checksum
How to use checksums
1d53a8cc62892c01dc7c88ba0fd373dac2cf91bed7d50e86cfe90891b6882c5e
BLAKE2b-256 checksum
How to use checksums
27093927d2d1419321057c4612f828b0615253d047df03e1837cd0954725ef9e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / natpath-0.1.1-py3-none-any.whl

Download URL natpath-0.1.1-py3-none-any.whl
Size 11.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
ba68f0b898fe795a717eb16b70526f5046be4c453137c976e6af8e3d7b373054
BLAKE2b-256 checksum
How to use checksums
53c4a02d7ea7bf84357711dc0b9d6dfb9b250206802eb49a53ec4402456169ef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page