This website uses cookies

Read our Privacy policy and Terms of use for more information.

What it actually is

Direct Connect is a private Ethernet circuit between a router you control and an AWS router sitting in a colocation facility. That is all. There is no managed service in the middle, no tunnel, no encryption by default. A connection is a port at a Direct Connect location: you plug fiber into it (or a partner does), and AWS lights up the other end.

What makes it a service rather than a cable are virtual interfaces. Each virtual interface (VIF) is a VLAN on that port carrying one BGP session, and the type of VIF decides what the BGP session can reach. Everything else in this article is a consequence of those two facts: it is a physical port you pay for by the hour, and a BGP session you operate.

If you remember one thing: Direct Connect does not make your traffic "faster to AWS". It makes your path deterministic. You trade the internet's variance for a circuit with a fixed price, a fixed route, and a fixed way of failing.

The model

There are two connection types. A dedicated connection is a whole port at 1, 10, 100, or 400 Gbps, owned by your account, which you reach through a Direct Connect Partner or your own network provider. A hosted connection is a slice of a partner's port, sold at 50, 100, 200, 300, 400, or 500 Mbps and 1, 2, 5, 10, or 25 Gbps, and the partner controls the capacity and the price. The AWS docs list single-mode fiber with 1000BASE-LX, 10GBASE-LR, 100GBASE-LR4, or 400GBASE-LR4 optics, 802.1Q VLAN support end to end, and BGP with MD5 authentication. BFD is optional, and asynchronous BFD only takes effect once you configure it on your own router.

Then come the three VIF types. A private VIF reaches a VPC over private IPs, attached either to a virtual private gateway or to a Direct Connect gateway. A public VIF reaches AWS public endpoints such as S3 and the API endpoints, and the prefixes you advertise on it are visible to all AWS customers. A transit VIF reaches one or more Transit Gateways through a Direct Connect gateway. MTU differs by type: private VIFs take 1500 or 9001, transit VIFs take 1500 or 8500.

The Direct Connect gateway (DXGW) is the piece people skip until they need it. It is a global construct, not tied to a Region, that lets one VIF reach virtual private gateways or transit gateways in other Regions and other accounts. Without it, a private VIF talks to exactly one VPC's virtual private gateway.

Who operates what: AWS operates its router and the DXGW. You operate your router, your BGP configuration, the cross-connect in the colo (billed by the colo provider), and the last-mile circuit if your datacenter is not in the colo. The last-mile part is why Direct Connect projects slip. The AWS side takes minutes; the physical side takes weeks.

When to use it, when not to

Direct Connect

Site-to-Site VPN

Internet + TLS

Path

Private circuit, fixed route

Encrypted tunnels over the internet

Public internet

Throughput

1 to 400 Gbps per port

Per-tunnel limits, ECMP over Transit Gateway to scale

Whatever your ISP gives you

Lead time

Weeks (cross-connect, circuit)

Minutes

None

Encryption

Not by default (MACsec or IPsec over it if you need it)

Always

TLS

Cost shape

Port-hours plus per-GB out, or flat rate

Per-connection hour plus data transfer

Data transfer out

Right when

Sustained high volume, predictable latency, compliance wants no internet path

Fast start, backup path, low volume

Everything that does not need the others

Use Direct Connect when you move enough data that per-GB internet egress is a line item someone asks about, or when a workload needs predictable latency to a datacenter. Do not use it to "be more secure" by itself: a cable is not encryption. And do not use it as your only path. The AWS pricing guide itself says that a single flat-rate connection leaves you to provide resiliency yourself, for example with Site-to-Site VPN, and the SLA says the same thing in legal language.

What it costs

Two meters run for a dedicated connection: port-hours and data transfer out (DTO). Data transfer in is $0.00 per GB everywhere, and there is no setup fee or minimum, but port-hours bill even when nothing flows. From the pay-as-you-go pricing page, dedicated ports outside Japan are $0.30/hour at 1 Gbps, $2.25 at 10 Gbps, $22.50 at 100 Gbps, and $85.00 at 400 Gbps. Hosted connections are priced by the partner; the page lists $0.03/hour at 50 Mbps up to $6.20/hour at 25 Gbps. DTO from a US source to the contiguous US, Canada, or Europe is $0.0200 per GB, and from Tokyo or Osaka to the US it is $0.0900.

