Configuring Website Deployment on AWS (EC2/ECS/Lambda)
AWS offers three main deployment paths for web applications: EC2 (virtual machines), ECS (containers), Lambda (serverless). Choice depends on scale, management requirements, and load pattern.
Static Site: S3 + CloudFront
Cheapest and most scalable option for SPA and static content:
# Create S3 bucket
aws s3 mb s3://myapp-production --region eu-west-1
# Configure as website
aws s3 website s3://myapp-production \
--index-document index.html \
--error-document index.html
# Deploy
aws s3 sync dist/ s3://myapp-production \
--delete \
--cache-control "public, max-age=31536000, immutable" \
--exclude "index.html"
aws s3 cp dist/index.html s3://myapp-production \
--cache-control "no-cache, no-store, must-revalidate"
# Invalidate CloudFront cache
aws cloudfront create-invalidation \
--distribution-id E1234567890 \
--paths "/*"
EC2: PHP/Laravel Application
# Launch template for Auto Scaling Group
aws ec2 create-launch-template \
--launch-template-name myapp-lt \
--version-description v1 \
--launch-template-data '{
"ImageId": "ami-0c55b159cbfafe1f0",
"InstanceType": "t3.medium",
"KeyName": "myapp-key",
"SecurityGroupIds": ["sg-0123456789"],
"UserData": "'"$(base64 user-data.sh)"'",
"IamInstanceProfile": {"Name": "EC2InstanceProfile"}
}'
# user-data.sh — initialization on EC2 startup
#!/bin/bash
apt update && apt install -y nginx php8.3-fpm composer
# Download and configure app
git clone https://github.com/user/myapp.git /var/www/app
cd /var/www/app
composer install --no-dev --optimize-autoloader
cp .env.production .env
php artisan key:generate
php artisan migrate --force
ECS Fargate: Containerized Application
// task-definition.json
{
"family": "myapp",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "512",
"memory": "1024",
"containerDefinitions": [
{
"name": "web",
"image": "123456789.dkr.ecr.eu-west-1.amazonaws.com/myapp:latest",
"portMappings": [{ "containerPort": 80 }],
"environment": [
{ "name": "APP_ENV", "value": "production" }
],
"secrets": [
{ "name": "DB_PASSWORD", "valueFrom": "arn:aws:secretsmanager:..." }
],
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost/health || exit 1"],
"interval": 30,
"timeout": 5
},
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/myapp",
"awslogs-region": "eu-west-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]
}
# Register task definition
aws ecs register-task-definition --cli-input-json file://task-definition.json
# Update service (Rolling Update)
aws ecs update-service \
--cluster myapp-cluster \
--service myapp-service \
--force-new-deployment \
--task-definition myapp:latest
Lambda: Serverless API
# handler.py
import json
import boto3
def lambda_handler(event, context):
path = event.get('path', '/')
method = event.get('httpMethod', 'GET')
if path == '/api/health':
return {
'statusCode': 200,
'headers': {'Content-Type': 'application/json'},
'body': json.dumps({'status': 'ok'}),
}
# Routing
return {'statusCode': 404, 'body': 'Not found'}
# serverless.yml (Serverless Framework)
service: myapp-api
provider:
name: aws
runtime: python3.11
region: eu-west-1
environment:
DB_HOST: ${ssm:/myapp/db-host}
functions:
api:
handler: handler.lambda_handler
events:
- httpApi:
path: /api/{proxy+}
method: ANY
timeout: 29
memorySize: 512
plugins:
- serverless-python-requirements
GitHub Actions CI/CD for ECS
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- 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: eu-west-1
- uses: aws-actions/amazon-ecr-login@v2
- name: Build and push to ECR
run: |
docker build -t $ECR_REGISTRY/myapp:$IMAGE_TAG .
docker push $ECR_REGISTRY/myapp:$IMAGE_TAG
- name: Deploy to ECS
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: task-definition.json
service: myapp-service
cluster: myapp-cluster
wait-for-service-stability: true
Implementation Timeline
| Option | Timeline |
|---|---|
| S3 + CloudFront (SPA) | 1–2 days |
| EC2 + ALB | 3–5 days |
| ECS Fargate | 5–7 days |
| Lambda + API Gateway | 4–7 days |







