,

Amazon Bedrock Private Access with PrivateLink and VPC Endpoints (AWS Gen AI Series, Part 9)

Bedrock traffic leaves your VPC by default. Here is how I wire it shut with PrivateLink interface endpoints, private DNS, and a scoped endpoint policy, plus what it costs per month.

AWS Gen AI Series · Part 9 of 30

Call Bedrock from an EC2 instance or a Lambda in a private subnet with default settings, and the request does not stay inside AWS the way most people assume. The SDK resolves the public bedrock-runtime DNS name, the packet walks out through a NAT gateway or an internet gateway, and it reaches the service over the public path. Traffic is encrypted with TLS, so this is not a leak. It is still a problem for anyone who has to prove, on paper, that model traffic never touches the internet. This part closes that path with an interface VPC endpoint, and shows what the setup costs once it is running.

Who this is for: You have Bedrock working from Part 1 through Part 8 and now need model calls to stay off the public internet. You know what a VPC, a subnet, and a security group are. You do not need prior PrivateLink experience. I define each piece as it comes up, and the deep IAM and KMS work waits for Part 10.
Key takeaways: Bedrock has no free gateway endpoint. Private access uses interface endpoints powered by AWS PrivateLink, billed per Availability Zone per hour. Most workloads need two: bedrock for control-plane calls and bedrock-runtime for inference. Turn on private DNS so application code does not change. Scope the endpoint policy and add an IAM aws:sourceVpce condition so a call only succeeds through the endpoint. Budget roughly 7 dollars per endpoint per AZ per month before data.

Your default Bedrock traffic leaves the VPC

A VPC, short for virtual private cloud, is your own isolated network inside a Region. When code in that network calls an AWS service, the request needs a route to reach it. For most services the default route is public: the service has a public DNS name and a public IP range, and your subnet reaches it through an internet gateway or a NAT gateway. Nothing about that is insecure by itself. The bytes are encrypted and AWS authenticates the caller with SigV4 request signing. What you lose is a hard guarantee. You cannot tell an auditor that a Bedrock request is physically incapable of egressing to the internet, because with the default route it can.

An interface VPC endpoint changes the route. It puts an entry point for Bedrock directly inside your subnets, so the request never leaves the Amazon network. This matters for regulated workloads, for accounts that run with no NAT gateway at all, and for a data perimeter where you block public egress and only allow named endpoints. It builds on the shared responsibility split from Part 2: AWS secures the service, and the network boundary around your call is your job.

Two paths from your subnet to BedrockDefault route leaves AWS. Interface endpoint keeps the call inside the network.DEFAULT PATHLambda inprivate subnetNAT gatewaythen internetPublic BedrockendpointPRIVATE PATHLambda inprivate subnetInterfaceendpoint (ENI)Bedrock overPrivateLinkstays insideAWS
The default route can egress through NAT. The interface endpoint terminates the call on a network interface in your own subnet.

What an interface endpoint builds in your subnets

When you create an interface endpoint, AWS drops an elastic network interface, an ENI, into each subnet you select. An ENI is a virtual network card with a private IP from that subnet. Your DNS resolution for the Bedrock name points at those private IPs, so the traffic lands on the ENI and rides PrivateLink to the service. You pick which Availability Zones to cover by choosing one subnet per AZ. Cover three AZs and you get three ENIs for that one endpoint, which is both the resilience story and the cost story, because billing is per AZ.

Two things trip people here. First, the endpoint has its own security group. That group controls who may reach the ENI, and it needs to allow inbound TCP 443 from the security groups your callers use. Miss that and the call does not fail cleanly. It hangs. Second, Bedrock is an interface-only service. S3 offers a free gateway endpoint, and people assume Bedrock has one too. It does not. Every Bedrock endpoint is a paid interface endpoint.

Gotcha: A missing security group rule on the endpoint produces a connection timeout, not an access-denied error. The SDK sits until it hits the socket timeout, then raises ConnectTimeoutError. Engineers chase IAM for an hour because the word timeout does not sound like a permissions problem. Check the endpoint security group first: it must allow 443 inbound from the caller security group.

Which Bedrock endpoints do you actually need?

Bedrock splits its API across separate PrivateLink service names, and each one is a separate endpoint you pay for. If you only run inference, one endpoint covers it. If your app also lists models or reads configuration, you need the control-plane endpoint too. Agents and Knowledge Bases pull in more. Here is the map I use when sizing an account.

Service name suffixWhat it carriesPrivate DNS name
bedrockControl plane: model access, list models, configbedrock.region.amazonaws.com
bedrock-runtimeInference: InvokeModel, Converse, streamingbedrock-runtime.region.amazonaws.com
bedrock-agentAgent and Knowledge Base build-timebedrock-agent.region.amazonaws.com
bedrock-agent-runtimeAgent invocation, retrieve for RAGbedrock-agent-runtime.region.amazonaws.com
bedrock-runtime-fipsFIPS 140 validated inference endpointbedrock-runtime-fips.region.amazonaws.com