Then there are the costs that are not on the AWS bill: the cross-connect fee at the colo, the last-mile circuit, and your router. Budget conversations that start from the $2.25/hour number and stop there are the reason finance dislikes network projects.

The thing that changed: on September 15, 2026, AWS announced flat-rate pricing for dedicated connections. One fixed hourly rate replaces port-hours plus DTO. It applies to 10 Gbps and 100 Gbps dedicated connections only, in commercial Regions (China excluded). You pick one of five geographic tiers, from same-metro to any Region globally, and the rate depends on bandwidth and tier. The tier covers DTO from the Regions inside it; traffic from Regions outside your tier is billed at standard rates. Data transfer in is still free, and SiteLink, the colo cross-connect, and last-mile charges are billed separately.

The flat-rate page publishes two example rates: a 10 Gbps port-pair at Tier 1 is $10.96/hour and a 100 Gbps port-pair at Tier 3 is about $219.18/hour. The page's monthly figure for the first one is $8,000.07, but $10.96 times 730 hours is $8,000.80. I use the arithmetic, and so should you when you budget; confirm against the page before signing anything.

The word "port-pair" matters. A flat-rate port-pair is two dedicated ports on different devices with the same speed and tier. One carries traffic and the other stands by, at no extra charge. Usable bandwidth is one port: a 10 Gbps port-pair is 10 Gbps, not 20. So the honest comparison is flat-rate against two pay-as-you-go ports, not one.

On that basis the break-even is in the next section's script. The short version for 10 Gbps: flat-rate wins above roughly 236 TB of outbound data per month, which is about 7 percent utilization of one port. Below that, pay-as-you-go is cheaper. Free tier: none.

The limits that bite

Straight from the quotas page. Hard limits: 50 private or public VIFs per dedicated connection (51 combined with transit, of which at most 4 are transit), and exactly 1 VIF per hosted connection. A DXGW takes at most 20 virtual private gateways, 6 transit gateways, and 30 private or transit VIFs. Adjustable: 10 active connections per location per Region per account, 200 DXGWs per account, 10 LAGs per Region.

The one that causes outages is the BGP prefix limit. A private or transit VIF accepts 100 routes per session by default (separately for IPv4 and IPv6), raisable to 1,000 with prefix controls. If you advertise more than your configured limit, the BGP session goes idle and the VIF reports DOWN. A route table that grew during a network merger has taken production off Direct Connect this way. Transit gateway to on-premises is capped at 200 prefixes combined unless raised.

Flat-rate has its own constraints. One billing mode per account per location. Billing mode changes are limited to three per connection in any rolling six months. A LAG cannot mix modes. Bandwidth cannot be changed after creation. Hosted connections are not eligible.

Build it

The physical port is not something I can walk you through in a blog post: you request it, wait for the Letter of Authority, and have the colo run a cross-connect. What we can build in an afternoon is everything on the design side: the cost model, a catalog of locations, and the AWS-side routing core (a Direct Connect gateway associated to a virtual private gateway) that costs nothing while idle. I did not have an AWS account attached to this run, so the boto3 below follows the current API reference and is not executed; the cost model is, and its output is shown.

Prerequisites. Python 3.10+, pip install boto3, an AWS account, and credentials for a role with these actions: directconnect:DescribeLocations, directconnect:DescribeConnections, directconnect:CreateDirectConnectGateway, directconnect:DescribeDirectConnectGateways, directconnect:CreateDirectConnectGatewayAssociation, directconnect:DescribeDirectConnectGatewayAssociations, directconnect:DeleteDirectConnectGateway, directconnect:DeleteDirectConnectGatewayAssociation, plus ec2:CreateVpnGateway, ec2:AttachVpnGateway, ec2:DescribeVpnGateways, ec2:DetachVpnGateway, ec2:DeleteVpnGateway. Estimated cost of following along: $0. The DXGW, the virtual private gateway, and the association have no hourly charge by themselves; only ports and data transfer do.

Step 1: the cost model. Save as dx_cost_model.py:

