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:
- A Lambda function
- An API Gateway (with its own fun config)
- An IAM role with a policy document
- A CloudWatch log group
- A deployment artifact (zip or container image)
- Some way to get that artifact there (CI, SAM, CDK, Terraform, clicking around in the console and hoping you remember what you did)
- Cold starts
- A 15 minute execution timeout
- No local state, no filesystem (well, 512 MB of
/tmp), no persistent connections - A bill that's small but irritating to reason about
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:
- No cold start that you can't control (your binary is local, on an SSD)
- No 15-minute timeout
- Full filesystem access
- Persistent state if you want it
- Logs go to the journal, queryable with
journalctl -u yourservice - Deployment is
scpandsystemctl daemon-reload(or NixOS rebuild, if you're civilized) - It's free. The box is already running.
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.(,.,.)
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=yessystemctl 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
- You have genuinely spiky, unpredictable traffic that spans orders of magnitude
- You don't want to manage any infrastructure at all (but then you're managing AWS infrastructure, which is its own beast)
- You're already deep in the AWS ecosystem and the integration points save you real time
- You need to run in multiple regions with minimal effort
When to Use a Socket-Activated Service Instead
- You have a server
- Your traffic is low to moderate
- You want to actually understand your infrastructure
- You're tired of 200 lines of IaC for a 50-line program
- You value the ability to
sshin and just look at things - You don't want to learn a new debugging/logging/deployment paradigm for what is fundamentally "a program that listens on a port"
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