<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Lee Harding</title>
    <description>The latest articles on DEV Community by Lee Harding (@mlhpdx).</description>
    <link>https://dev.to/mlhpdx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3584010%2F5bd7223f-ea4d-4dff-afbb-ec16ee5462ac.png</url>
      <title>DEV Community: Lee Harding</title>
      <link>https://dev.to/mlhpdx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mlhpdx"/>
    <language>en</language>
    <item>
      <title>UDP as a Serverless Event Source</title>
      <dc:creator>Lee Harding</dc:creator>
      <pubDate>Fri, 10 Apr 2026 22:44:04 +0000</pubDate>
      <link>https://dev.to/aws-builders/udp-as-a-serverless-event-source-2k03</link>
      <guid>https://dev.to/aws-builders/udp-as-a-serverless-event-source-2k03</guid>
      <description>&lt;p&gt;Are you versed in &lt;a href="https://aws.amazon.com/event-driven-architecture/" rel="noopener noreferrer"&gt;Event Driven Architecture&lt;/a&gt; (EDA)? This article is going to explore applying EDA at the outside edge of systems as a boundary for traditional network traffic.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is EDA?
&lt;/h2&gt;

&lt;p&gt;Setting aside hyperbole and focusing on the "what":&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Event-driven architectures have three key components: event producers, event routers, and event consumers. [AWS Documentation]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;But what is an "event"?&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A record of a significant change in state or a notable occurrence within a system, stated in the past tense. [Wikipedia]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In EDA, things happen and information about those occurrances flows from producers to consumers. Consumers may, of course, produce events of their own creating "flows" of events around and between systems. That sounds a lot like what network equipment does, right?&lt;/p&gt;

&lt;p&gt;So let's make that literal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modeling a UDP "Event"
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://datatracker.ietf.org/doc/html/rfc768" rel="noopener noreferrer"&gt;User Datagram Protocol&lt;/a&gt; is simple intentionally.  It was designed in the 1980's as the "catch-all" mechanism for moving data around the internet when TCP wasn't appropriate. &lt;/p&gt;

&lt;p&gt;A UDP packet is just a bunch of bytes following a well-defined format:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[UDP Header][UDP Payload]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, it's not quite that simple because UDP is an extension of another protocol called the Internet Protocol (IP). And IP is almost always carried over ethernet, so the full content of a unit of data travelling the internet as UDP is really:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Ethernet Header][IP Header][UDP Header][UDP Payload]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Okay, okay, it's not that simple either but I'm going to leave it there for this article. In fact, I won't be talking about the ethernet header beyond this point so we can just focus on the UDP part (and a little about IP later).&lt;/p&gt;

&lt;p&gt;Breaking out the UDP header in more detail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Source Port][Destination Port][Length][Checksum][Payload]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Normally the operating system parses that header to decide which application or service should receive it.  A UDP packet with a destination port of &lt;code&gt;514&lt;/code&gt; is often delivered to &lt;code&gt;rsyslogd&lt;/code&gt;, for example.  If we're going to treat that UDP packet as an event inside AWS we're going to need a JSON model of it, like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="s2"&gt;"Destination: { "&lt;/span&gt;&lt;span class="err"&gt;Port&lt;/span&gt;&lt;span class="s2"&gt;": 514 },
  "&lt;/span&gt;&lt;span class="err"&gt;Payload&lt;/span&gt;&lt;span class="s2"&gt;": "&lt;/span&gt;&lt;span class="err"&gt;&amp;lt;base&lt;/span&gt;&lt;span class="mi"&gt;64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;encoded&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;payload?&lt;/span&gt;&lt;span class="s2"&gt;"
}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pretty minimal, but you get the idea.  What happened to &lt;code&gt;SourcePort&lt;/code&gt; and &lt;code&gt;Checksum&lt;/code&gt; you ask? We're just ignoring them. In reality they're optional and you may get &lt;code&gt;0&lt;/code&gt; values for both in some (many?) situations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Routing and Delivery
&lt;/h2&gt;

&lt;p&gt;With that model we can listen for UDP packets and fire an event to EventBridge for each:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;

&lt;span class="n"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;events&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;sock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;AF_INET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;SOCK_RAW&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;IPPROTO_UDP&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;recvfrom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;65535&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;ihl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0x0F&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;
    &lt;span class="n"&gt;dst_port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;struct&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unpack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;!H&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ihl&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;ihl&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;])[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pkt&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;ihl&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;

    &lt;span class="n"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put_events&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Entries&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Source&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;udp.raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;DetailType&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;UdpPacket&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Detail&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Destination&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Port&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;dst_port&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Payload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;base64&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;b64encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And from there we can use Rules to direct the packets to consumers like Lambda, Step Function, DynamoDB or S3. The possibilities are endless.&lt;/p&gt;

&lt;p&gt;Let's take an example. We could setup a Rule that filters everything destined for port &lt;code&gt;514&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws events put-rule &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--name&lt;/span&gt; udp-port-514 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--event-pattern&lt;/span&gt; &lt;span class="s1"&gt;'{
    "source": ["udp.raw"],
    "detail": {
      "Destination": {
        "Port": [514]
      }
    }
  }'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can direct those packets to a Firehose for archival:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws events put-targets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--rule&lt;/span&gt; udp-port-514 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--targets&lt;/span&gt; &lt;span class="s2"&gt;"Id"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"firehose-target"&lt;/span&gt;,&lt;span class="s2"&gt;"Arn"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:firehose:REGION:ACCOUNT_ID:deliverystream/YOUR_STREAM"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or to CloudWatch Logs, or maybe both:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws events put-targets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--rule&lt;/span&gt; udp-port-514 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--targets&lt;/span&gt; &lt;span class="s2"&gt;"Id"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"cwlogs-target"&lt;/span&gt;,&lt;span class="s2"&gt;"Arn"&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"arn:aws:logs:REGION:ACCOUNT_ID:log-group:/eventbridge/udp-514"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the flexibility of EDA applied to network traffic. Here the use case is as simple as it gets, but that's just to pique your interest.  Far more complicated scenarios benefit even more from EDA due to strong decoupling and the scalability of AWS services.&lt;/p&gt;

&lt;h2&gt;
  
  
  Caveats
&lt;/h2&gt;

&lt;p&gt;Scalability can work against you.&lt;/p&gt;

&lt;p&gt;Network traffic can be &lt;strong&gt;FAST&lt;/strong&gt;. Really, really fast.  Connecting a public-facing service directly to AWS resources like this is a terrible idea. It can lead to huge costs should someone decide (on purpose or accidentally) to DDoS you. &lt;/p&gt;

&lt;p&gt;Strong, stateful firewalls with rate limiting and abuse prevention must be in place.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;EDA is a powerful architecture pattern that applies to networking events as well as application generated events. Thinking more broadly about the "edge" of your systems opens-up great potential for simplification and, honestly, is just plain fun.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>network</category>
      <category>eventdriven</category>
      <category>udp</category>
    </item>
  </channel>
</rss>
