Spry with AWS Lambda: Deploying Dart Servers as Serverless Functions
Spry with AWS Lambda: Running Dart Servers as Serverless Functions
Deploy your Spry Dart applications on AWS Lambda with custom runtimes, automatic scaling, and zero‑infrastructure management.
AWS Lambda is the leading serverless compute service, allowing you to run code without provisioning or managing servers. With a custom runtime and Docker support, you can package Spry applications as Lambda functions, combining Dart's performance with Lambda's scalability.
In this tutorial, you’ll learn:
- Why run Dart on AWS Lambda? Serverless benefits and use cases
- How to create a custom Dart runtime for AWS Lambda
- Packaging Spry applications as Lambda container images
- Integrating with API Gateway for HTTP endpoints
- Environment configuration, logging, and monitoring
- Cost optimization and performance tuning
All examples are based on a real Spry microservice and can be adapted for your own projects.
Why AWS Lambda for Spry?
- Serverless scaling – Automatic scaling from zero to thousands of concurrent executions
- Cost efficiency – Pay only for compute time (per‑millisecond billing)
- Managed infrastructure – No servers to patch, monitor, or secure
- Event‑driven architecture – Integrate with S3, SQS, DynamoDB, and 200+ AWS services
- Docker support – Package any runtime as a container image
- Global deployment – Deploy Lambda functions across AWS regions
Prerequisites
- An AWS account with appropriate IAM permissions
- AWS CLI configured locally (
aws configure) - Docker installed (for container image builds)
- Basic familiarity with AWS Lambda and API Gateway
- Spry application ready for serverless deployment
Project Structure
spry‑lambda‑example/
├── Dockerfile.lambda # Docker image for Lambda custom runtime
├── bootstrap # Custom runtime bootstrap script (shell)
├── lambda/
│ ├── function.json # Lambda function configuration
│ ├── environment.env # Environment variables
│ └── layers/ # Optional Lambda layers
├── sam/
│ ├── template.yaml # AWS SAM template
│ └── build‑spec.yaml # CI/CD build specification
├── cdk/
│ └── lib/ # AWS CDK constructs (TypeScript)
├── lib/
│ └── main.dart # Spry application entry point
├── scripts/
│ └── deploy‑lambda.sh # Deployment automation
└── README.md
1. Custom Dart Runtime for AWS Lambda
AWS Lambda supports custom runtimes via a bootstrap executable. For Dart, we create a lightweight shell script that launches the Dart VM with our compiled Spry application.
Bootstrap script (bootstrap):
#!/bin/sh
set -euo pipefail
# Set Dart runtime environment
export DART_VM_OPTIONS="--disable-service-auth-codes"
export PORT=8080
# Execute the compiled Dart binary
exec /app/bin/spry‑server
Make it executable:
chmod +x bootstrap
2. Docker Image for Lambda
Create a Dockerfile that follows the Lambda container image specification:
# ──────────────────────────────────────────────────────────────────────────────
# Builder stage (AOT compilation)
# ──────────────────────────────────────────────────────────────────────────────
FROM dart:stable AS builder
WORKDIR /app
COPY pubspec.yaml pubspec.lock ./
RUN dart pub get --offline
COPY . .
RUN dart compile exe lib/main.dart -o /app/bin/spry‑server
# ──────────────────────────────────────────────────────────────────────────────
# Runtime stage (Lambda‑compatible)
# ──────────────────────────────────────────────────────────────────────────────
FROM public.ecr.aws/lambda/provided:al2023
# Install runtime dependencies
RUN yum install -y ca‑certificates && yum clean all
# Copy compiled binary and bootstrap
COPY --from=builder /app/bin/spry‑server /app/bin/
COPY bootstrap /app/
WORKDIR /app
# Set Lambda handler (custom runtime uses bootstrap)
CMD ["/app/bootstrap"]
Build and push to Amazon ECR:
aws ecr create‑repository --repository‑name spry‑lambda --region us‑east‑1
aws ecr get‑login‑password --region us‑east‑1 | docker login --username AWS --password‑stdin <account‑id>.dkr.ecr.us‑east‑1.amazonaws.com
docker build -f Dockerfile.lambda -t <account‑id>.dkr.ecr.us‑east‑1.amazonaws.com/spry‑lambda:latest .
docker push <account‑id>.dkr.ecr.us‑east‑1.amazonaws.com/spry‑lambda:latest
3. Spry Application with Lambda Support
Update your Spry application to handle Lambda's event‑based invocation:
// lib/main.dart
import 'dart:io';
import 'package:spry/spry.dart';
import 'package:spry_lambda/spry_lambda.dart'; // Hypothetical adapter
Future<void> main() async {
final app = Application();
// Lambda‑specific middleware
app.use((request, next) async {
// Add Lambda context to request
final context = request.context['lambdaContext'];
if (context != null) {
// Log Lambda request ID
print('Lambda Request ID: ${context['awsRequestId']}');
}
return next(request);
});
// Health check for Lambda lifecycle
app.get('/health', (request) async {
return Response.json({
'status': 'healthy',
'timestamp': DateTime.now().toIso8601String(),
'runtime': 'Dart on AWS Lambda',
});
});
// Your application routes
app.get('/', (request) async {
return Response.text('Spry on AWS Lambda 🚀');
});
// Lambda handler entry point
final handler = LambdaHandler(app);
await handler.serve();
}
4. AWS SAM Deployment
AWS Serverless Application Model (SAM) simplifies Lambda deployment.
SAM template (sam/template.yaml):
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
SpryLambdaFunction:
Type: AWS::Serverless::Function
Properties:
PackageType: Image
ImageUri: <account‑id>.dkr.ecr.us‑east‑1.amazonaws.com/spry‑lambda:latest
MemorySize: 512
Timeout: 30
Environment:
Variables:
LOG_LEVEL: info
Events:
HttpApi:
Type: HttpApi
Properties:
Path: /{proxy+}
Method: ANY
Outputs:
SpryApiUrl:
Description: "HTTP API endpoint URL"
Value: !Sub "https://${ServerlessHttpApi}.execute‑api.${AWS::Region}.amazonaws.com"
Deploy with SAM:
sam build
sam deploy --guided
5. AWS CDK Deployment (Alternative)
For infrastructure‑as‑code with TypeScript, use AWS CDK:
// cdk/lib/spry‑lambda‑stack.ts
import * as cdk from 'aws‑cdk‑lib';
import * as lambda from 'aws‑cdk‑lib/aws‑lambda';
import * as apigw from 'aws‑cdk‑lib/aws‑apigateway';
export class SpryLambdaStack extends cdk.Stack {
constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const spryFunction = new lambda.DockerImageFunction(this, 'SpryFunction', {
code: lambda.DockerImageCode.fromEcr(
'<account‑id>.dkr.ecr.us‑east‑1.amazonaws.com/spry‑lambda:latest'
),
memorySize: 512,
timeout: cdk.Duration.seconds(30),
environment: {
LOG_LEVEL: 'info',
},
});
new apigw.LambdaRestApi(this, 'SpryApi', {
handler: spryFunction,
proxy: true,
});
}
}
6. Testing and Monitoring
Local testing with SAM CLI:
sam local start‑api
curl http://localhost:3000/
CloudWatch Logs:
aws logs tail /aws/lambda/SpryLambdaFunction --follow
Lambda Insights:
Enable Lambda Insights for performance monitoring.
7. Performance Optimization
- Cold start mitigation: Use Provisioned Concurrency for critical paths
- Memory tuning: Adjust memory size (affects CPU and network performance)
- Layer caching: Package Dart runtime as a Lambda layer for faster deployments
- Dependency pruning: Remove unused packages to reduce binary size
- AOT compilation: Use
dart compile exefor faster startup
8. Cost Estimation
Example monthly cost for 1 million requests:
- Requests: 1M × $0.20 per million = $0.20
- Compute: 1M × 500ms × 512MB × $0.0000166667 per GB‑second ≈ $4.27
- Total: ~$4.47 per month
9. Troubleshooting
Common Issues
- Cold starts: Use Provisioned Concurrency or reduce binary size
- Timeout errors: Increase timeout or optimize Dart VM startup
- Permission errors: Ensure IAM roles have correct policies
- Container image too large: Use multi‑stage builds and strip unused assets
- Dart VM memory growth: Monitor memory usage and set appropriate limits
Debugging Commands
# View Lambda logs
aws logs get‑log‑events --log‑group‑name /aws/lambda/SpryLambdaFunction
# Invoke function directly
aws lambda invoke --function‑name SpryLambdaFunction response.json
# Update function configuration
aws lambda update‑function‑configuration --function‑name SpryLambdaFunction --memory‑size 1024
Conclusion
AWS Lambda provides a powerful, scalable platform for running Spry Dart applications in production. With custom runtimes and container support, you can leverage Dart's performance while benefiting from serverless economics.
This tutorial covered essential patterns—custom runtimes, Docker packaging, SAM/CDK deployment, and optimization—to get your Spry application running on AWS Lambda.
Next Steps:
- Implement CI/CD – Automate builds and deployments with GitHub Actions or AWS CodePipeline
- Add authentication – Integrate with AWS Cognito or API Gateway authorizers
- Multi‑region deployment – Deploy across regions for latency reduction
- Event‑driven architecture – Connect Lambda to SQS, SNS, or EventBridge
- Security hardening – Apply least‑privilege IAM roles and network isolation
All code from this tutorial is available in the spry‑lambda‑example GitHub repository.
Happy serverless building! 🚀
This is a draft tutorial. Final content will include more detailed examples, troubleshooting guides, and best practices for production deployments.