Features | Pricing | Documentation | Contact | Blog | About

Now Available: Decapsulated Delivery for WireGuard Listeners

By Lee Harding | February 26, 2026 | 6 min read
Diagram showing WireGuard traffic decapsulated at the Gateway before delivery to a Destination

WireGuard Listeners now support a Decapsulated Delivery mode. When enabled, UDP Gateway strips the WireGuard tunnel headers before forwarding packets to your Destinations, so every Destination sees the original inner UDP payload — identical to what a plain UDP Listener would deliver.

What changed and why it matters

Previously, a WireGuard Listener always delivered the full encapsulated packet to its Destinations. If you wanted to work with the inner payload, your Lambda or Step Functions function needed to perform the decapsulation itself.

That works well for Lambda and Step Functions, where you're running code and have full control. But it created a gap for Destinations that don't run code at all: CloudWatch Logs, SQS, Kinesis, IoT Core. If your goal is to read or process the application payload in those Destinations, the full WireGuard-encapsulated packet — including tunnel headers and framing — requires additional parsing steps that those services don't provide natively.

There are legitimate reasons to keep the full encapsulated packet. If you're archiving raw traffic for forensic analysis, feeding a packet-level replay system, or need to preserve the exact on-wire format, full encapsulated delivery to SQS or Kinesis using the base64 formatter works well. The full packet lands in your stream or queue exactly as received. Decapsulated Delivery is not for that use case, and if that's what you need, leave the option disabled.

Decapsulated Delivery is for the case where you want the inner application payload — readable, processable, and consistent with what plain UDP Listeners already deliver. It moves the unpacking step to the Gateway, where it happens once for all Destinations on a Listener. The result is a simpler architecture, less code to maintain, and — for the first time — the ability to share a single Destination between a plain UDP Listener and a WireGuard Listener.

Sharing Destinations across Listener types

This is the most immediate practical benefit. Consider a syslog aggregation setup delivering to CloudWatch Logs with UTF-8 formatting. Previously, you needed separate Destinations depending on whether traffic arrived over plain UDP or WireGuard — the payload format was different.

With Decapsulated Delivery, both Listeners can point at the same CloudWatch Logs Destination and the resulting log records are identical readable text:

Feb 26 14:32:18 router1 sshd[4821]: Accepted publickey for admin from 192.168.1.10 port 54322 ssh2
Feb 26 14:32:19 sensor-03 kernel: eth0: renamed from veth9a1b2c
Feb 26 14:32:21 app-server systemd[1]: Started Application Service.

No Lambda. No custom parsing. Just log records in CloudWatch, regardless of whether they arrived via a plain UDP Listener on port 5514 or a WireGuard Listener on port 31820.

The JSON envelope for decapsulated packets

When a WireGuard Listener delivers a packet with Decapsulated Delivery enabled, the JSON envelope includes the standard fields — plus additional metadata about the original tunnel addressing. This lets a Destination distinguish traffic from different virtual peers or route based on the inner IP headers, even without running any code:

{
  "Tag": "pxp-batch-51573167-00000",
  "ReceivedAt": "2026-02-26T03:39:52",
  "Remote": {
    "IpAddress": "203.0.113.42",
    "Port": 54520
  },
  "Local": {
    "Domain": "ingress-1",
    "Port": 2196,
    "Protocol": "wg"
  },
  "Inner": {
    "Protocol": 17,
    "Version": 4,
    "SourceAddress": "10.0.1.1",
    "SourcePort": 52296,
    "DestinationAddress": "10.10.16.10",
    "DestinationPort": 2048
  },
  "Formatter": "utf8",
  "Data": "hello from wireguard\n"
}

The outer Remote and Local fields describe the WireGuard peer's public UDP endpoint — the address and port used to establish the tunnel. The Local object includes a Protocol: "wg" field to identify the Listener type. The Inner object captures the inner IP and port addressing from inside the tunnel: the source and destination as seen by the tunneled protocol, along with the IP version and protocol number.

For many integrations, the inner addressing is the relevant part. Your application data originated at Inner.SourceAddress and was destined for a specific Inner.DestinationPort. These are the addresses your application logic cares about — and they're now available to every Destination without writing any code to extract them.

That has real consequences for codeless Destinations. An SQS consumer can filter messages by Inner.DestinationPort to separate service types without any knowledge of WireGuard. A Kinesis processor can partition by Inner.SourceAddress to attribute traffic to specific tunnel peers. A CloudWatch Logs Insights query can aggregate by virtual IP. None of that required WireGuard awareness before — it requires it even less now.

How to enable it

Decapsulated Delivery is opt-in. Existing WireGuard Listeners continue to deliver full encapsulated packets to their Destinations — no change to existing behavior without an explicit configuration update. If you're using full encapsulated delivery intentionally (for packet capture, archival, or analysis), you don't need to change anything.

To enable it, add DecapsulatedDelivery: true to the Properties of your WireGuard Listener resource:

Listener:
  Type: Custom::ProxylityUdpGatewayListener
  Properties:
    ServiceToken: !FindInMap [ProxylityConfig, !Ref "AWS::Region", ServiceToken]
    ApiKey: !FindInMap [ProxylityConfig, Account, ApiKey]
    Protocols:
      - wg
    Peers:
      - PublicKey: "<your-peer-public-key>"
        AllowedIPs:
          - 0.0.0.0/0
          - ::/0
    DecapsulatedDelivery: true
    Destinations:
      - Role:
          Arn: !GetAtt DestinationRole.Arn
        DestinationArn: !GetAtt SyslogDestination.Arn

If your current Destination Lambda uses DecapsulatedHandler from the SDK to do the unpacking, you can now move that work to the Gateway and simplify your function significantly. The WireGuard decapsulation logic, IP header parsing, protocol dispatch, and port extraction that previously lived in your Lambda are no longer necessary just to reach the application payload — the Gateway handles all of that before delivery. The DecapsulatedHandler pattern remains fully supported for cases where you want fine-grained control over inner packet processing in code, but it is no longer the entry fee for working with WireGuard traffic in Lambda.

Decapsulated Delivery also simplifies fan-out. A single WireGuard Listener can deliver the inner payload simultaneously to a Lambda function, a CloudWatch Logs group, and a Kinesis stream — all from one Listener configuration. Previously, each codeless Destination on that Listener received the full encapsulated packet and was effectively unable to work with it directly. Now all three Destinations receive the same clean inner payload, and none of them need to understand WireGuard to consume it.

What carries across from plain UDP

When Decapsulated Delivery is enabled, the inner UDP payload is delivered using the same formatting options as any plain UDP Listener: hex, base64, utf8, ascii. The same CloudFormation templates, the same Destination ARNs, the same formatter configuration. The only behavioral difference is the presence of the Inner metadata block in the envelope.

Getting started

Decapsulated Delivery is available now on all WireGuard Listeners. Enable it with a single configuration property and update your Destinations to point at existing targets that already handle plain UDP traffic.

For configuration reference, IAM requirements for WireGuard Listeners, and worked CloudFormation examples, see the Listeners documentation.

Ready to modernize your UDP backends?

Get started with Proxylity UDP Gateway today. No upfront costs ‐ pay only for what you use.

Buy with AWS Try free with AWS Explore Documentation