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.

RequirementDetailsWhy it matters
At least 2 subnets in different AZsManaged Flink distributes TaskManagers across AZs for fault toleranceIf 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 onlyAWS recommends private subnets; public subnet deployments have inconsistent behaviorManaged Flink ENIs may not receive public IPs even in public subnets — using private subnets with VPC endpoints is the supported pattern
Sufficient ENI capacityManaged Flink allocates 1 ENI per subnet for each active TaskManagerEach 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 sourcesRoute table entries must cover the destination IPs for MSK, RDS, OpenSearchMSK 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 ServiceEndpoint typeCostRequired for
Amazon Kinesis Data StreamsInterface endpoint (com.amazonaws.{region}.kinesis-streams)~$7/month per AZReading/writing KDS shards from private subnet
Amazon S3Gateway endpoint (free)FreeWriting Flink checkpoints and savepoints to S3; reading application JAR from S3
Amazon Managed Flink control planeInterface endpoint (com.amazonaws.{region}.kinesisanalytics)~$7/month per AZFlink JobManager communicating with Managed Flink service for application status updates
AWS CloudWatchInterface endpoint (com.amazonaws.{region}.monitoring)~$7/month per AZPublishing Flink metrics to CloudWatch from private subnet
AWS Secrets ManagerInterface endpoint (com.amazonaws.{region}.secretsmanager)~$7/month per AZFetching 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:

Complete failure mode reference

SymptomRoot causeDetectionFix
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 subnetFlink task logs: "Connection to kinesis.us-east-1.amazonaws.com timed out"; CloudWatch shows 0 records readCreate 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 NATFlink JobManager logs: checkpoint failed; numberOfFailedCheckpoints incrementing rapidlyCreate S3 Gateway Endpoint and add to route tables for Managed Flink subnets
MSK connection refused on port 9094/9098Flink security group not added to MSK inbound rules; using wrong port for authentication methodFlink task logs: "Connection refused to broker b-1.msk-cluster..."; MSK CloudWatch IncomingBytesRate = 0Add Flink SG to MSK inbound on correct port; verify authentication method matches MSK cluster config
Application works in one AZ, fails intermittentlyVPC endpoints only created in one AZ; TaskManagers in the second AZ cannot reach endpointsIntermittent task failures correlated with TaskManager placement; errors in tasks running in the AZ without the endpointCreate VPC endpoints in all AZs covered by Managed Flink subnets
Flink starts successfully but cannot reach MSK in peered VPCSecurity group rules using SG ID reference across VPC peering (not supported); peering route missing in one directionMSK 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