The FIPS endpoints exist in us-east-1, us-east-2, us-west-2, ca-central-1, us-gov-east-1, and us-gov-west-1. A Knowledge Base also needs an S3 gateway endpoint, which is free, plus an interface endpoint for the vector store such as OpenSearch Serverless.

My default for a plain inference workload is two endpoints, bedrock and bedrock-runtime. Skip the control-plane one only if your app never touches control-plane calls, which is rarer than it sounds because SDK helpers and health checks often list models on startup.

Private DNS, on or off

When you create the endpoint you get one switch that matters more than any other: enable private DNS name. Turn it on and the standard Regional DNS name, bedrock-runtime.us-east-1.amazonaws.com, resolves to the endpoint ENI inside your VPC. Your application code does not change. The same boto3 client that worked before now routes privately, because the name it already uses points somewhere new. Leave it off and you must pass an explicit endpoint URL in every client, which means touching every service that calls Bedrock. I turn it on in almost every case.

The one time to leave it off is when the VPC already needs the public Bedrock name to resolve publicly for some other reason, because private DNS is VPC-wide and hijacks that name for everything in the VPC. You also cannot enable private DNS on two endpoints for the same service in the same VPC. The second one will refuse.

What private DNS changesApp calls defaultBedrock DNS namePrivate DNS ONresolves to ENIPrivate DNS OFFresolves to publicNo code changeprivate pathPass endpoint_urlin every clientPrivate DNS is VPC-wide. Turning it on redirects the name for every resource in the VPC, not just one app.
Private DNS on is the low-friction default. Off is for the narrow case where the VPC still needs the public name.

Lock the policy to your VPC

An endpoint with private DNS routes traffic privately, but by default it still allows every Bedrock action for every principal. Two controls tighten that. The first is the endpoint policy, a resource policy attached to the endpoint that says which principals may do which actions through it. The second is an IAM policy on your caller with an aws:sourceVpce condition, which says the caller may reach Bedrock only when the request arrives through your endpoint. Use both. The endpoint policy is the gate on the endpoint. The IAM condition stops the same role from calling Bedrock over some other path.

A least-privilege endpoint policy for a single app role and a single model looks like this.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": { "AWS": "arn:aws:iam::111122223333:role/genai-app" },
    "Action": [
      "bedrock:InvokeModel",
      "bedrock:InvokeModelWithResponseStream"
    ],
    "Resource": "arn:aws:bedrock:*::foundation-model/anthropic.claude-sonnet-4-6-v1"
  }]
}

The matching IAM policy on the role pins access to the endpoint. Replace the value with your real endpoint ID.

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["bedrock:InvokeModel"],
    "Resource": "*",
    "Condition": {
      "StringEquals": { "aws:sourceVpce": "vpce-029dea71225152fde" }
    }
  }]
}

My take

Ship the endpoint policy as Allow with the exact actions and model ARNs you use, not a wildcard. It is a second, network-anchored place to enforce which models an app may call, and it survives even if someone loosens the IAM role by mistake. I have caught an over-broad role because the endpoint policy still said no. Defense in depth is only real when the two layers disagree at the right moment.

What does private access cost?

Interface endpoints are billed two ways: a charge for each hour the endpoint is provisioned in each Availability Zone, and a charge for data processed. As of mid 2026, PrivateLink lists 0.01 dollars per AZ per hour and 0.01 dollars per GB for the first petabyte in a Region. A partial hour bills as a full hour, and you pay for every AZ whether or not traffic flows through it. Spread one endpoint across three AZs and you are paying for three ENIs around the clock. That is the number people forget when they scatter endpoints across dev, test, and staging accounts.

ComponentRateQuantityMonthly
bedrock-runtime endpoint0.01 per AZ-hour3 AZ x 730 hr21.90
bedrock control-plane endpoint0.01 per AZ-hour3 AZ x 730 hr21.90
Data processed0.01 per GB300 GB3.00
Total46.80

All figures in US dollars, us-east-1 list price, 730 hours per month. Model inference tokens are billed separately by Bedrock and are not part of this table.

Monthly cost of a two endpoint private setupus-east-1 list price, three AZs, 300 GB processed, US dollars0102030404521.90runtime21.90control3.00data46.80total
Endpoint-hours, not data, drive the bill at this scale. Two endpoints across three AZs is about 44 dollars before a byte moves.

Worked example

A team runs the same two endpoints in four accounts: prod, staging, dev, and a sandbox. Each account covers three AZs. That is 4 accounts x 2 endpoints x 3 AZs = 24 ENIs, at 7.30 dollars each per month, so 175.20 dollars a month in endpoint-hours before any data or inference. The sandbox and dev endpoints sit idle most of the week.

Cutting dev and sandbox to a single AZ, or to the runtime endpoint only, drops those two accounts from 43.80 to about 7.30 dollars each. New monthly total near 102 dollars. Same private guarantee where it counts, 73 dollars a month back.

