Lambdo's and Dont's

Lambdagony: You Don't Need a Lambda, You Need a Socket

You know that Lambda function you wrote? The one that receives an HTTP request, does some small thing, and returns a response? The one with 200 lines of Terraform around it, an API Gateway in front of it, a CloudWatch log group you always forget how to query, a 3-second cold start that appears at the worst possible time, and a deployment pipeline held together with YAML and prayers?

What if I told you the exact same workload could be a 12-line systemd unit on a box you already have?

The Problem with Lambda

Lambda is great for what Lambda is great for. The issue is that people reach for it when they don't need it. You don't have "thousands of concurrent requests with unpredictable spiky traffic." You have a webhook that fires twice an hour from your git forge and a cron job that runs nightly. You are not Netflix.

But you've been told serverless is the future, so now you have:

All for something that accepts a JSON payload, writes a row to a database, and returns {"ok": true}.

What systemd Gives You for Free

systemd has a feature called socket activation. It's been around since 2010. The idea is dead simple: systemd listens on a port. When a connection arrives, it starts your service and hands over the socket. When your service is idle, it can exit, and systemd goes back to listening.

Sound familiar? It's Lambda. It's literally Lambda. Except:

How It Works

You need two unit files. That's it.

The Socket Unit

# /etc/systemd/system/myapi.socket
[Unit]
Description=My API Socket

[Socket]
ListenStream=8080

[Install]
WantedBy=sockets.target

This tells systemd: listen on port 8080. When someone connects, start the corresponding .service.

The Service Unit

# /etc/systemd/system/myapi.service
[Unit]
Description=My API Service
Requires=myapi.socket

[Service]
Type=notify
ExecStart=/usr/local/bin/myapi
NonBlocking=true

# Hardening, because we're not animals
DynamicUser=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
NoNewPrivileges=yes

Your binary receives the already-open socket as file descriptor 3 (or via the LISTEN_FDS protocol — sd_listen_fds(3) if you want to be proper about it). Most languages have a library for this. In Go:

listeners, _ := activation.Listeners()
http.Serve(listeners[0], myHandler)

In Python:

import socket, os

fd = 3  # or use the sd-daemon library
sock = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)

In Rust there's listenfd. In C there's sd-daemon. You get the idea.

Enable and Start

systemctl enable --now myapi.socket

That's it. Port 8080 is now listening. Your service starts on first connection.

"But What About Scaling?"

If you genuinely need to handle thousands of concurrent requests with autoscaling across regions, yes, use a managed service. Nobody is arguing you should run Twitter on socket-activated systemd units.

But be honest with yourself: most of the things people put in Lambdas are internal tools, webhooks, simple APIs, and cron jobs. These are workloads that a $5/month VPS handles without breaking a sweat. The "scale" argument is a thought-terminating cliche that prevents people from choosing the simple option.

"But What About High Availability?"

You already have a server. Is it running? Then your service is available. If it's not running, your Lambda isn't going to help you either because the database it talks to is probably on the same infrastructure that's down.

If you genuinely need HA, run two boxes and put a load balancer in front. Still simpler than Lambda + API Gateway + custom domain + ACM certificate + Route53 record.

"But Lambda Scales to Zero!"

So does a socket-activated service. That's the whole point. When nobody is connecting, your process isn't running. The socket is open, held by PID 1, using approximately zero resources.

Bonus: systemd Timers Instead of CloudWatch Events / EventBridge

That cron-triggered Lambda? It's a systemd timer:

# /etc/systemd/system/nightly-job.timer
[Unit]
Description=Run nightly job

[Timer]
OnCalendar=*-*-* 03:00:00
Persistent=true

[Install]
WantedBy=timers.target
# /etc/systemd/system/nightly-job.service
[Unit]
Description=Nightly job

[Service]
Type=oneshot
ExecStart=/usr/local/bin/nightly-job
DynamicUser=yes
systemctl enable --now nightly-job.timer

Want to see when it last ran? systemctl status nightly-job.timer. Want logs? journalctl -u nightly-job. Want to run it manually right now? systemctl start nightly-job. No clicking through 4 AWS console pages.

When to Actually Use Lambda

When to Use a Socket-Activated Service Instead

The Punchline

The cloud providers have done an incredible job of making people forget that computers are good at running programs. A modern Linux box with systemd can do socket activation, process supervision, resource limits (cgroups), sandboxing (namespaces, seccomp), logging, and scheduled tasks. It's been able to do all of this for over a decade.

Lambda is a product. Socket activation is a feature of your operating system. One of them has a marketing budget.


Comments