14 KiB
description
| description |
|---|
| Set up GitHub Actions CI/CD pipeline for Rust Lambda deployment |
You are helping the user set up a GitHub Actions workflow for automated Lambda deployment.
Your Task
Create a complete GitHub Actions workflow for building, testing, and deploying Rust Lambda functions.
-
Ask about deployment preferences:
- Which AWS region(s)?
- Which architecture (x86_64, arm64, or both)?
- Deploy on every push to main, or only on tags/releases?
- AWS authentication method (OIDC or access keys)?
- Single function or multiple functions?
-
Create workflow file: Create
.github/workflows/deploy-lambda.ymlwith appropriate configuration -
Set up AWS authentication:
- OIDC (recommended, more secure)
- Access keys (simpler setup)
-
Configure required secrets in GitHub repo settings
Complete Workflow Examples
Option 1: OIDC Authentication (Recommended)
name: Deploy Lambda
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
AWS_REGION: us-east-1
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run tests
run: cargo test --verbose
- name: Run clippy
run: cargo clippy -- -D warnings
- name: Check formatting
run: cargo fmt -- --check
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
id-token: write # Required for OIDC
contents: read
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.11.0
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build Lambda (ARM64)
run: cargo lambda build --release --arm64
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy to AWS Lambda
run: |
cargo lambda deploy \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--region ${{ env.AWS_REGION }} \
--arch arm64
- name: Test deployed function
run: |
aws lambda invoke \
--function-name ${{ secrets.LAMBDA_FUNCTION_NAME }} \
--payload '{"test": true}' \
response.json
cat response.json
Option 2: Access Keys Authentication
name: Deploy Lambda
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
AWS_REGION: us-east-1
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Run tests
run: cargo test --verbose
build-and-deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Cache cargo dependencies
uses: actions/cache@v4
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
with:
version: 0.11.0
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build Lambda (ARM64)
run: cargo lambda build --release --arm64
- 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: ${{ env.AWS_REGION }}
- name: Deploy to AWS Lambda
run: |
cargo lambda deploy \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--region ${{ env.AWS_REGION }} \
--arch arm64
Option 3: Multi-Architecture Build
name: Deploy Lambda
on:
push:
branches: [ main ]
release:
types: [ published ]
env:
CARGO_TERM_COLOR: always
jobs:
build-matrix:
strategy:
matrix:
include:
- arch: x86_64
aws_arch: x86_64
build_flags: ""
- arch: arm64
aws_arch: arm64
build_flags: "--arm64"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build for ${{ matrix.arch }}
run: cargo lambda build --release ${{ matrix.build_flags }} --output-format zip
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: lambda-${{ matrix.arch }}
path: target/lambda/**/*.zip
deploy:
needs: build-matrix
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
id-token: write
contents: read
strategy:
matrix:
arch: [arm64, x86_64]
steps:
- uses: actions/checkout@v4
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: lambda-${{ matrix.arch }}
path: target/lambda
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: us-east-1
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Deploy ${{ matrix.arch }}
run: |
cargo lambda deploy my-function-${{ matrix.arch }} \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--arch ${{ matrix.arch }}
Option 4: Multiple Functions
name: Deploy Lambda Functions
on:
push:
branches: [ main ]
env:
CARGO_TERM_COLOR: always
AWS_REGION: us-east-1
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --all
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
permissions:
id-token: write
contents: read
strategy:
matrix:
function:
- name: api-handler
memory: 512
timeout: 30
- name: data-processor
memory: 2048
timeout: 300
- name: event-consumer
memory: 1024
timeout: 60
steps:
- uses: actions/checkout@v4
- name: Install Rust
uses: dtolnay/rust-toolchain@stable
- name: Install Zig
uses: goto-bus-stop/setup-zig@v2
- name: Install cargo-lambda
run: pip install cargo-lambda
- name: Build ${{ matrix.function.name }}
run: cargo lambda build --release --arm64 --bin ${{ matrix.function.name }}
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Deploy ${{ matrix.function.name }}
run: |
cargo lambda deploy ${{ matrix.function.name }} \
--iam-role ${{ secrets.LAMBDA_EXECUTION_ROLE_ARN }} \
--region ${{ env.AWS_REGION }} \
--memory ${{ matrix.function.memory }} \
--timeout ${{ matrix.function.timeout }} \
--arch arm64 \
--env-var RUST_LOG=info
AWS OIDC Setup
For OIDC authentication (recommended), set up in AWS:
1. Create OIDC Provider in AWS IAM
aws iam create-open-id-connect-provider \
--url https://token.actions.githubusercontent.com \
--client-id-list sts.amazonaws.com \
--thumbprint-list 6938fd4d98bab03faadb97b34396831e3780aea1
2. Create IAM Role for GitHub Actions
# Create trust policy
cat > github-actions-trust-policy.json <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:YOUR_GITHUB_ORG/YOUR_REPO:*"
}
}
}
]
}
EOF
# Create role
aws iam create-role \
--role-name GitHubActionsLambdaDeployRole \
--assume-role-policy-document file://github-actions-trust-policy.json
# Attach policies
aws iam attach-role-policy \
--role-name GitHubActionsLambdaDeployRole \
--policy-arn arn:aws:iam::aws:policy/AWSLambda_FullAccess
# Get role ARN (save this for GitHub secrets)
aws iam get-role --role-name GitHubActionsLambdaDeployRole --query 'Role.Arn'
GitHub Secrets Configuration
Configure these secrets in GitHub repository settings (Settings → Secrets and variables → Actions):
For OIDC:
AWS_ROLE_ARN: ARN of the GitHub Actions IAM roleLAMBDA_EXECUTION_ROLE_ARN: ARN of the Lambda execution roleLAMBDA_FUNCTION_NAME(optional): Function name if different from repo
For Access Keys:
AWS_ACCESS_KEY_ID: AWS access keyAWS_SECRET_ACCESS_KEY: AWS secret keyLAMBDA_EXECUTION_ROLE_ARN: ARN of the Lambda execution role
Optional secrets:
AWS_REGION: Override default region- Environment-specific variables as needed
Advanced Patterns
Deploy on Git Tags
on:
push:
tags:
- 'v*'
# In deploy step:
- name: Get tag version
id: tag
run: echo "version=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
- name: Deploy with version tag
run: |
cargo lambda deploy \
--tags Version=${{ steps.tag.outputs.version }}
Environment-Specific Deployment
on:
push:
branches:
- main
- develop
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Set environment
id: env
run: |
if [ "${{ github.ref }}" = "refs/heads/main" ]; then
echo "name=production" >> $GITHUB_OUTPUT
echo "suffix=" >> $GITHUB_OUTPUT
else
echo "name=development" >> $GITHUB_OUTPUT
echo "suffix=-dev" >> $GITHUB_OUTPUT
fi
- name: Deploy
run: |
cargo lambda deploy my-function${{ steps.env.outputs.suffix }} \
--env-var ENVIRONMENT=${{ steps.env.outputs.name }}
Conditional Deployment
- name: Check if Lambda code changed
id: lambda-changed
uses: dorny/paths-filter@v2
with:
filters: |
lambda:
- 'src/**'
- 'Cargo.toml'
- 'Cargo.lock'
- name: Deploy Lambda
if: steps.lambda-changed.outputs.lambda == 'true'
run: cargo lambda deploy
Slack Notifications
- name: Notify Slack on success
if: success()
uses: slackapi/slack-github-action@v1
with:
payload: |
{
"text": "Lambda deployed successfully: ${{ github.repository }}"
}
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
Performance Optimizations
Faster Builds with Caching
- name: Cache Rust
uses: Swatinem/rust-cache@v2
with:
cache-on-failure: true
Parallel Jobs
jobs:
test:
# Testing job
build:
# Build job (independent of test for speed)
deploy:
needs: [test, build] # Only deploy if both succeed
Troubleshooting CI/CD
Issue: "cargo-lambda: command not found"
Solution: Ensure pip install cargo-lambda runs before use
Issue: "Zig not found"
Solution: Add goto-bus-stop/setup-zig@v2 step
Issue: "AWS credentials not configured"
Solution: Verify secrets are set and aws-actions step is included
Issue: Build caching not working
Solution: Use Swatinem/rust-cache@v2 for better Rust caching
Issue: Deployment fails intermittently
Solution: Add retry logic or use aws lambda wait function-updated
Testing the Workflow
-
Create workflow file in
.github/workflows/deploy-lambda.yml -
Configure secrets in GitHub settings
-
Push to trigger:
git add .github/workflows/deploy-lambda.yml git commit -m "Add Lambda deployment workflow" git push -
Monitor in GitHub Actions tab
-
Check logs for any issues
Best Practices
- Always run tests before deployment
- Use OIDC instead of long-lived credentials
- Cache dependencies for faster builds
- Deploy on main branch only, test on PRs
- Use matrix builds for multiple architectures
- Tag deployments with version info
- Add notifications for deployment status
- Set up monitoring and alerts in AWS
- Use environments for production deployments
- Document secrets needed in README
After creating the workflow, guide the user through:
- Setting up required secrets
- Testing the workflow
- Monitoring deployments
- Handling failures