One thing you may be doing wrong (which is exactly what I did wrong but got a hint about from @Ramratan Gupta above) is to put the NAT gateway in the private subnet and not in the public.
The NAT gateway provides your private ip Lambda function with internet connectivity and so it sort of stands to reason the NAT gateway needs to have internet access itself and be in the Public network.
To summarise:
- You have to create at least two subnets: one public and one private. Put both in the same VPC.
- What makes a subnet public or private is the 0.0.0.0/0 route.
- You need to have both an internet gateway (igw) and a NAT gateway (ngw).
- To create the NAT gateway, you need an Elastic IP. Also, and here's what I did wrong, you need to create the NAT gateway in the PUBLIC network.
- The igw goes in the same subnet that the NAT gateway is: the public one.
- You need to have two routes: one with the 0.0.0.0/0 route referencing the igw and the other referencing the ngw.
- You then need to associate the NAT-route containing route table with the private subnet and the igw-route containing route table with the public subnet. Please note that until you do this association, there is no difference between the subnet that would make one public and one private. You make one private and the other public by how you associate the subnets with the route tables.
- Finally, you need to connect your Lambda function to the VPC and also to the private subnet only. This way, when your lambda tries to reach the internet, it goes to the private subnet, looks for the 0.0.0.0/0 route, which directs it to the NAT gateway that is in the public subnet. The NAT gateway then contacts the internet using the igw (because they are in the same subnet) and upon receiving a response, returns it to your Lambda function.
- Please also note that there are some permissions your Lambda function needs (that AWS will warn you about) in order to connect to a VPC.