Stop Building on Your Laptop: Automating Lambda Builds with GitHub Actions

 

Stop Building on Your Laptop: Automating Lambda Builds with GitHub Actions

"It works on my machine" is not a deployment strategy.





If you are the only person on your team who knows the exact docker run command to deploy the production Lambda, you are a liability. You have a "Bus Factor" of 1. If you get sick, your laptop dies, or you go on vacation, production is frozen.

Manual deployments are fragile. They depend on your local Docker version, your internet connection, and your uncommitted local changes.

The solution is to move your terminal to the cloud.

This guide will show you how to take the bulletproof Docker build we mastered in previous articles and automate it inside a GitHub Actions pipeline using modern, secure OIDC authentication.


The Shift: Moving the Terminal to the Cloud

CI/CD (Continuous Integration / Continuous Deployment) sounds complex, but it is simple: It is a computer in the cloud running the commands you used to type manually.

Instead of:
Code -> Your Laptop -> AWS

The flow becomes:
Code -> GitHub -> GitHub Runner (The Cloud Terminal) -> AWS

This guarantees that what is in the repo is exactly what runs in production.


The Security Upgrade: Stop Using Access Keys

Before we write the pipeline, we must fix a security hole.

The Old Way: Creating an IAM User, generating AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, and pasting them into GitHub Secrets.

  • The Risk: Keys last forever. If they leak, an attacker owns your account.

The Right Way: OIDC (OpenID Connect).
This is the modern standard. GitHub asks AWS for a temporary token for a specific build.

The Setup (One-Time):

You need to create an IAM Role in AWS that trusts your specific GitHub repository.

Trust Policy Example (JSON):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::123456789012:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
          "token.actions.githubusercontent.com:sub": "repo:my-org/my-repo:ref:refs/heads/main"
        }
      }
    }
  ]
}

Replace 123456789012 with your Account ID and my-org/my-repo with your GitHub details.


The Workflow: A Production-Grade Pipeline

Create a file in your repo at .github/workflows/lambda-deploy.yml.

This pipeline assumes we are building a Deployment Package (Code + Dependencies together), which is the standard starting point for most teams.

name: Deploy Lambda

on:
  push:
    branches:
      - main  # Only run when code is pushed to main

permissions:
  id-token: write   # Required for OIDC authentication
  contents: read    # Required to checkout code

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      # Optimization: Cache NPM modules to speed up builds
      - name: Cache Node Modules
        id: cache-npm
        uses: actions/cache@v3
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}

      - name: Configure AWS Credentials (OIDC)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789012:role/MyGitHubDeployRole
          aws-region: us-east-1

      - name: Build with Docker (The Golden Command)
        # We mount the GitHub Runner's npm cache (~/.npm) into the Docker container
        # so 'npm ci' doesn't have to re-download the internet every time.
        run: |
          # Ensure cache directory exists to avoid Docker mount errors
          mkdir -p ~/.npm

          docker run --rm \
            --platform linux/amd64 \
            -v "$PWD":/var/task \
            -v "$HOME/.npm":/root/.npm \
            amazonlinux:2023 \
            bash -c "npm ci && zip -r function.zip ."

      - name: Deploy to AWS Lambda
        run: |
          aws lambda update-function-code \
            --function-name my-production-function \
            --zip-file fileb://function.zip

Critical Architecture Note:

In the Docker command above, we used --platform linux/amd64.

  • If your Lambda runs on Intel (x86_64), keep it as is.
  • If your Lambda runs on Graviton (ARM64), you must change it to --platform linux/arm64.

Pro Tips for Power Users

  1. Branch Protection: Go to your GitHub Repo Settings -> Branches. Enable "Require status checks to pass before merging." This stops anyone (even you) from breaking main.
  2. OIDC Flexibility: The JSON policy above is strict (only main branch). If you want to allow any branch to deploy to a dev environment, change StringEquals to StringLike and use a wildcard: "repo:my-org/my-repo:*".
  3. Code vs. Layers: This pipeline builds a "Fat Zip" (Code + Deps). If you followed our Layers Guide, you would split this into two workflows: one that builds/publishes the Layer (rarely changes), and one that zips/deploys the Code (changes often).

Conclusion

By moving your build to GitHub Actions, you eliminate the "Human Element."

You no longer have to worry if your colleague is building on an M5 Mac vs. an Intel PC, or if they forgot to run npm install. The pipeline is the source of truth.

It works on the machine that matters: The Cloud.


Aaron Rose is a software engineer and technology writer at tech-reader.blog and the author of Think Like a Genius.

Comments

Popular posts from this blog

The New ChatGPT Reason Feature: What It Is and Why You Should Use It

Insight: The Great Minimal OS Showdown—DietPi vs Raspberry Pi OS Lite

Raspberry Pi Connect vs. RealVNC: A Comprehensive Comparison