HOURS = 730
PAYG_PORT_HR = {"1G": 0.30, "10G": 2.25, "100G": 22.50, "400G": 85.00}
DTO_PER_GB = 0.0200        # US source -> US / Canada / Europe
FLAT_10G_TIER1_HR = 10.96  # port-pair, includes DTO in tier

def payg(speed, gb_out, ports=2):
    return PAYG_PORT_HR[speed] * HOURS * ports + gb_out * DTO_PER_GB

def flat_10g():
    return FLAT_10G_TIER1_HR * HOURS

def line_rate_gb(gbps):
    return gbps * 3600 * HOURS / 8

if __name__ == "__main__":
    flat = flat_10g()
    be = (flat - PAYG_PORT_HR["10G"] * HOURS * 2) / DTO_PER_GB
    print(f"flat: ${flat:,.2f}/mo | payg floor: ${payg('10G', 0):,.2f}/mo")
    print(f"break-even: {be/1000:,.0f} TB/mo = {be/line_rate_gb(10):.1%} of one 10G port")
    for tb in (10, 50, 100, 236, 400):
        print(f"{tb:>4} TB -> payg ${payg('10G', tb*1000):>9,.2f} | flat ${flat:>9,.2f}")

Run it with python3 dx_cost_model.py. Output from my run:

flat: $8,000.80/mo | payg floor: $3,285.00/mo
break-even: 236 TB/mo = 7.2% of one 10G port
  10 TB -> payg $ 3,485.00 | flat $ 8,000.80
  50 TB -> payg $ 4,285.00 | flat $ 8,000.80
 100 TB -> payg $ 5,285.00 | flat $ 8,000.80
 236 TB -> payg $ 8,005.00 | flat $ 8,000.80
 400 TB -> payg $11,285.00 | flat $ 8,000.80

Two caveats the script cannot fix. The $0.02/GB figure applies to the US-to-US, Canada, and Europe rows; swap in your own source row from the pricing page. And Tier 1 covers only the local metro's Region, so a multi-Region design may need a higher tier at a higher rate. The flat-rate guide's own example: a 100 Gbps pair at SFO serving us-east-1 and eu-central-1 sits in Tier 2 for one and Tier 3 for the other.

Step 2: find locations. Pick where your cross-connect would land:

import boto3

dx = boto3.client("directconnect", region_name="us-east-1")
for loc in dx.describe_locations()["locations"]:
    print(loc["locationCode"], loc["locationName"], loc["region"],
          loc.get("availablePortSpeeds"))

describe_locations returns the location code you pass to create_connection later, the Region it homes to, and the port speeds on offer. Filter by the Region where your workloads live; the location's home Region is what the flat-rate tier is measured against.

Step 3: the AWS-side core. Create a virtual private gateway, attach it to a VPC, create a DXGW, and associate them:

import boto3

REGION, VPC_ID = "us-east-1", "vpc-0123456789abcdef0"   # your VPC
ec2 = boto3.client("ec2", region_name=REGION)
dx = boto3.client("directconnect", region_name=REGION)

vgw = ec2.create_vpn_gateway(Type="ipsec.1")["VpnGateway"]["VpnGatewayId"]
ec2.attach_vpn_gateway(VpcId=VPC_ID, VpnGatewayId=vgw)

gw = dx.create_direct_connect_gateway(
    directConnectGatewayName="sl-demo-dxgw",
    amazonSideAsn=64512)["directConnectGateway"]["directConnectGatewayId"]

assoc = dx.create_direct_connect_gateway_association(
    directConnectGatewayId=gw,
    gatewayId=vgw,
    addAllowedPrefixesToDirectConnectGateway=[{"cidr": "10.0.0.0/16"}])
print(gw, vgw, assoc["directConnectGatewayAssociation"]["associationState"])

The cidr list is the set of VPC prefixes AWS will advertise to your router over BGP. It is the allow-list you will tune most often, and it is what keeps you under the 100-route default. Your on-premises ASN will live on the VIF, not here; the Amazon-side ASN set on the DXGW must differ from it, per the VIF rules (you cannot use the same ASN for the customer gateway and the virtual gateway or DXGW on one VIF).

