Connect to private resources from API Gateway with VPC Link

19/04/2018 Vincent Van der Kussen

Until recently it was not possible to reach services in your VPC’s private subnets from API  Gateway. This recently changed and API Gateway now supports Endpoints to Private
VPC’s . This means you can reach services in Private VPC’s without using custom headers or Lambda Proxy’s.

In this example we will create an API in API Gateway with some resources . When we access these resources, API Gateway will contact the backend which runs in a private subnet and returns the response back to the client.

The following endpoints will be created:

  • / (returns the status of the API)
  • /header (returns the host header of the request)
  • /foo/* (example to proxy everything under /foo to the backend)

To establish this connection a Network Load Balancer (NLB) will be used. A Network Load Balancer operates at level 4 of the OSI model and therefore only uses the TCP protocol. We will use HTTP to communicate with our service In this example we use Nginx as a proxy to our application.

Network Load Balancer

Open the AWS EC2 console and create a new Load Balancer of the type Network Load Balancer

  • Give it a name and choose Internal Facing (we don’t want to expose it publicly).
  • Select TCP and Choose port 80 (this is the tcp port where we will receive connections on).
  • In Availability Zones select the VPC and subnets where you want to deploy the NLB.

Configure routing

In the next step we need to create a Target Group (or use an existing one). In this example we will create a new target group. In the Health Check configuration we’ll use the same port as the traffic port.

NOTE: If your backend service is listening on another tcp port you’ll need to adjust this so that the health check doesn’t fail.

Register targets

In the next screen select the instances where you want to send traffic to. You can also skip this and add the instances later.

NOTE: Instead of a single instance you could also attach the Target Group to an Auto Scaling Group.

API Gateway

Now that we have our NLB configured we can configure our API Gateway.

Configure the VPC Link in API gateway

Open the API Gateway console and choose VPC Links. Create a new VPC Link and select the Target NLB we created earlier.

Create the API

Now we can create our API. The fasted way is to create a Swagger file and import that.

Example Swagger file:

Once created you should see your API Resources

Create VPCLink

To create the actual connection to our private subnets we need to create a VPC Link. In the API Gateway console and choose VPC Links. Create a new VPC Link and select the Target NLB we created earlier.

Deploy API

We can now deploy the API by running APIs => <your api name> => Actions => Deploy API . Choose a name for the stage (in this example we use ‘test’ ).

Once deployed you will see a ‘test’ stage under Stages. Navigate to the Stage Variables tab and add a new variable with the name vpcLinkId  and the value is the ID of the VPCLink we created earlier.

Create Custom Domainname

Because we are using Virtual Hosts in Nginx based on the incoming url we will have to make the API available on that URL. To do this we need to create a Custom Domain Name in the API Gateway Console.
In this example we are using

NOTE: Because Cloudfront is used for this, it can take a while until the Cloudfront Distribution is deployed.

We will also need to create a DNS record in Route 53 that will point to this Cloudfront Distribution. Use the Target Domain Name value.


The backend application used in this example is a simple Python Flask application which exposes some API endpoints:

Python Flask app:

The application is proxied by Nginx.

NOTE: The server_name parameter is important as API Gateway will send the Endpoint URL configured in the Integration Request as host header.

Nginx config:

Now let’s see if everything works.




If you want to use API Gateway but don’t want or can’t expose your backend services publicly VPC Link can be a good solution. However you’ll still need to find a solution to proxy the requests and do TLS/SSL offloading. Of course it will probably be a matter of time before AWS supports this natively.


Share this AWSome post

Comments (3)

  1. Bojan

    Nice post. Really helpfull. I have case when I have deployed microservices in ecs cluster and I want to access them via api gateway and vpc link. Everythings work well except that every third or second request tooks 5 – 10 second. Sometimesbit takes only 20ms. I tested this with url provided from api gateway after deploy. Should I use domain instead? Will this help to solve performance issue.

    • Vincent Van der Kussen

      Hi Bojan,

      I’m glad you liked the post and that it helped you out. The delays you are experiencing is something I haven’t seen where we implemented the setup as described in the blogpost.
      Are you sure your service on ECS is always responding within the required period?

      The API Gateway url should work without delay if you are testing with a few requests. Adding a Custom Domain adds a Cloudfront distribution which might help but I would suggest to check if there are no problems on your backend (ALB -> ECS -> Service). AWS X-ray might be a good tool to troubleshoot your issue.

  2. Stefan Tobé

    Hi Vincent,
    I also experience intermittent delays with lambda services.
    I have been told that whenever the time between two subsequent https calls to these lambda microservices exceed certain time (like 10 minutes) then the connection to the lambda service is disposed of by the vpc or aws infra. Most likely if you handle microservice requests within 5 minutes or so of each other as a maximum you should not experience this issue. Since clients will randomly generate requests one cannot expect every request to come in within 5 minutes so our external (internet facing) monitoring tool nagios is performing dummy lookups to these microserivces in lambda every 5 minutes in order to keep the services (and aws infra) ‘hot standby’

Leave a Reply

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