An interface endpoint puts an elastic network interface for Bedrock inside your own subnets, so requests leave your instances and land on a private address instead of going out through a NAT gateway. The creation is three commands. The part worth getting right is confirming that traffic is genuinely using it, because the failure mode is not an error — it is everything continuing to work over the internet.
Which endpoint service you need
Bedrock is not one API. AWS documents separate interface endpoint services, and choosing the wrong one produces a private path for calls you are not making:
-
com.amazonaws.region.bedrock-runtime— inference.InvokeModel,Converseand their streaming forms. This is the one an application needs. -
com.amazonaws.region.bedrock— the control plane. Model listing, customisation jobs, guardrail management. A deployment pipeline needs this; a request path does not. -
com.amazonaws.region.bedrock-agentandcom.amazonaws.region.bedrock-agent-runtime— agent build-time and agent runtime, separately. -
com.amazonaws.region.bedrock-mantle— the Mantle API surface, resolving to a.api.awshostname rather than.amazonaws.com.
There are also bedrock-fips and bedrock-runtime-fips variants, which Amazon documents as available in a specific short list of Regions rather than everywhere.
The service-name list is the volatile part of this page — bedrock-mantle was not in it when the first Bedrock endpoints shipped. Before building, run aws ec2 describe-vpc-endpoint-services --filters Name=service-name,Values="*bedrock*" in your Region: that command answers for the day you run it, which the docs and this page cannot. Amazon’s PrivateLink page for Bedrock holds the current list.
Creating the endpoint
- Confirm the VPC has both
enableDnsSupportandenableDnsHostnamesset. Private DNS for an interface endpoint silently requires both, and without them the endpoint is created and never used. - Create a security group whose inbound rule allows TCP 443 from the CIDR of the subnets that will call Bedrock. The endpoint’s security group governs traffic to the ENI, and a default group that only allows itself will refuse your Lambda.
- Create the endpoint in at least two Availability Zones.
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc123 \
--vpc-endpoint-type Interface \
--service-name com.amazonaws.us-east-1.bedrock-runtime \
--subnet-ids subnet-0aaa111 subnet-0bbb222 \
--security-group-ids sg-0ccc333 \
--private-dns-enabled
In Terraform the same resource is aws_vpc_endpoint with vpc_endpoint_type = "Interface", service_name, subnet_ids, security_group_ids and private_dns_enabled = true. The general treatment of Terraform for AI infrastructure covers how to keep the subnet list from drifting between environments.
The DNS check that proves it
With private DNS enabled, nothing in your code changes: the SDK still resolves bedrock-runtime.us-east-1.amazonaws.com, and the Route 53 Resolver inside the VPC now answers with the endpoint’s private addresses instead of the public ones. That is exactly why you have to check — success and failure look identical from the application.
From an instance or a container in one of those subnets:
# Working: private addresses from the subnet CIDR
$ dig +short bedrock-runtime.us-east-1.amazonaws.com
10.0.12.47
10.0.28.113
# Not working: a public address. Traffic is going out through NAT.
$ dig +short bedrock-runtime.us-east-1.amazonaws.com
52.94.xxx.xxx
Run it from inside the VPC. Running it from your laptop always returns public addresses and proves nothing. If you cannot get a shell in the subnet — the usual case with Lambda — do the lookup in the function itself and log it, or call the endpoint-specific hostname directly to bypass DNS entirely. Amazon documents that form as https://vpce-id.bedrock-runtime.region.vpce.amazonaws.com, passed as endpoint_url to the boto3 client or --endpoint-url to the CLI. It is the right tool for a diagnostic and the wrong one for production code, because it hardcodes an endpoint id that changes if the endpoint is recreated.
The second confirmation is negative and stronger: remove the NAT gateway route, or point the subnet’s default route at nothing, and see whether inference still succeeds. A private path that survives the removal of the public one is a private path.
The endpoint policy
An interface endpoint carries a resource policy, and the default one allows full access to the service through it. Narrowing it gives you a network-attached control that no identity policy can grant around — useful precisely because it is evaluated separately:
{
"Version": "2012-10-17",
"Statement": [
{
"Principal": "*",
"Effect": "Allow",
"Action": [
"bedrock:InvokeModel",
"bedrock:InvokeModelWithResponseStream"
],
"Resource": "*"
}
]
}
Two cautions. This policy lists two actions, so a call to Converse through this endpoint is denied — a denial that appears nowhere in the caller’s IAM policy and is invisible to simulate-principal-policy. And a policy that scopes Resource to specific model ARNs here duplicates a decision you are probably already making in the role, so keep one of the two authoritative rather than maintaining both.
The Availability Zone trap
An interface endpoint is not one thing. It is one elastic network interface per subnet you enabled, and the subnets you pass to --subnet-ids determine which Availability Zones get one. This produces the most confusing failure in this whole setup, because it is intermittent rather than total.
Consider a Lambda function attached to three subnets in three AZs, and an endpoint created in two of them — a very easy thing to do when the function’s subnet list grew after the endpoint was written. Lambda places execution environments across all three. Two thirds of your cold environments resolve the endpoint and work. The remaining third has no local ENI, and what happens next depends on your networking: with private DNS enabled the resolver still returns the endpoint addresses, so traffic crosses AZ boundaries and works but incurs cross-AZ data charges; with a security group or NACL that only permits within-AZ CIDRs it hangs until the socket times out. Either way the symptom is “Bedrock is flaky” and the cause is a subnet list, which no amount of reading IAM policies will reveal.
Confirm the actual placement rather than the intent:
aws ec2 describe-vpc-endpoints \
--filters Name=service-name,Values=com.amazonaws.us-east-1.bedrock-runtime \
--query 'VpcEndpoints[].{Id:VpcEndpointId,State:State,DNS:PrivateDnsEnabled,Subnets:SubnetIds}'
Check three fields. State must be available, not pending or pendingAcceptance — an endpoint that never finished provisioning looks created in the console. PrivateDnsEnabled must be true if you expect zero code changes. And SubnetIds must be a superset of every subnet any caller runs in. That last comparison is worth encoding in your IaC by feeding the endpoint and the function from the same subnet variable, so the two lists cannot drift apart in the first place.
The cost model follows the same per-AZ structure and is worth stating because it is the argument you will be asked for. AWS bills interface endpoints per endpoint-hour per Availability Zone, plus a charge per GB of data processed. So a three-AZ endpoint costs three times the hourly rate of a one-AZ endpoint, and the trade against a NAT gateway is not automatically favourable — a NAT gateway you keep for other egress bills its own hourly rate regardless. Check the current per-hour and per-GB figures on AWS’s PrivateLink pricing page for your Region rather than assuming; both numbers move, and the honest justification for this endpoint is usually the network boundary rather than the bill.
What the endpoint does not cover
A bedrock-runtime endpoint privatises inference and nothing else. Everything adjacent still needs its own path:
- Model invocation logging delivers to CloudWatch Logs or S3. Those are separate endpoints — and the S3 one is a gateway endpoint, configured on the route table rather than as an ENI.
- Secrets and parameters. A function in a VPC calling Secrets Manager needs a Secrets Manager endpoint; the Lambda extension has the same requirement, because it makes the API call the SDK would have made.
- Knowledge bases reach a vector store and an S3 bucket, both outside this endpoint.
- Any non-AWS model provider. An interface endpoint is an AWS PrivateLink construct for an AWS service. Traffic to a third-party inference API still needs egress, which is the thing people are usually surprised by after switching a route table off.
Top comments (0)