Monitoring Tunnel State and Troubleshooting a Down Tunnel
Overview
Domain expertise for detecting and diagnosing AWS Site-to-Site VPN tunnel failures with Amazon
CloudWatch. Covers the TunnelState metric and how it reads differently for static versus BGP
connections, building alarms that notify an Amazon Simple Notification Service (SNS) topic, enabling
and reading Site-to-Site VPN logs to find the cause, why the data metrics are not a liveness signal,
and the CloudWatch cost consideration. This reference covers tunnel state and VPN logs; AWS CloudTrail
captures the API-level events such as connection creation and modification.
Does not cover building the connection or its high availability (other references). Assumes a connection exists (the creating-a-site-to-site-vpn-connection reference).
Execute commands using the AWS MCP server when connected (sandboxed execution, audit logging,
observability). Fall back to the AWS CLI otherwise. Pass --region {region} matching the connection.
Table of Contents
- Overview
- Workflow
- TunnelState reads differently by routing type
- Wiring a working alarm
- Publish tunnel activity and BGP logs to CloudWatch
- Data metrics are not liveness
- CloudWatch cost
- Troubleshooting
- Procedure
- Security Considerations
- Additional Resources
Workflow
To detect and diagnose tunnel failures, set the right alarm against the connection's routing type, wire it to an SNS topic, and enable VPN logs to find the cause when a tunnel drops. See the Procedure section below.
The procedure covers:
- Determining the routing type so the
TunnelStatethreshold is correct - Creating the alarm with the right metric, statistic, threshold, and SNS topic
- Asking the customer which of the two log types to enable (tunnel activity, BGP, or both) and enabling them to CloudWatch Logs
- Reading the logs for the activity or BGP detail that names the cause
TunnelState reads differently by routing type
The TunnelState metric does not read the same way for both routing types. For static VPNs, 0 is
DOWN and 1 is UP. For BGP VPNs, 1 means ESTABLISHED and values between 0 and 1 mean at least one
tunnel is not up. An alarm threshold set without knowing this misfires or stays silent during a real
outage.
Constraints:
- You MUST set the alarm threshold against the customer's actual routing type
- You SHOULD confirm whether the connection is static or BGP before configuring the alarm
Wiring a working alarm
A working alarm needs the right metric, statistic, threshold, and an SNS topic wired together, and the statistic depends on the alarm scope: alarming when any tunnel drops versus only when all tunnels are down versus watching one specific tunnel.
Constraints:
- You MUST sequence the alarm setup: metric, statistic, threshold, then SNS topic
- You MUST pick the statistic that matches the scope:
Minimumfor any-tunnel-down,Maximumfor all-tunnels-down, or theTunnelIpAddressdimension for a single tunnel - You SHOULD confirm the SNS topic has a confirmed subscription so the notification actually reaches someone
- You MUST encrypt the CloudWatch Logs group that receives Site-to-Site VPN logs with an AWS KMS key and enable server-side encryption on the SNS topic, and MUST verify that all SNS topic subscribers are authorized operations personnel
Publish tunnel activity and BGP logs to CloudWatch
When a tunnel is down the metric says it is down but not why, and customers spend time guessing at IKE, dead peer detection, or BGP causes. Site-to-Site VPN can publish two separate, independently enabled log types to CloudWatch Logs to help with troubleshooting (the same two names the AWS docs use — see AWS Site-to-Site VPN logs):
- Tunnel activity logs record the IPsec/IKE control plane: IPsec tunnel establishment, IKE phase
1/2 protocol state (Established / Rekeying / Negotiating / Down), dead peer detection (DPD), and
NAT-T detection, with verbose per-message detail for IKEv1/IKEv2 errors and negotiation (for
example, pre-shared-key mismatch, "No Proposal Match Found", or DPD time-out). They apply to
every connection, static or dynamic. Controlled by the
LogEnabled/LogGroupArn/LogOutputFormatfields. - Tunnel BGP logs record the BGP control plane as two event types:
BGPStatus(session state transitions such as OpenConfirm→Established, prefix-limit warnings and violations, hold-timer expiry, and Cease notifications) andRouteStatus(routes ADVERTISED / UPDATED / WITHDRAWN, with a details field when a route is denied). They apply only to dynamic (BGP) connections and are meaningless on a static connection. Controlled by the separateBgpLogEnabled/BgpLogGroupArn/BgpLogOutputFormatfields.
Both are toggled through modify-vpn-tunnel-options, but they are distinct switches: enabling
tunnel activity logging does NOT enable BGP logging. A dynamic connection whose tunnel stays up at
the IKE layer but drops its BGP session (prefix-limit hit, hold-timer expiry, route withdrawn) shows
nothing useful in the activity log — only the BGP log names that cause. This is exactly the trap of
enabling "the VPN logs" and quietly getting activity logs alone.
Constraints:
- You MUST treat tunnel activity logs and BGP logs as two separate choices, not one "VPN logs" toggle
- You MUST ask the customer which log type(s) to enable — tunnel activity, BGP, or both — before
running
modify-vpn-tunnel-options, rather than defaulting to activity logs alone - For a dynamic (BGP) connection you MUST offer BGP logs and SHOULD recommend enabling both activity and BGP logs, since BGP-layer failures are invisible in the activity log
- For a static connection you MUST NOT offer BGP logs (they capture nothing) and enable only tunnel activity logs
- You MUST enable the chosen VPN logs and point the customer at the fields that explain the failure, rather than leaving them to guess
- You SHOULD enable logs before an incident where possible, since they only capture events after they are turned on
Data metrics are not liveness
The TunnelDataIn and TunnelDataOut metrics can report traffic even when a tunnel is down,
because of periodic status checks and background BGP and ARP requests. Customers who watch data
volume as a health signal conclude the tunnel is fine when it is not.
Constraints:
- You MUST rely on
TunnelStatefor health and treat the data metrics as usage, not liveness - You SHOULD correct the customer if they propose alarming on data volume as a health signal
CloudWatch cost
Site-to-Site VPN does not charge for metrics or logs, but the CloudWatch metrics, alarms, and logs themselves bill at standard CloudWatch rates. Turning on detailed monitoring and log delivery across every connection adds up.
Constraints:
- You MUST note the CloudWatch cost so the customer makes the tradeoff knowingly
- You SHOULD suggest enabling full metrics, alarms, and logs for production VPNs while trimming them for test and development workloads
Troubleshooting
Alarm never fires during an outage, or fires constantly
The threshold does not match the routing type. Set it against static (0/1) or BGP (1 = established) (TunnelState reads differently by routing type).
Alarm fires but no one is notified
The SNS topic has no confirmed subscription. Confirm the subscription (Wiring a working alarm).
Tunnel is down and the cause is unknown
VPN logs are not enabled, or only one of the two types is. Enable the right type(s) — tunnel activity logs for IKE/IPsec detail, BGP logs for BGP session and route detail on dynamic connections — and read them (Publish tunnel activity and BGP logs to CloudWatch).
Tunnel is up but a dynamic connection has no route / traffic
The IKE tunnel is up but the BGP session or route exchange failed, and only activity logging was enabled so the cause is invisible. Enable BGP logs and read the BGP status and route-status fields (Publish tunnel activity and BGP logs to CloudWatch).
Tunnel looks healthy by data volume but is actually down
Data metrics report background traffic. Use TunnelState for health (Data metrics are not liveness).
Procedure
Overview
This procedure determines the routing type, creates a correctly-thresholded alarm wired to an SNS topic, and enables VPN logs, then surfaces the connection console link to verify tunnel status.
Parameters
- region (required): The AWS Region of the connection.
- vpn_connection_id (required): The connection to monitor.
- routing_type (required):
staticordynamic(BGP), to set the threshold correctly. - scope (required): one of
any-tunnel-down,all-tunnels-down, orsingle-tunnel, to pick the statistic and dimension (see Step 2).any-tunnel-downpages when either tunnel of the connection drops (redundancy health);all-tunnels-downpages only when the whole connection is lost;single-tunnelwatches one specific tunnel by its outside IP. - sns_topic_arn (required): The SNS topic to notify.
- kms_key_arn (required): The AWS KMS key ARN used to encrypt the CloudWatch Logs group that receives VPN logs.
- tunnel_outside_ips (required): The outside IP addresses of the VPN tunnels (one per tunnel, obtained from
describe-vpn-connections). - log_types (required): which of the two log types to enable —
activity,bgp, orboth. Ask the customer explicitly.activity(tunnel activity log: IKE/IPsec/DPD/NAT-T) applies to any connection;bgp(tunnel BGP log: session and route events) applies only to dynamic connections. For a dynamic connection, recommendboth; for a static connection this is forced toactivity. - environment (optional):
stagingortest, to weigh the CloudWatch cost.
Constraints for parameter acquisition:
- You MUST establish the routing type and alarm scope upfront
- You MUST ask the customer which log type(s) to enable (
activity,bgp, orboth) before modifying any tunnel; for a dynamic connection recommendboth, and for a static connection enable onlyactivityand do not offerbgp - You MUST verify that all SNS topic subscribers are authorized operations personnel
- You SHOULD confirm the SNS topic exists and has a confirmed subscription
Steps
1. Determine routing type and threshold
Constraints:
- You MUST confirm credentials with
aws sts get-caller-identity - You SHOULD recommend ephemeral IAM role-based credentials (instance profile, SSO session, or assumed role) rather than long-lived IAM user access keys for running these commands
- You MUST set the
TunnelStatethreshold against the routing type: static is 0 DOWN / 1 UP; BGP is 1 ESTABLISHED, 0 to 1 means a tunnel is not up
2. Create the alarm
Constraints:
You MUST create the alarm with the right metric, statistic for the scope, threshold, and SNS topic:
aws cloudwatch put-metric-alarm --alarm-name s2s-vpn-tunnel-down-{vpn_connection_id}-{scope} \ --namespace AWS/VPN --metric-name TunnelState --dimensions Name=VpnId,Value={vpn_connection_id} \ --statistic Maximum --threshold 1 --comparison-operator LessThanThreshold \ --period 300 --evaluation-periods 1 --treat-missing-data breaching \ --alarm-actions {sns_topic_arn} --region {region}You MUST verify that all SNS topic subscribers are authorized operations personnel before wiring the topic to the alarm
You MUST verify the SNS topic has server-side encryption enabled before wiring it to the alarm, checking that
KmsMasterKeyIdis set on the topic:aws sns get-topic-attributes --topic-arn {sns_topic_arn} --region {region}You MUST set
--treat-missing-data breachingso the alarm fires rather than going to INSUFFICIENT_DATA when a down tunnel stops reporting metricsYou MUST include both
{vpn_connection_id}and{scope}in the alarm name so each connection-and-scope alarm is unique;put-metric-alarmis an upsert, so a name without the scope would let a second alarm on the same connection (for example anany-tunnel-downalarm added alongside anall-tunnels-downalarm) silently overwrite the first, and a name without the connection id would collide across connections (for example the second leg of an HA pair)You MUST pick the statistic and dimension that match the requested
scope. With theName=VpnIddimension,TunnelStateaggregates across both tunnels of the connection, so the statistic selects which aggregate you alarm on:scopeIntent Statistic Dimension any-tunnel-downPage when either tunnel drops (redundancy health) Minimum(falls below 1 as soon as one tunnel is down)Name=VpnIdall-tunnels-downPage only when the whole connection is lost Maximum(falls below 1 only when no tunnel is up)Name=VpnIdsingle-tunnelWatch one specific tunnel statistic does not matter (already one tunnel) Name=VpnIdplusName=TunnelIpAddress,Value={tunnel_outside_ip}The example below uses
Maximum(theall-tunnels-downscope), which alarms only on full connection loss; switch toMinimumforany-tunnel-downto be paged the moment either tunnel drops, or add theTunnelIpAddressdimension forsingle-tunnel
3. Confirm which log types to enable, then enable VPN logs
There are two independent log types (see "Publish tunnel activity and BGP logs to CloudWatch"):
tunnel activity logs (LogEnabled, IKE/IPsec/DPD, any connection) and tunnel BGP logs
(BgpLogEnabled, BGP session and route events, dynamic connections only). They are separate
switches on the same modify-vpn-tunnel-options call, and enabling one does not enable the other.
Constraints:
You MUST confirm with the customer which log type(s) to enable —
activity,bgp, orboth— before runningmodify-vpn-tunnel-options. Do NOT silently enable activity logs alone. For a dynamic (BGP) connection, recommendbothbecause BGP-layer failures (prefix-limit hits, hold-timer expiry, withdrawn routes) do not appear in the activity log. For a static connection, enable onlyactivityand do not offerbgp.You MUST create the CloudWatch Logs group with AWS KMS encryption, then enable log delivery to it on each tunnel so a future down tunnel can be diagnosed.
create-log-groupdoes not return an ARN, so construct the log group ARN asarn:aws:logs:{region}:{account_id}:log-group:/aws/vpn/{vpn_connection_id}:*(or retrieve it withdescribe-log-groups), and runmodify-vpn-tunnel-optionsonce per tunnel outside IP, since each connection has two tunnels:aws logs create-log-group --log-group-name /aws/vpn/{vpn_connection_id} \ --kms-key-id {kms_key_arn} --region {region}You MUST build the
LogOptionsstring from the customer'slog_typeschoice, setting only the fields for the log types they chose. Run once per address in{tunnel_outside_ips}:activityonly:--tunnel-options "LogOptions={CloudWatchLogOptions={LogEnabled=true,LogGroupArn={log_group_arn},LogOutputFormat=json}}"bgponly (dynamic connections only):--tunnel-options "LogOptions={CloudWatchLogOptions={BgpLogEnabled=true,BgpLogGroupArn={log_group_arn},BgpLogOutputFormat=json}}"both(recommended for dynamic connections):--tunnel-options "LogOptions={CloudWatchLogOptions={LogEnabled=true,LogGroupArn={log_group_arn},LogOutputFormat=json,BgpLogEnabled=true,BgpLogGroupArn={log_group_arn},BgpLogOutputFormat=json}}"
Full call, repeated per tunnel outside IP:
aws ec2 modify-vpn-tunnel-options --vpn-connection-id {vpn_connection_id} \ --vpn-tunnel-outside-ip-address {tunnel_outside_ip} \ --tunnel-options "{log_options_for_chosen_types}" \ --region {region}You MUST warn the customer that
modify-vpn-tunnel-optionsbriefly renegotiates and interrupts the affected tunnel while the change is applied; on a production connection, enable logs one tunnel at a time and confirm the first tunnel is back up before modifying the second, so the connection is never fully downYou SHOULD point the customer at the IKE fields (activity log) and the BGP status / route fields (BGP log) that name the cause; see AWS Site-to-Site VPN logs for the field reference and Enable Site-to-Site VPN logs for the console flow
4. Confirm and surface the console link
Constraints:
You MUST present the VPN connection console link, filling
{region}and{vpnConnectionId}from the request, and tell the customer to open it and confirm tunnel status:https://console.aws.amazon.com/vpc/home?region={region}#VpnConnectionDetails:VpnConnectionId={vpnConnectionId}
Example
Example input
{
"region": "us-east-1",
"vpn_connection_id": "vpn-0abc1234def567890",
"routing_type": "dynamic",
"scope": "all-tunnels-down",
"sns_topic_arn": "arn:aws:sns:us-east-1:111122223333:vpn-alerts",
"kms_key_arn": "arn:aws:kms:us-east-1:111122223333:key/abcd1234-ef56-7890-abcd-1234567890ab",
"tunnel_outside_ips": ["198.51.100.10", "198.51.100.11"],
"log_types": "both",
"environment": "staging"
}Example output
BGP connection: TunnelState threshold set to alarm below 1 (established). all-tunnels-down alarm
(Maximum statistic, VpnId dimension) wired to the vpn-alerts SNS topic. Confirmed the connection is
dynamic and asked which logs to enable; enabled BOTH tunnel activity logs (IKE/IPsec) and BGP logs
to a KMS-encrypted CloudWatch Logs group on both tunnels (198.51.100.10, 198.51.100.11), so a
BGP-layer failure is not left invisible. Noted CloudWatch cost is justified for
the staging workload. Open the connection to confirm tunnel status:
https://console.aws.amazon.com/vpc/home?region=us-east-1#VpnConnectionDetails:VpnConnectionId=vpn-0abc1234def567890Troubleshooting
Alarm misfires or stays silent
Threshold does not match routing type. Set it against static or BGP (Step 1).
No notification on alarm
SNS topic has no confirmed subscription. Confirm it (Step 2).
Cause of a down tunnel is unknown
Enable VPN logs and read the IKE and BGP fields (Step 3).
Healthy by data volume but down
Use TunnelState, not data metrics, for health (Data metrics are not liveness).
Security Considerations
VPN logs and tunnel state changes expose infrastructure detail (peer IPs, BGP and IKE events), so the monitoring path is itself sensitive and must be access-controlled.
Constraints:
- You MUST encrypt the CloudWatch Logs group that receives Site-to-Site VPN logs with an AWS KMS key and enable server-side encryption on the SNS topic, and MUST verify that all SNS topic subscribers are authorized operations personnel
- You SHOULD apply a least-privilege retention and access policy to the log group so only authorized personnel can read tunnel and BGP detail
Additional Resources
- Monitor AWS Site-to-Site VPN tunnels using Amazon CloudWatch (AWS Site-to-Site VPN User Guide)
- Create Amazon CloudWatch alarms to monitor AWS Site-to-Site VPN tunnels (AWS Site-to-Site VPN User Guide)
- AWS Site-to-Site VPN logs (AWS Site-to-Site VPN User Guide)
- AWS VPN Pricing
- Setting up of AWS Site-to-Site VPN automated monitoring solution (AWS Networking & Content Delivery Blog)