LocalStack: Compute Services Guide
Hands-on Practice for EC2 and Lambda
Note: Bash commands in this guide work on Linux, macOS, and Windows (via Git Bash, WSL, or PowerShell with bash support).
Overview
This guide provides hands-on exercises for AWS Compute Services using LocalStack's FREE tier.
Services Available in FREE Tier
| Service | Available | Notes |
|---|---|---|
| EC2 | ✅ Yes | Full mock implementation |
| Lambda | ✅ Yes | Full Python/Node.js runtimes |
| Elastic Beanstalk | ❌ No | Requires Base tier |
Prerequisites
bash
# Start LocalStack with compute services
docker run --rm -it \
-p 4566:4566 \
-e SERVICES=lambda,ec2,iam,sts \
localstack/localstack
# Or update docker-compose.yml1. AWS Lambda (Serverless Compute)
Create a Lambda Function
bash
# Create project directory
mkdir lambda-demo && cd lambda-demo
# Create handler.py
cat > handler.py << 'EOF'
import json
def lambda_handler(event, context):
# Log the incoming event
print("Received event:", json.dumps(event))
# Extract data from event
name = event.get('name', 'World')
# Return response
return {
'statusCode': 200,
'body': json.dumps({
'message': f'Hello, {name}!'
})
}
EOF
# Package the function
zip function.zip handler.py
# Create IAM role (required for Lambda)
aws --endpoint-url=http://localhost:4566 iam create-role \
--role-name lambda-execution-role \
--assume-role-policy-document '{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"
}]
}'
# Create the Lambda function
aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name hello-world \
--runtime python3.9 \
--role arn:aws:iam::000000000000:role/lambda-execution-role \
--handler handler.lambda_handler \
--zip-file fileb://function.zip \
--timeout 10 \
--memory-size 128
# List functions
aws --endpoint-url=http://localhost:4566 lambda list-functions
# Get function details
aws --endpoint-url=http://localhost:4566 lambda get-function \
--function-name hello-worldInvoke Lambda Function
bash
# Synchronous invocation
aws --endpoint-url=http://localhost:4566 lambda invoke \
--function-name hello-world \
--payload '{"name": "LocalStack"}' \
response.json
# View the response
cat response.json
# Asynchronous invocation
aws --endpoint-url=http://localhost:4566 lambda invoke \
--function-name hello-world \
--invocation-type Event \
--payload '{"name": "Async"}' \
response.jsonUpdate Lambda Function
bash
# Update the code
cat > handler.py << 'EOF'
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Updated: Hello from Lambda!',
'timestamp': str(context.aws_request_id)
})
}
EOF
# Re-package
zip -u function.zip handler.py
# Update function code
aws --endpoint-url=http://localhost:4566 lambda update-function-code \
--function-name hello-world \
--zip-file fileb://function.zipLambda with Environment Variables
bash
# Update function configuration
aws --endpoint-url=http://localhost:4566 lambda update-function-configuration \
--function-name hello-world \
--environment Variables={TABLE_NAME=Users,REGION=us-east-1}
# Get configuration
aws --endpoint-url=http://localhost:4566 lambda get-function-configuration \
--function-name hello-worldDelete Lambda Function
bash
# Delete the function
aws --endpoint-url=http://localhost:4566 lambda delete-function \
--function-name hello-world2. Amazon EC2 (Virtual Servers)
List Available Instance Types
bash
# Describe instance types (mock data)
aws --endpoint-url=http://localhost:4566 ec2 describe-instance-types \
--query 'InstanceTypes[?InstanceType==`t2.micro`]'Create Key Pair
bash
# Create a key pair
aws --endpoint-url=http://localhost:4566 ec2 create-key-pair \
--key-name my-key-pair \
--query 'KeyMaterial' \
--output text > my-key-pair.pem
# Set permissions
chmod 400 my-key-pair.pem
# List key pairs
aws --endpoint-url=http://localhost:4566 ec2 describe-key-pairsCreate Security Group
bash
# Create security group
aws --endpoint-url=http://localhost:4566 ec2 create-security-group \
--group-name my-security-group \
--description "My security group"
# Add inbound rule (SSH)
aws --endpoint-url=http://localhost:4566 ec2 authorize-security-group-ingress \
--group-name my-security-group \
--protocol tcp \
--port 22 \
--cidr 0.0.0.0/0
# Add inbound rule (HTTP)
aws --endpoint-url=http://localhost:4566 ec2 authorize-security-group-ingress \
--group-name my-security-group \
--protocol tcp \
--port 80 \
--cidr 0.0.0.0/0
# Describe security groups
aws --endpoint-url=http://localhost:4566 ec2 describe-security-groups \
--group-names my-security-groupLaunch EC2 Instance
bash
# Note: In LocalStack, instances are mocked
# This command creates a mock instance record
# Launch instance
INSTANCE_ID=$(aws --endpoint-url=http://localhost:4566 ec2 run-instances \
--image-id ami-12345678 \
--count 1 \
--instance-type t2.micro \
--key-name my-key-pair \
--security-groups my-security-group \
--query 'Instances[0].InstanceId' \
--output text)
echo "Instance ID: $INSTANCE_ID"
# Describe instance
aws --endpoint-url=http://localhost:4566 ec2 describe-instances \
--instance-ids $INSTANCE_ID
# List all instances
aws --endpoint-url=http://localhost:4566 ec2 describe-instancesStop and Terminate Instance
bash
# Stop instance
aws --endpoint-url=http://localhost:4566 ec2 stop-instances \
--instance-ids $INSTANCE_ID
# Start instance
aws --endpoint-url=http://localhost:4566 ec2 start-instances \
--instance-ids $INSTANCE_ID
# Terminate instance
aws --endpoint-url=http://localhost:4566 ec2 terminate-instances \
--instance-ids $INSTANCE_IDWorking with AMIs
bash
# Create AMI (mock)
aws --endpoint-url=http://localhost:4566 ec2 create-image \
--instance-id $INSTANCE_ID \
--name "my-ami"
# Describe images
aws --endpoint-url=http://localhost:4566 ec2 describe-images3. Hands-On Projects
Project 1: REST API with Lambda
bash
# Create API Gateway
API_ID=$(aws --endpoint-url=http://localhost:4566 apigateway create-rest-api \
--name "lambda-api" \
--query 'id' \
--output text)
# Create resource
RESOURCE_ID=$(aws --endpoint-url=http://localhost:4566 apigateway create-resource \
--rest-api-id $API_ID \
--parent-id $ROOT_ID \
--path-part "hello" \
--query 'id' \
--output text)
# Create Lambda function
cat > api_handler.py << 'EOF'
import json
def lambda_handler(event, context):
return {
'statusCode': 200,
'body': json.dumps({'message': 'Hello from API Gateway!'})
}
EOF
zip function.zip api_handler.py
aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name api-handler \
--runtime python3.9 \
--role arn:aws:iam::000000000000:role/test-role \
--handler api_handler.lambda_handler \
--zip-file fileb://function.zip
# Test the complete flow
echo "API created: $API_ID"
echo "Resource created: $RESOURCE_ID"Project 2: Lambda with DynamoDB
bash
# Create DynamoDB table
aws --endpoint-url=http://localhost:4566 dynamodb create-table \
--table-name Visitors \
--attribute-definitions AttributeName=id,AttributeType=S \
--key-schema AttributeName=id,KeyType=HASH \
--billing-mode PAY_PER_REQUEST
# Create Lambda that writes to DynamoDB
cat > visitors_handler.py << 'EOF'
import json
import boto3
import os
dynamodb = boto3.resource('dynamodb', endpoint_url=os.environ['AWS_ENDPOINT_URL'])
table = dynamodb.Table('Visitors')
def lambda_handler(event, context):
# Put item
table.put_item(Item={
'id': context.aws_request_id,
'timestamp': str(context.invoked_function_arn)
})
# Scan table
response = table.scan()
return {
'statusCode': 200,
'body': json.dumps({
'message': 'Visit recorded!',
'total_visitors': response['Count']
})
}
EOF
zip function.zip visitors_handler.py
aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name visitor-counter \
--runtime python3.9 \
--role arn:aws:iam::000000000000:role/test-role \
--handler visitors_handler.lambda_handler \
--zip-file fileb://function.zip \
--environment Variables={AWS_ENDPOINT_URL=http://localhost:4566}
# Invoke the function
aws --endpoint-url=http://localhost:4566 lambda invoke \
--function-name visitor-counter \
response.json
cat response.jsonProject 3: Lambda with S3
bash
# Create S3 bucket
aws --endpoint-url=http://localhost:4566 s3 mb s3://lambda-bucket
# Create Lambda that processes S3 events
cat > s3_handler.py << 'EOF'
import json
import boto3
s3 = boto3.client('s3', endpoint_url='http://localhost:4566')
def lambda_handler(event, context):
# Process S3 event
for record in event['Records']:
bucket = record['s3']['bucket']['name']
key = record['s3']['object']['key']
# Get object
response = s3.get_object(Bucket=bucket, Key=key)
content = response['Body'].read().decode('utf-8')
print(f"Processed file: {key}")
print(f"Content: {content}")
return {'statusCode': 200}
EOF
zip function.zip s3_handler.py
aws --endpoint-url=http://localhost:4566 lambda create-function \
--function-name s3-processor \
--runtime python3.9 \
--role arn:aws:iam::000000000000:role/test-role \
--handler s3_handler.lambda_handler \
--zip-file fileb://function.zip
# Test with sample event
echo "Test file" > test.txt
aws --endpoint-url=http://localhost:4566 s3 cp test.txt s3://lambda-bucket/
# Create S3 event notification (if supported)
# Note: Full S3 event support may vary in LocalStack4. Common Patterns
Error Handling in Lambda
python
# error_handler.py
import json
def lambda_handler(event, context):
try:
# Your logic here
result = process_data(event)
return {
'statusCode': 200,
'body': json.dumps(result)
}
except Exception as e:
print(f"Error: {str(e)}")
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)})
}
def process_data(event):
# Your processing logic
return {'status': 'success'}Lambda Layers
bash
# Create a layer
mkdir -p my-layer/python
cd my-layer/python
# Create layer library
cat > utils.py << 'EOF'
def helper_function():
return "Helper result"
EOF
cd ../
zip -r layer.zip python/
# Publish layer
aws --endpoint-url=http://localhost:4566 lambda publish-layer-version \
--layer-name my-layer \
--zip-file fileb://layer.zip \
--compatible-runtimes python3.9
# List layers
aws --endpoint-url=http://localhost:4566 lambda list-layersLambda Aliases and Versions
bash
# Publish version
aws --endpoint-url=http://localhost:4566 lambda publish-version \
--function-name hello-world
# Create alias
aws --endpoint-url=http://localhost:4566 lambda create-alias \
--function-name hello-world \
--function-version 1 \
--name PROD
# Update alias
aws --endpoint-url=http://localhost:4566 lambda update-alias \
--function-name hello-world \
--function-version 2 \
--name PROD5. Practice Exercises
Exercise 1: Basic Lambda
- Create a Lambda function that adds two numbers
- Test with different inputs
- Verify the output
python
def lambda_handler(event, context):
a = event.get('a', 0)
b = event.get('b', 0)
return {
'result': a + b
}Exercise 2: Lambda with Environment Variables
- Create Lambda function that reads environment variables
- Set DB_HOST, DB_PORT as environment variables
- Access them in your function
Exercise 3: EC2 Instance Management
- Create a security group
- Launch a mock EC2 instance
- Stop and start the instance
- Terminate the instance
Exercise 4: Error Scenarios
- Create Lambda with invalid code
- Test error handling
- Check CloudWatch Logs (if available)
6. Cleanup Commands
bash
# Delete all Lambda functions
aws --endpoint-url=http://localhost:4566 lambda list-functions \
--query 'Functions[].FunctionName' \
--output text | xargs -I {} aws --endpoint-url=http://localhost:4566 lambda delete-function --function-name {}
# Terminate all EC2 instances
aws --endpoint-url=http://localhost:4566 ec2 describe-instances \
--query 'Reservations[].Instances[].InstanceId' \
--output text | xargs -I {} aws --endpoint-url=http://localhost:4566 ec2 terminate-instances --instance-ids {}
# Delete all security groups
aws --endpoint-url=http://localhost:4566 ec2 describe-security-groups \
--query 'SecurityGroups[?GroupName!=`default`].GroupName' \
--output text | xargs -I {} aws --endpoint-url=http://localhost:4566 ec2 delete-security-group --group-name {}
# Delete all key pairs
aws --endpoint-url=http://localhost:4566 ec2 describe-key-pairs \
--query 'KeyPairs[].KeyName' \
--output text | xargs -I {} aws --endpoint-url=http://localhost:4566 ec2 delete-key-pair --key-name {}Additional Resources
Documentation
DigitalCloud Cheat Sheets
Practice Resources
Last Updated: January 2026