Diagnose "it deployed but cannot connect"

Turn a silent network timeout into a named cause. Run Reachability Analyzer between the two resources to get an explanation code, then check the five layers in order — security groups, network ACLs, routes, gateways (IGW, NAT, endpoints) and VPC Block Public Access — with the CLI or TypeScript call for each.

markdownguidev4AJWB-W6CRstage: operatetrust: officialservice: vpcinterface: cliinterface: sdkconnection timed outcannot connecttimeout no errorReachability Analyzercreate-network-insights-pathstart-network-insights-analysisNetworkPathFoundExplanationCodeENI_SG_RULES_MISMATCHNO_ROUTE_TO_DESTINATIONsecurity groupnetwork ACL ephemeral portsroute tableNAT gatewayVPC Block Public Accessflow logs

Created 18 Oct 2026 · Modified 23 Oct 2026 · 4 versions

A timeout has no error message, so make AWS produce one: run Reachability Analyzer from the source to the destination. When the path is blocked, it names the blocking component and gives an explanation code you can look up in reference/vpc/errors.md. It reads configuration only: it checks security groups, network ACLs, routes, gateways and load balancers, not the application. Each analysis is billed.

1. Ask the analyzer

aws ec2 create-network-insights-path --source <source-id> --destination <destination-id> \
  --protocol TCP --filter-at-source '{"DestinationPortRange":{"FromPort":443,"ToPort":443}}'
aws ec2 start-network-insights-analysis --network-insights-path-id <nip-…>
aws ec2 describe-network-insights-analyses --network-insights-analysis-ids <nia-…> \
  --query 'NetworkInsightsAnalyses[0].[Status,NetworkPathFound,Explanations[].ExplanationCode]'

Sources and destinations are resource IDs: instances, network interfaces, internet gateways, endpoints and so on. To test toward an IP address, omit --destination and put DestinationAddress in the filter. The analysis takes a few minutes. Delete the analysis before the path; analyses are kept for 120 days.

import {
  EC2Client,
  CreateNetworkInsightsPathCommand,
  StartNetworkInsightsAnalysisCommand,
  DescribeNetworkInsightsAnalysesCommand,
} from "@aws-sdk/client-ec2";

const ec2 = new EC2Client({ region: "<region>" });

const path = await ec2.send(new CreateNetworkInsightsPathCommand({
  Source: "<source id: instance, ENI, IGW, endpoint…>",
  Destination: "<destination id>",
  Protocol: "tcp",
  FilterAtSource: { DestinationPortRange: { FromPort: 443, ToPort: 443 } },
}));
const pathId = path.NetworkInsightsPath?.NetworkInsightsPathId!;

const start = await ec2.send(new StartNetworkInsightsAnalysisCommand({ NetworkInsightsPathId: pathId }));
const analysisId = start.NetworkInsightsAnalysis?.NetworkInsightsAnalysisId!;

for (;;) {
  await new Promise((r) => setTimeout(r, 10_000));
  const res = await ec2.send(new DescribeNetworkInsightsAnalysesCommand({ NetworkInsightsAnalysisIds: [analysisId] }));
  const a = res.NetworkInsightsAnalyses?.[0];
  if (a?.Status === "running") continue;
  console.log("reachable:", a?.NetworkPathFound);
  for (const e of a?.Explanations ?? []) {
    console.log(e.ExplanationCode, e.Direction, e.SecurityGroups?.map((g) => g.Id), e.Acl?.Id, e.RouteTable?.Id);
  }
  break;
}
  • NetworkPathFound: true means the network allows the connection, so the problem is above the network. Check that the process is listening on that port, the TLS settings, and the application's own firewall.
  • NO_PATH can also mean an unsupported case, such as IPv6. Then do the manual checks below.

2. Or check the five layers by hand, in this order

Security groups (stateful, allow-only)

  • The destination's group needs an inbound rule for the port, from the source's IP, CIDR or security group. A new group allows no inbound traffic.
  • The source's group needs an outbound rule. A new group allows all outbound traffic, but it may have been removed.
  • Return traffic is automatic. You never add rules for responses.
  • A rule that references a security group only matches private IPs of members, in the same VPC, a peered VPC, or (inbound only) across a transit gateway. Traffic to a public IP or through a middlebox does not match it.

aws ec2 describe-security-groups --group-ids <sg> --query 'SecurityGroups[0].[IpPermissions,IpPermissionsEgress]'

Network ACLs (stateless, ordered)

  • They apply when traffic enters or leaves a subnet, in both directions, separately. Responses are not automatically allowed, so the return path needs the ephemeral ports 1024–65535.
  • Rules are evaluated lowest number first, and the first match wins.
  • A custom NACL denies everything until you add rules (UNVERIFIED in the collected sources: the NACL page links to its custom-NACL subpage without stating the default). The default NACL allows all traffic.

aws ec2 describe-network-acls --filters Name=association.subnet-id,Values=<subnet>

Routes

  • The source subnet's route table needs a route covering the destination. The most specific route wins.
  • Internet from a private subnet: 0.0.0.0/0 → nat-…. From a public subnet: 0.0.0.0/0 → igw-…, and the resource needs a public IP.
  • Gateway endpoints (S3, DynamoDB) work only for the route tables they are associated with.

aws ec2 describe-route-tables --filters Name=association.subnet-id,Values=<subnet> --query 'RouteTables[0].Routes'

A subnet with no explicit association uses the main route table (UNVERIFIED in the collected sources).

Gateways

  • NAT gateway: must be Available. A zonal one sits in a public subnet routed to the internet gateway. A regional one needs no public subnet but can take up to 60 minutes to expand to a new Availability Zone. NAT passes only TCP, UDP and ICMP, drops connections idle for 350 seconds (with an RST), and does not support IPsec or fragmented TCP.
  • Internet gateway: attached to the VPC, and the resource has a public IPv4 address or an Elastic IP.
  • Interface endpoint: private DNS on, and its security group allows 443 from the caller.

VPC Block Public Access

An account-level setting per Region that overrides routes. In Bidirectional mode it blocks internet gateway traffic both ways. In Ingress-only mode it blocks inbound traffic while NAT and egress-only gateways keep working. Excluded VPCs or subnets are exempt. Reachability Analyzer reports it as VPC_BLOCK_PUBLIC_ACCESS_ENABLED. Check with aws ec2 describe-vpc-block-public-access-options (UNVERIFIED command name).

3. Things that are not the network

  • DNS to the Route 53 Resolver (the VPC+2 address), instance metadata, and Time Sync can't be blocked by security groups or NACLs. If those fail, look elsewhere.
  • A load balancer in the path brings its own rules: listeners, target health, and target groups in the enabled AZs (guides/elb/fix-alb-5xx.md).
  • Lambda in a VPC never has a public IP (patterns/lambda-in-vpc.md). Fargate pulling images needs a route to ECR and S3 (patterns/ecs-to-ecr.md).

4. Confirm with traffic

Turn on VPC Flow Logs for the network interface or subnet. A REJECT record means a security group or NACL dropped the packet; nothing recorded means it never arrived, so check the routes. How to read the records: reference/vpc/flow-logs.md. Ready-made queries: reference/cloudwatch/logs-insights-queries.md.

Read this document as markdown — the same bytes an agent receives.