When a port exists, the VIF is one call, from the AWS CLI reference shape: aws directconnect create-private-virtual-interface --connection-id dxcon-xxxx --new-private-virtual-interface virtualInterfaceName=prod,vlan=101,asn=65000,mtu=9001,addressFamily=ipv4,directConnectGatewayId=<gw>. VLAN is 1 to 4094, unique on the connection, and cannot be changed after creation. MD5 BGP authentication is on by default and cannot be disabled.

Verify it works

For the AWS-side core, poll the association until it is associated:

import time
for _ in range(20):
    a = dx.describe_direct_connect_gateway_associations(
        directConnectGatewayId=gw)["directConnectGatewayAssociations"][0]
    print(a["associationState"])
    if a["associationState"] == "associated":
        break
    time.sleep(15)

Expected: associating a few times, then associated. For a real connection, the verification is different and it is the part most teams skip: ask AWS to bring down the BGP session on one connection through the Resiliency Toolkit's failover test, and confirm your traffic moves to the redundant VIF. If you have never watched traffic fail over, you do not have a redundant design; you have a diagram.

What the SLA is actually buying

The Direct Connect SLA defines three deployment shapes, and the credit you get depends entirely on which one you built. Multi-Site Redundant requires at least four connections across two locations, two per location on unique AWS devices, plus Enterprise Support and a completed Well-Architected Review; credits start when monthly uptime falls below 99.99 percent. Multi-Site Non-Redundant needs at least two connections across two locations and Enterprise Support, with a 99.9 percent threshold. A Single Connection starts crediting below 95.0 percent, and AWS does not recommend it for production.

Read the credit line: 10, 25, then 100 percent of the port-hour charge for the affected connections, issued only above $1, non-refundable, and the sole remedy. It is a refund of port-hours, not compensation for downtime. Unavailability means a complete inability to send or receive for at least 120 consecutive seconds. Excluded: anything past the demarcation point, your equipment, your configuration, and hosted connections accessed through a partner. The toolkit docs map this to design names: maximum resiliency targets 99.99 percent, high resiliency 99.9 percent, and development and test covers device failure in a single location only. The SLA is a design checklist more than an insurance policy.

When it breaks

VIF stuck in down with BGP idle. Either the BGP MD5 key does not match, the VLAN tag does not match what AWS has, or you advertised more prefixes than your limit and AWS dropped the session. Check prefix count first; it is the silent one.

Traffic flows one way. Your router learned AWS routes but AWS does not know yours, usually because the DXGW association's allowed prefixes do not include the on-premises-reachable range, or an asymmetric route points back through the internet.

Throughput stops at a number that is not your port speed. MTU mismatch. Jumbo frames need to be enabled consistently, and switching a VIF to jumbo can disrupt every VIF on that connection for up to 30 seconds. If two routes for the same prefix come in with different MTUs, or a VPN advertises the same route, the path uses 1500.

The flat-rate bill does not drop. Check that the tier covers the source Region of your traffic. Out-of-tier DTO is billed at standard rates, and multi-Region DXGW or Transit Gateway designs hide this.

Failover works on paper only. Both connections landed on the same AWS device, or the same location. The toolkit prevents same-device terminations; a hand-ordered classic connection does not.

Cleanup

Delete in reverse order, and make sure no VIF still references the gateway:

dx.delete_direct_connect_gateway_association(
    directConnectGatewayId=gw, virtualGatewayId=vgw)
# wait until the association is gone, then:
dx.delete_direct_connect_gateway(directConnectGatewayId=gw)
ec2.detach_vpn_gateway(VpcId=VPC_ID, VpnGatewayId=vgw)
ec2.delete_vpn_gateway(VpnGatewayId=vgw)

Deleting a real connection stops port-hours; remember the cross-connect at the colo is billed by the colo and has its own termination process. For this tutorial's resources the bill was $0 and stays $0.

The decision

If your steady-state outbound volume is under about 236 TB a month on 10 Gbps, stay pay-as-you-go and spend the savings on a second location. If you are above it, flat-rate buys you a budget that no longer moves with traffic. Either way, the cable is the cheap part; the BGP session and the second path are the product.

Sources