Guide · Managed Flink VPC
Managed Flink VPC Connectivity for MCP Servers — MSK, RDS, Private Subnet Sources
Managed Apache Flink can run either without VPC configuration (using the Managed Flink service network, which can reach public endpoints) or inside your VPC (for private MSK brokers, RDS databases, and OpenSearch domains that are not publicly accessible). The critical constraint when deploying Managed Flink in a VPC is that the service creates ENIs (Elastic Network Interfaces) in the subnets you specify — one per TaskManager per AZ — and all Flink-to-service communication traverses those ENIs. If your subnets are in private AZs without a NAT gateway or VPC endpoints for Kinesis Data Streams, S3, and the Kinesis Analytics control plane, the application will start but fail to read from Kinesis or write checkpoints to S3. The three things you must configure before a VPC-deployed Managed Flink application can work: at least two private subnets in different AZs (Managed Flink requires multi-AZ TaskManager placement), security groups that allow outbound traffic to your data sources on their respective ports, and either a NAT gateway or VPC interface endpoints for every AWS service the application needs to reach.
TL;DR
To access MSK from Managed Flink in a VPC: configure Flink with the same VPC as MSK, add the Flink security group to the MSK cluster's allowed inbound security groups (port 9092 for plaintext, 9094 for TLS, 9098 for IAM auth). For Kinesis Data Streams access from a private subnet, create a VPC Interface Endpoint for kinesis-streams in the same VPC. For S3 checkpoints from a private subnet, create a VPC Gateway Endpoint for S3 (free, routes S3 traffic through the VPC rather than via NAT). Do not use public subnets for Managed Flink — the service requires private subnets with either NAT or VPC endpoints.
VPC configuration requirements
When you enable VPC configuration on a Managed Flink application, you specify a VPC ID, one or more subnet IDs, and one or more security group IDs. Managed Flink provisions ENIs in the specified subnets and attaches the specified security groups to those ENIs. Every network connection the Flink application makes (to Kinesis, MSK, S3, the Managed Flink control plane) originates from one of these ENIs.
| Requirement | Details | Why it matters |
|---|---|---|
| At least 2 subnets in different AZs | Managed Flink distributes TaskManagers across AZs for fault tolerance | If both subnets are in the same AZ, Managed Flink may reject the configuration or fail to start when the AZ has insufficient capacity |
| Private subnets only | AWS recommends private subnets; public subnet deployments have inconsistent behavior | Managed Flink ENIs may not receive public IPs even in public subnets — using private subnets with VPC endpoints is the supported pattern |
| Sufficient ENI capacity | Managed Flink allocates 1 ENI per subnet for each active TaskManager | Each KPU (Kinesis Processing Unit) = 1 vCPU + 4 GB RAM = approximately 1 TaskManager; a 4 KPU application in 2 subnets = up to 8 ENIs per subnet — check account ENI limits |
| Subnets must have routing to data sources | Route table entries must cover the destination IPs for MSK, RDS, OpenSearch | MSK and RDS in the same VPC: local routing (default). Cross-VPC: VPC peering or Transit Gateway route table entries required |
Security group rules for common data sources
The Flink application's security group needs outbound rules allowing traffic to each data source. The data source's security group needs an inbound rule allowing traffic from the Flink application's security group (not the IP range — use security group ID references for dynamic ENI IPs):
// Terraform example — security group rules for Managed Flink accessing MSK and RDS
// 1. Outbound from Flink SG to MSK SG
resource "aws_security_group_rule" "flink_to_msk_tls" {
type = "egress"
from_port = 9094 // MSK TLS
to_port = 9094
protocol = "tcp"
source_security_group_id = aws_security_group.msk.id
security_group_id = aws_security_group.flink_app.id
description = "Managed Flink → MSK TLS brokers"
}
resource "aws_security_group_rule" "flink_to_msk_iam" {
type = "egress"
from_port = 9098 // MSK IAM auth
to_port = 9098
protocol = "tcp"
source_security_group_id = aws_security_group.msk.id
security_group_id = aws_security_group.flink_app.id
description = "Managed Flink → MSK IAM authentication port"
}
// 2. Inbound to MSK SG from Flink SG
resource "aws_security_group_rule" "msk_from_flink_tls" {
type = "ingress"
from_port = 9094
to_port = 9094
protocol = "tcp"
source_security_group_id = aws_security_group.flink_app.id
security_group_id = aws_security_group.msk.id
description = "MSK accepts TLS connections from Managed Flink"
}
// 3. Outbound from Flink SG to RDS (PostgreSQL)
resource "aws_security_group_rule" "flink_to_rds" {
type = "egress"
from_port = 5432
to_port = 5432
protocol = "tcp"
source_security_group_id = aws_security_group.rds.id
security_group_id = aws_security_group.flink_app.id
description = "Managed Flink → RDS PostgreSQL (for JDBC source/sink)"
}
// 4. Allow Flink to reach VPC endpoints (HTTPS 443 for Kinesis, S3 control plane)
resource "aws_security_group_rule" "flink_to_vpc_endpoints" {
type = "egress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = [var.vpc_cidr] // VPC endpoints are local to the VPC CIDR
security_group_id = aws_security_group.flink_app.id
description = "Managed Flink → VPC endpoints (Kinesis, S3, Managed Flink control plane)"
}
VPC endpoints for private subnet Managed Flink
When Managed Flink runs in a private subnet without a NAT gateway, it cannot reach AWS service endpoints over the public internet. You must create VPC endpoints for each AWS service the application needs. The endpoint type matters for Kinesis vs S3:
| AWS Service | Endpoint type | Cost | Required for |
|---|---|---|---|
| Amazon Kinesis Data Streams | Interface endpoint (com.amazonaws.{region}.kinesis-streams) | ~$7/month per AZ | Reading/writing KDS shards from private subnet |
| Amazon S3 | Gateway endpoint (free) | Free | Writing Flink checkpoints and savepoints to S3; reading application JAR from S3 |
| Amazon Managed Flink control plane | Interface endpoint (com.amazonaws.{region}.kinesisanalytics) | ~$7/month per AZ | Flink JobManager communicating with Managed Flink service for application status updates |
| AWS CloudWatch | Interface endpoint (com.amazonaws.{region}.monitoring) | ~$7/month per AZ | Publishing Flink metrics to CloudWatch from private subnet |
| AWS Secrets Manager | Interface endpoint (com.amazonaws.{region}.secretsmanager) | ~$7/month per AZ | Fetching MSK SASL credentials at application startup |
// Terraform: S3 Gateway endpoint (free — no interface endpoint needed)
resource "aws_vpc_endpoint" "s3" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids // Must include the Flink subnet route tables
}
// Terraform: Kinesis Data Streams interface endpoint
resource "aws_vpc_endpoint" "kinesis_streams" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.region}.kinesis-streams"
vpc_endpoint_type = "Interface"
subnet_ids = var.private_subnet_ids // Same subnets as Managed Flink
security_group_ids = [aws_security_group.vpc_endpoints.id]
private_dns_enabled = true // Required — Flink uses the default Kinesis DNS name
tags = { Name = "kinesis-streams-endpoint" }
}
The private_dns_enabled = true setting on the Kinesis interface endpoint is critical. It overrides the public DNS resolution for kinesis.{region}.amazonaws.com with a private IP pointing to the interface endpoint ENI. Without this, Flink resolves the Kinesis endpoint to the public IP and traffic exits the VPC (failing in a private subnet with no NAT, or incurring NAT data processing charges if a NAT gateway exists).
MSK connectivity: TLS, SASL/SCRAM, and IAM authentication
When Managed Flink connects to MSK as a Kafka source or sink, the authentication method must match what the MSK cluster is configured to accept. MSK supports three client authentication mechanisms, and the Flink Kafka connector properties differ for each:
// Option 1: MSK with TLS (mutual TLS — MSK provisioned)
Properties kafkaProps = new Properties();
kafkaProps.setProperty("bootstrap.servers", "b-1.msk-cluster.example.kafka.us-east-1.amazonaws.com:9094");
kafkaProps.setProperty("security.protocol", "SSL");
kafkaProps.setProperty("ssl.truststore.location", "/etc/ssl/certs/kafka.client.truststore.jks"); // Bundled in application JAR
// Option 2: MSK with SASL/SCRAM (username/password from Secrets Manager)
Properties kafkaProps = new Properties();
kafkaProps.setProperty("bootstrap.servers", "b-1.msk-cluster.example.kafka.us-east-1.amazonaws.com:9096");
kafkaProps.setProperty("security.protocol", "SASL_SSL");
kafkaProps.setProperty("sasl.mechanism", "SCRAM-SHA-512");
// Fetch credentials from Secrets Manager at runtime — do not hardcode in application properties
SecretsManagerClient secrets = SecretsManagerClient.builder().region(Region.US_EAST_1).build();
GetSecretValueResponse secret = secrets.getSecretValue(
GetSecretValueRequest.builder().secretId("alivemcp/msk/kafka-credentials").build()
);
JsonNode creds = objectMapper.readTree(secret.secretString());
String jaasConfig = String.format(
"org.apache.kafka.common.security.scram.ScramLoginModule required username=\"%s\" password=\"%s\";",
creds.get("username").asText(),
creds.get("password").asText()
);
kafkaProps.setProperty("sasl.jaas.config", jaasConfig);
// Option 3: MSK Serverless or MSK with IAM authentication (port 9098)
Properties kafkaProps = new Properties();
kafkaProps.setProperty("bootstrap.servers", "b-1.msk-cluster.example.kafka.us-east-1.amazonaws.com:9098");
kafkaProps.setProperty("security.protocol", "SASL_SSL");
kafkaProps.setProperty("sasl.mechanism", "AWS_MSK_IAM");
kafkaProps.setProperty("sasl.jaas.config", "software.amazon.msk.auth.iam.IAMLoginModule required;");
kafkaProps.setProperty("sasl.client.callback.handler.class",
"software.amazon.msk.auth.iam.IAMClientCallbackHandler");
// IAM auth uses the Managed Flink application's execution role — no credentials to manage
IAM authentication (Option 3) is the recommended approach for MSK Serverless and for new MSK provisioned clusters. It eliminates credential rotation (the Managed Flink execution role's IAM permissions govern access) and integrates with CloudTrail for audit. The MSK IAM auth library must be included in the application's fat JAR or uploaded as a Managed Flink dependency.
Cross-VPC connectivity via VPC peering or Transit Gateway
AliveMCP's architecture may have Managed Flink in one VPC and MSK in another (e.g., a shared services VPC). VPC peering or Transit Gateway handles cross-VPC connectivity, but three additional requirements apply beyond standard peering configuration:
- Overlapping CIDR blocks prevent peering. Check that the Managed Flink VPC CIDR and the MSK VPC CIDR do not overlap before establishing peering. If they overlap, Transit Gateway with route table isolation (not peering) is the only option.
- Security group rule references only work within the same VPC. In cross-VPC rules, use CIDR ranges (the MSK VPC's subnet CIDRs for the broker subnets) rather than security group ID references — security group ID references across peered VPCs are not supported.
- Private DNS does not automatically cross VPC peering boundaries. If the MSK VPC has private DNS enabled for its VPC endpoints, those DNS names do not resolve from the Managed Flink VPC. Configure Route 53 Resolver forwarding rules to forward MSK-related DNS queries to the MSK VPC's resolver, or use explicit broker IP addresses in the bootstrap.servers property.
Complete failure mode reference
| Symptom | Root cause | Detection | Fix |
|---|---|---|---|
| Application starts then fails to read from Kinesis (ConnectionTimeout) | No VPC endpoint for Kinesis Data Streams; Flink resolving to public IP which is unreachable from private subnet | Flink task logs: "Connection to kinesis.us-east-1.amazonaws.com timed out"; CloudWatch shows 0 records read | Create VPC Interface Endpoint for kinesis-streams with private_dns_enabled=true |
| Checkpoint writes fail (S3 AccessDenied or ConnectionRefused) | No S3 VPC Gateway Endpoint; S3 traffic routing to public IP not reachable from private subnet without NAT | Flink JobManager logs: checkpoint failed; numberOfFailedCheckpoints incrementing rapidly | Create S3 Gateway Endpoint and add to route tables for Managed Flink subnets |
| MSK connection refused on port 9094/9098 | Flink security group not added to MSK inbound rules; using wrong port for authentication method | Flink task logs: "Connection refused to broker b-1.msk-cluster..."; MSK CloudWatch IncomingBytesRate = 0 | Add Flink SG to MSK inbound on correct port; verify authentication method matches MSK cluster config |
| Application works in one AZ, fails intermittently | VPC endpoints only created in one AZ; TaskManagers in the second AZ cannot reach endpoints | Intermittent task failures correlated with TaskManager placement; errors in tasks running in the AZ without the endpoint | Create VPC endpoints in all AZs covered by Managed Flink subnets |
| Flink starts successfully but cannot reach MSK in peered VPC | Security group rules using SG ID reference across VPC peering (not supported); peering route missing in one direction | MSK connection timeout; peering route tables have entries in Flink VPC but not MSK VPC (or vice versa) | Replace SG ID references with CIDR range in cross-VPC rules; verify route tables in both VPCs have entries for the peer CIDR |