AWS Lambda in VPC: RDS Access, NAT Gateway vs Interface Endpoints, and Cold Start Impact
Run AWS Lambda functions inside a VPC to access RDS and private resources. Covers VPC configuration, security groups, NAT gateway vs VPC interface endpoints for internet access, cold start impact, and Terraform setup.
Lambda functions run outside your VPC by default — they have internet access but can't reach private resources like RDS, ElastiCache, or internal services. Placing Lambda inside a VPC solves that, but introduces new problems: cold starts increase by 1–3 seconds during ENI (Elastic Network Interface) attachment, and internet access requires either a NAT gateway or VPC endpoints.
This guide covers VPC Lambda architecture, the right security group rules, and how to eliminate the cold start penalty.
When Lambda Needs a VPC
| Need | Solution |
|---|---|
| Access RDS PostgreSQL/MySQL | VPC required |
| Access ElastiCache Redis | VPC required |
| Access internal microservices | VPC required |
| Access DynamoDB | VPC endpoint or public (no VPC needed) |
| Access S3 | VPC endpoint or public (no VPC needed) |
| Access Secrets Manager | VPC endpoint (recommended) |
| Internet access from VPC Lambda | NAT gateway required |
Architecture Overview
VPC (10.0.0.0/16)
├── Private Subnets (10.0.1.0/24, 10.0.2.0/24) — Lambda + RDS
│ ├── Lambda function (ENI attached per AZ)
│ └── RDS PostgreSQL (Multi-AZ)
├── Public Subnets (10.0.101.0/24, 10.0.102.0/24)
│ └── NAT Gateway (for Lambda internet access)
└── VPC Endpoints
├── Secrets Manager (interface endpoint — no NAT needed)
├── S3 (gateway endpoint — free)
└── DynamoDB (gateway endpoint — free)
☁️ Is Your Cloud Costing Too Much?
Most teams overspend 30–40% on cloud — wrong instance types, no reserved pricing, bloated storage. We audit, right-size, and automate your infrastructure.
- AWS, GCP, Azure certified engineers
- Infrastructure as Code (Terraform, CDK)
- Docker, Kubernetes, GitHub Actions CI/CD
- Typical audit recovers $500–$3,000/month in savings
Terraform: VPC Lambda Setup
# terraform/lambda-vpc.tf
# Security group for Lambda
resource "aws_security_group" "lambda" {
name = "${var.app_name}-lambda-${var.environment}"
vpc_id = var.vpc_id
description = "Lambda function security group"
# No inbound rules needed — Lambda is invoked by AWS, not by incoming connections
# Outbound: allow Lambda to connect to RDS
egress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.rds.id]
description = "PostgreSQL access"
}
# Outbound: allow Lambda to call VPC endpoints (HTTPS)
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # Needed for VPC endpoints + NAT gateway internet
description = "HTTPS outbound (VPC endpoints + internet via NAT)"
}
# Outbound: allow DNS resolution
egress {
from_port = 53
to_port = 53
protocol = "udp"
cidr_blocks = [var.vpc_cidr]
description = "DNS"
}
tags = { Name = "${var.app_name}-lambda-${var.environment}" }
}
# Security group for RDS: allow inbound from Lambda only
resource "aws_security_group" "rds" {
name = "${var.app_name}-rds-${var.environment}"
vpc_id = var.vpc_id
description = "RDS security group — Lambda access only"
ingress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.lambda.id]
description = "PostgreSQL from Lambda"
}
# No direct internet access to RDS
tags = { Name = "${var.app_name}-rds-${var.environment}" }
}
# Lambda function with VPC config
resource "aws_lambda_function" "app" {
function_name = "${var.app_name}-${var.environment}"
role = aws_iam_role.lambda.arn
package_type = "Image"
image_uri = "${var.ecr_repo}:${var.image_tag}"
timeout = 30
memory_size = 1024
architectures = ["arm64"]
vpc_config {
# Deploy to multiple AZs for high availability
# Lambda creates one ENI per AZ for each function version in use
subnet_ids = var.private_subnet_ids # Private subnets (no internet)
security_group_ids = [aws_security_group.lambda.id]
}
environment {
variables = {
NODE_ENV = var.environment
AWS_REGION = var.aws_region
}
}
# Secrets injected from Secrets Manager at invocation
# (requires execution role with secretsmanager:GetSecretValue)
}
# Lambda requires ENI management permissions
resource "aws_iam_role_policy_attachment" "lambda_vpc" {
role = aws_iam_role.lambda.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole"
}
Cold Start: The ENI Problem (Pre-2020 vs Today)
Before 2020, VPC Lambda had notorious 10–30 second cold starts caused by ENI creation per function instance. AWS fixed this in 2020 with pre-created ENIs shared across functions. Today's cold start impact is much smaller.
Current cold start breakdown for VPC Lambda (2027):
| Component | Non-VPC Lambda | VPC Lambda |
|---|---|---|
| ENI attachment | 0ms | ~0ms (pre-created) |
| Container init | 200–500ms | 200–500ms |
| Runtime init | 100–300ms | 100–300ms |
| Code init | 50–200ms | 50–200ms |
| Total typical cold start | 350–1,000ms | 400–1,100ms |
The VPC penalty is now typically 50–100ms, not 10–30 seconds. For most applications, VPC Lambda is fine.
Remaining cold start mitigation strategies:
// 1. Initialize DB connection outside handler (reused on warm invocations)
import { Pool } from "pg";
// Created once per Lambda instance, reused across invocations
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 1, // Lambda: only 1 concurrent execution per instance
idleTimeoutMillis: 300_000, // Keep connection for 5 minutes
});
export const handler = async (event: any) => {
// pool is already initialized — no connection overhead on warm invocations
const { rows } = await pool.query("SELECT $1::text AS message", ["hello"]);
return { message: rows[0].message };
};
# 2. Provisioned concurrency (pre-warm instances — no cold start)
resource "aws_lambda_provisioned_concurrency_config" "app" {
count = var.environment == "production" ? 1 : 0
function_name = aws_lambda_function.app.function_name
qualifier = aws_lambda_alias.live.name
provisioned_concurrent_executions = 2 # Keep 2 warm instances
# Cost: ~$25/month per 1GB instance running 24/7
}
⚙️ DevOps Done Right — Zero Downtime, Full Automation
Ship faster without breaking things. We build CI/CD pipelines, monitoring stacks, and auto-scaling infrastructure that your team can actually maintain.
- Staging + production environments with feature flags
- Automated security scanning in the pipeline
- Uptime monitoring + alerting + runbook automation
- On-call support handover docs included
VPC Endpoints: Avoid NAT Gateway Costs
NAT gateway charges: $0.045/hour (~$32/month) + $0.045/GB processed. For Lambda functions that call Secrets Manager, S3, or DynamoDB heavily, VPC endpoints save money and remove the internet dependency.
# terraform/vpc-endpoints.tf
# Gateway endpoints: FREE (S3 and DynamoDB only)
resource "aws_vpc_endpoint" "s3" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids
tags = { Name = "${var.app_name}-s3-endpoint" }
}
resource "aws_vpc_endpoint" "dynamodb" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.dynamodb"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids
}
# Interface endpoints: $7.30/AZ/month each
# Worth it if NAT gateway traffic would cost more
# Secrets Manager: Lambda fetches secrets without internet
resource "aws_vpc_endpoint" "secrets_manager" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.secretsmanager"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.vpc_endpoints.id]
private_dns_enabled = true # Lambda calls secretsmanager.region.amazonaws.com — resolved to private IP
tags = { Name = "${var.app_name}-secrets-endpoint" }
}
# SSM Parameter Store
resource "aws_vpc_endpoint" "ssm" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.ssm"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.vpc_endpoints.id]
private_dns_enabled = true
}
# SQS (if Lambda consumes from SQS)
resource "aws_vpc_endpoint" "sqs" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.sqs"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids
security_group_ids = [aws_security_group.vpc_endpoints.id]
private_dns_enabled = true
}
# Security group for VPC endpoints: allow HTTPS from Lambda
resource "aws_security_group" "vpc_endpoints" {
name = "${var.app_name}-vpc-endpoints"
vpc_id = var.vpc_id
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
security_groups = [aws_security_group.lambda.id]
description = "HTTPS from Lambda"
}
}
Decision: NAT Gateway vs VPC Endpoints
Lambda calls: Secrets Manager, SSM, SQS, S3, DynamoDB (AWS services only)
→ Use VPC endpoints. No NAT gateway needed. Cheaper, no internet dependency.
Lambda calls: External APIs (Stripe, Resend, OpenAI, etc.)
→ Need NAT gateway. VPC endpoints only work for AWS services.
Lambda calls: Both external APIs AND AWS services
→ NAT gateway + VPC endpoints for S3/DynamoDB (gateway endpoints are free).
Interface endpoints for heavy AWS service callers.
Connection Pooling: PgBouncer for Lambda
Lambda with VPC + RDS: each Lambda instance opens its own connection. At scale (100 concurrent Lambda instances), you can exhaust RDS max_connections.
# RDS Proxy: connection pooling managed by AWS
resource "aws_db_proxy" "postgres" {
name = "${var.app_name}-proxy"
debug_logging = false
engine_family = "POSTGRESQL"
idle_client_timeout = 1800
require_tls = true
role_arn = aws_iam_role.rds_proxy.arn
auth {
auth_scheme = "SECRETS"
iam_auth = "REQUIRED" # IAM auth — no password in connection string
secret_arn = var.db_secret_arn
}
vpc_security_group_ids = [aws_security_group.rds_proxy.id]
vpc_subnet_ids = var.private_subnet_ids
}
resource "aws_db_proxy_default_target_group" "postgres" {
db_proxy_name = aws_db_proxy.postgres.name
connection_pool_config {
max_connections_percent = 70 # Use up to 70% of RDS max_connections
max_idle_connections_percent = 50
connection_borrow_timeout = 120 # Wait up to 2 min for connection
}
}
# Lambda connects to proxy endpoint, not RDS directly
# DATABASE_URL = postgres://user@proxy-endpoint.proxy-xxx.us-east-1.rds.amazonaws.com:5432/db
Cost Comparison
| Architecture | Monthly Cost (typical) |
|---|---|
| Lambda without VPC | ~$0 networking |
| Lambda + VPC + NAT Gateway | $32+ (NAT) + data transfer |
| Lambda + VPC + VPC Endpoints only | $7.30/endpoint/AZ |
| Lambda + RDS Proxy | $0.015/vCPU-hour of RDS |
Rule of thumb: If your Lambda functions call more than 10GB/month through NAT gateway, replacing with VPC endpoints saves money.
See Also
- AWS ECS Fargate Production Setup
- AWS Lambda Container Images
- AWS Lambda Cold Start Optimization
- AWS RDS Aurora Serverless
- Terraform State Management
Working With Viprasol
Lambda-in-VPC architecture has real tradeoffs: you gain access to private resources but add networking complexity. Our team designs VPC Lambda architectures with the right security groups, interface endpoints for heavy AWS service callers (avoiding NAT gateway costs), RDS Proxy for connection pooling, and provisioned concurrency where cold starts matter.
What we deliver:
- Lambda + RDS security groups with minimal egress (port 5432 to RDS SG only)
- VPC endpoint configuration: S3/DynamoDB (gateway, free) + Secrets Manager/SQS (interface)
- RDS Proxy setup for Lambda connection pooling at scale
- Decision matrix: when to use NAT gateway vs VPC endpoints
- Terraform module with all components (lambda, SGs, endpoints, proxy)
Talk to our team about your Lambda networking architecture →
Or explore our cloud infrastructure services.
About the Author
Viprasol Tech Team
Custom Software Development Specialists
The Viprasol Tech team specialises in algorithmic trading software, AI agent systems, and SaaS development. With 100+ projects delivered across MT4/MT5 EAs, fintech platforms, and production AI systems, the team brings deep technical experience to every engagement. Based in India, serving clients globally.
Need DevOps & Cloud Expertise?
Scale your infrastructure with confidence. AWS, GCP, Azure certified team.
Free consultation • No commitment • Response within 24 hours
Making sense of your data at scale?
Viprasol builds end-to-end big data analytics solutions — ETL pipelines, data warehouses on Snowflake or BigQuery, and self-service BI dashboards. One reliable source of truth for your entire organisation.