One endpoint cost by AZ countCost is linear in AZs. Each AZ adds 7.30 dollars a month.07.3014.6021.901 AZ2 AZ3 AZ7.3014.6021.90
Two AZs cover most resilience needs. The third AZ is real money on endpoints that see little traffic.

Failure modes that look like something else

Most private Bedrock incidents I have seen are not Bedrock problems. They are network problems wearing a Bedrock error message. Three recur.

The connection that hangs. Covered above: the endpoint security group does not allow 443 from the caller. The symptom is a timeout, not a denial. Fix the inbound rule.

The call that works but still egresses. You created the endpoint but never enabled private DNS, and you did not pass an endpoint URL either, so the default name still resolves publicly. The app works, which is why nobody notices, and the compliance guarantee you thought you bought is not there. Confirm resolution from inside the subnet: the Bedrock name should return a private IP, not a public one.

The cross-Region surprise. If you use a cross-Region inference profile from Part 8, Bedrock may route the request to a second Region for capacity. Your interface endpoint is Regional. It does not follow the request. For a strict private posture you either avoid cross-Region profiles or stand up endpoints in every Region the profile can reach. Decide this on purpose, because the routing is invisible until an audit asks where a given request ran.

Before you change production: Enabling private DNS is VPC-wide and takes effect for every resource in the VPC at once. Test in a non-production VPC first, verify the Bedrock name resolves to a private IP from inside a subnet, and keep a rollback plan. This is general guidance, not a substitute for your own change process and security review.

Building and testing the endpoint

Here is the whole thing end to end. Create the runtime endpoint across three subnets with private DNS on, then prove the path from a private instance.

aws ec2 create-vpc-endpoint 
  --vpc-endpoint-type Interface 
  --vpc-id vpc-0abc123 
  --service-name com.amazonaws.us-east-1.bedrock-runtime 
  --subnet-ids subnet-0aa subnet-0bb subnet-0cc 
  --security-group-ids sg-0privatelink 
  --private-dns-enabled

# State starts as pending. It flips to available in about 2 minutes.

From a Lambda or an EC2 host in a private subnet, this call now rides the endpoint. No endpoint_url needed once private DNS is on.

import boto3

client = boto3.client('bedrock-runtime', region_name='us-east-1')
resp = client.converse(
    modelId='anthropic.claude-sonnet-4-6-v1',
    messages=[{'role': 'user', 'content': [{'text': 'ping'}]}],
)
print(resp['output']['message']['content'][0]['text'])

# Expected: a short model reply printed to stdout.
# Failure mode: if the endpoint security group blocks 443,
# this hangs, then raises botocore ConnectTimeoutError.
# That is a network fix, not an IAM fix.

To confirm the path is actually private, resolve the name from inside the subnet and check the IP is in your VPC CIDR, or set the endpoint policy to Deny for a moment and watch the same call fail with an access error. When it denies, you have proof the traffic is going through the endpoint and nowhere else.

Two endpoints, private DNS on, policy scoped to the VPC

For a real inference workload I stand up two interface endpoints, bedrock and bedrock-runtime, across two or three AZs depending on how much the app matters. I enable private DNS so no code changes. I attach an Allow endpoint policy scoped to the exact actions and model ARNs, and I pin the caller role with an aws:sourceVpce condition. In non-production accounts I cut to one AZ or the runtime endpoint alone, because idle endpoint-hours are the quiet line item that adds up across accounts. That is the whole recommendation, and it costs about 44 dollars a month in the shape most teams actually run.

Private networking gets your bytes off the internet. It does not encrypt your data at rest or manage who holds the keys. That is Part 10, on data residency, KMS, and the rest of the security story. Go turn on private DNS in a test VPC today and resolve the Bedrock name from a private subnet. If it returns a private IP, you have already done the hard part.

AWS Gen AI Series · Part 9 of 30
« Previous: Part 8  |  Guide  |  Next: Part 10 »

References

AWS docs: interface VPC endpoints for Amazon Bedrock
AWS PrivateLink pricing
AWS ML blog: private access to Amazon Bedrock with PrivateLink
• Related on this site: the vendor-neutral GenAI guide

About The Author


Discover more from Journal of Intelligent Infrastructure – By Dr Pranay Jha

Subscribe to get the latest posts sent to your email.

Leave a Reply

Your email address will not be published. Required fields are marked *

Architect’s Toolkit

About the Author

Dr. Pranay Jha is a Cloud and AI Consultant with 18+ years of experience in hybrid cloud, virtualization, and enterprise infrastructure transformation. He specializes in VMware technologies, multi-cloud strategy, and Generative AI solutions. He holds a PhD in Computer Applications with research focused on Cloud and AI, has published multiple research papers, and has been a VMware vExpert since 2016 and a VMUG Community Leader.

Discover more from Journal of Intelligent Infrastructure - By Dr Pranay Jha

Subscribe now to keep reading and get access to the full archive.

Continue reading