Matthias Bigl
How to Keep Your AWS Lambdas Warm with GitHub Actions (And Why You're Committing Architectural Heresy)
Let's get one thing straight before we write a single line of YAML:
If you are doing this, you are defeating the entire purpose of serverless.Language: text
You chose AWS Lambda because you wanted it to scale to zero. You wanted to pay only for exactly what you use. But then your user clicked a button, stared at a loading spinner for four seconds, and closed the tab.
Now you're here, looking up how to keep AWS Lambdas warm using GitHub Actions scheduled workflows, effectively building a weird Rube Goldberg machine to simulate a server that never turns off.
Theo would look at you through his webcam, sigh, and say:
"Bro. Just use a persistent server. Put it on Railway or Vercel. You are not Google. Stop playing cloud architect."
The Primeagen would look at your 4-second boot time and say:
"Skill issue. If you didn't write it in a garbage-collected language and just used Rust, it would be blazingly fast and you wouldn't need a cron job to keep it alive."
Both of them are right.
But neither of them is here to pay your AWS bill or rewrite your legacy Node.js backend.
So, out of morbid curiosity, let's explore how this actually works, what AWS is doing under the hood, and whether you should ever ship this crime against cloud computing.
The Truth About Cold Starts (It's a Tiny Linux Server)
A common myth is that AWS has to boot a virtual machine from absolute zero every time a Lambda cold start happens.
That's not actually true.
AWS uses Firecracker, their open-source microVM technology, and maintains a massive pool of pre-warmed generic microVMs ready to be claimed.
When a request arrives, AWS doesn't boot a VM from scratch. Instead, it grabs one of those tiny Linux environments and prepares it to run your function.
The cold start delay happens in the next steps.
Language: mermaid
The slow part typically includes:
- Downloading and unpacking your code
- Attaching ENIs if you're inside a VPC
- Booting the runtime (Node, Python, etc.)
- Running initialization code
- Opening database connections
- Parsing dependencies
If your Lambda has 300MB of dependencies and a database client, that initialization step can easily take several seconds.
The Key Trick
AWS usually keeps Lambda execution environments alive for roughly 5–15 minutes after a request.
If we invoke the Lambda every 5 minutes, AWS never tears down the environment.
Meaning the next real user request hits a warm runtime instead of triggering a cold start.
This is the entire hack.
Keeping a Lambda warm with a cron job is just running a server with extra steps.
The Philosophical Crisis: Should You Actually Do This?
By pinging your Lambda every 5 minutes, you're effectively saying:
"I want an always-on server, but I want it to run on Lambda and pretend it's serverless."
There are a few legitimate reasons you might do this.
Reasons You Might Actually Do This
You're broke
A GitHub Action hitting a Lambda every 5 minutes results in:
- 8,640 requests per month
Assuming:
- 50ms runtime
- 1GB Lambda
That's about 432 GB-seconds of compute.
The AWS free tier gives you:
- 1,000,000 requests
- 400,000 GB-seconds
Meaning this costs exactly $0.
And $0 > $5 VPS.
You're trapped
Your company mandated serverless architecture, but your product manager is angry about the loading spinner and nobody wants to pay for Provisioned Concurrency.
This hack improves UX without touching infrastructure budgets.
You're wrapping a bloated API
Sometimes you inherit a giant Java or Python library that takes 4–6 seconds to initialize.
A rewrite isn't happening this sprint.
So you cheat.
Reasons You Should Absolutely Not Do This
The concurrency blind spot
This hack only keeps one instance warm.
If two users hit your API simultaneously:
- User A → warm instance
- User B → cold start
Lambda will spin up a second microVM and the problem returns.
You're reinventing a worse EC2 instance
If your service needs to be ready 24/7 with low latency, a small persistent container is often the better architecture.
You're masking the real problem
If cold starts ruin your UX, the issue is often:
- oversized dependencies
- large container images
- slow initialization logic
GitHub Actions cron isn't perfect
Scheduled workflows can be delayed during peak hours.
You will still get occasional cold starts.
Just fewer of them.
The Architecture of the Hack
The trick is simple.
GitHub Actions periodically pings your Lambda, which prevents AWS from shutting down the runtime.
Language: mermaid
Step 1: The Warmup Short-Circuit
You don't want your warmup request triggering heavy logic or database queries.
Add a fast early return in your Lambda handler.
// handler.js
export const handler = async (event) => {
// Catch the warmup ping
if (event?.source === "warmup") {
console.log("The tiny Linux server is awake.");
return { statusCode: 200, body: "warm" };
}
// Your actual logic here
return {
statusCode: 200,
body: JSON.stringify({
message: "Hello from a warm Lambda!",
}),
};
};
Language: text
Step 2: Store Your Secrets
Go to:
GitHub Repo → Settings → Secrets and variables → Actions
Create a secret called:
LAMBDA_URL
Language: text
This should contain your API Gateway endpoint or Lambda Function URL.
Do not hardcode it in the workflow.
We may be committing architectural sins, but we still follow basic security practices.
Step 3: The GitHub Actions Workflow
Create:
.github/workflows/keep-lambda-warm.yml
Language: text
name: Keep Lambda Warm 🔥
on:
schedule:
- cron: "*/5 * * * *"
workflow_dispatch:
jobs:
warm-lambda:
name: Ping the tiny Linux server
runs-on: ubuntu-latest
steps:
- name: Ping Lambda via HTTP
run: |
response=$(curl -s -o /dev/null -w "%{http_code}" \
-X POST "${{ secrets.LAMBDA_URL }}" \
-H "Content-Type: application/json" \
-d '{"source": "warmup"}')
echo "Response code: $response"
if [ "$response" -ne 200 ]; then
echo "Lambda returned $response — we are completely cooked."
exit 1
fi
echo "Lambda is warm. The microVM lives."
Language: text
Step 4: Invoking a Private Lambda (Inside a VPC)
If your Lambda has no public endpoint, invoke it using the AWS CLI.
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_REGION }}
- name: Invoke Lambda directly
run: |
aws lambda invoke \
--function-name my-unnecessarily-serverless-function \
--payload '{"source": "warmup"}' \
--cli-binary-format raw-in-base64-out \
response.json
cat response.json
Language: text
The Verdict
Using GitHub Actions to keep AWS Lambdas warm is the ultimate "I refuse to pay for Provisioned Concurrency" hack.
Is it smart?
Financially, yes. It costs zero dollars.
Is it good architecture?
Absolutely not.
You are fighting the platform's core design, and the illusion collapses the moment multiple users hit your API simultaneously.
If you find yourself deploying 15 cron jobs just to keep a fleet of Lambdas alive, take a step back.
Stop fighting the cloud.
Spin up a persistent container.
Or go full Primeagen mode, open NeoVim, rewrite the whole thing in Rust, and boot in 12 milliseconds.
Until then:
Enjoy your artificially warm Firecracker microVM. 🔥