Zum Hauptinhalt springen
Echtzeit-Radar & Feeds
Alle RSS Feeds ➔
👥 Community & Social
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Windows Tipps & SecurityGrafikkarte vor Überhitzung schützen: So geht’s(25.09.2026 um 08:00 Uhr)
••••••••••
Intelligence View
⚡ tsecurity.de Intelligence

Deploying FastAPI to AWS: Part 2 - Containerizing with ECS Fargate

This is Part 2 of a 3-part series. In Part 1, we deployed FastAPI using EC2. Now let's see how containers change the game.* After deploying my FastAPI journal API using EC2 (covered in Part 1, I started running into some operational…

0
↗ Quelle (dev.to)
Reagiere als Erste:r — dein Feedback zählt!

This is Part 2 of a 3-part series. In Part 1, we deployed FastAPI using EC2. Now let's see how containers change the game.*






After deploying my FastAPI journal API using EC2 (covered in Part 1, I started running into some operational challenges. Manual scaling, server maintenance, and the "it works on my machine" problem made me realize why so many teams are moving to containers.



In this part, we'll take the same FastAPI application and deploy it using ECS Fargate with RDS. You'll see how this approach solves many of the challenges we faced with EC2.






Why Containers Changed Everything for Me



After a few weeks of managing EC2 instances manually, I experienced:




  • My app crashed during a traffic spike (no auto-scaling)

  • A deployment broke production (environment differences)

  • Spending weekends doing server maintenance

  • Debugging issues that only happened in production



Containers solved all of these problems. Here's how.






What We're Building



Same FastAPI journal API, but now with:




Internet → Application Load Balancer → ECS Fargate Tasks → RDS PostgreSQL
↓
CloudWatch Logs






This architecture gives us:





  • Automatic scaling based on traffic


  • High availability across multiple zones


  • Easy deployments with zero downtime


  • Consistent environments from dev to production






Step 1: Containerizing the FastAPI Application



First, let's create a Dockerfile for our journal API:




FROM python:3.11-slim

# Set working directory
WORKDIR /app

# Install system dependencies
RUN apt-get update && apt-get install -y \
gcc \
curl \
&& rm -rf /var/lib/apt/lists/*

# Copy requirements and install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY . .

# Create non-root user for security
RUN useradd --create-home --shell /bin/bash app \
&& chown -R app:app /app
USER app

# Expose port
EXPOSE 8000

# Health check for ECS
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1

# Run the application
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]









Add a Health Check Endpoint



ECS needs a way to check if your container is healthy. Add this to your FastAPI app:




# In your main.py or create a new health.py
from fastapi import APIRouter

router = APIRouter()

@router.get("/health")
async def health_check():
return {"status": "healthy"}

# Add to your main app
app.include_router(router)









Test Locally



Before deploying, make sure your container works:




# Build the image
docker build -t journal-api .

# Run locally
docker run -p 8000:8000 --env-file .env journal-api

# Test it
curl http://localhost:8000/health









Step 2: Set Up AWS Infrastructure






Create ECR Repository



We need a place to store our Docker images:




# Create ECR repository
aws ecr create-repository --repository-name journal-api

# Get login token and login
aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin your-account-id.dkr.ecr.us-east-1.amazonaws.com









Push Your Image






# Tag your image
docker tag journal-api:latest your-account-id.dkr.ecr.us-east-1.amazonaws.com/journal-api:latest

# Push to ECR
docker push your-account-id.dkr.ecr.us-east-1.amazonaws.com/journal-api:latest









Create RDS Database



Instead of managing PostgreSQL ourselves, let's use RDS:




# Create DB subnet group
aws rds create-db-subnet-group \
--db-subnet-group-name journal-db-subnet-group \
--db-subnet-group-description "Subnet group for journal database" \
--subnet-ids subnet-xxxxxxxxx subnet-yyyyyyyyy

# Create RDS instance
aws rds create-db-instance \
--db-instance-identifier journal-db \
--db-instance-class db.t3.micro \
--engine postgres \
--engine-version 15.4 \
--master-username postgres \
--master-user-password YourSecurePassword123 \
--allocated-storage 20 \
--vpc-security-group-ids sg-xxxxxxxxx \
--db-subnet-group-name journal-db-subnet-group \
--backup-retention-period 7 \
--multi-az









Step 3: Create ECS Cluster and Task Definition






Create ECS Cluster






aws ecs create-cluster --cluster-name journal-api-cluster









Create Task Definition



This is where we define how our container should run:




{
"family": "journal-api-task",
"networkMode": "awsvpc",
"requiresCompatibilities": ["FARGATE"],
"cpu": "256",
"memory": "512",
"executionRoleArn": "arn:aws:iam::your-account:role/ecsTaskExecutionRole",
"taskRoleArn": "arn:aws:iam::your-account:role/ecsTaskRole",
"containerDefinitions": [
{
"name": "journal-api",
"image": "your-account-id.dkr.ecr.us-east-1.amazonaws.com/journal-api:latest",
"portMappings": [
{
"containerPort": 8000,
"protocol": "tcp"
}
],
"essential": true,
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/journal-api",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
},
"environment": [
{
"name": "POSTGRES_HOST",
"value": "your-rds-endpoint.amazonaws.com"
},
{
"name": "POSTGRES_PORT",
"value": "5432"
},
{
"name": "POSTGRES_DB",
"value": "journal_db"
}
],
"secrets": [
{
"name": "POSTGRES_USER",
"valueFrom": "arn:aws:secretsmanager:us-east-1:account:secret:db-user"
},
{
"name": "POSTGRES_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:us-east-1:account:secret:db-password"
},
{
"name": "SECRET_KEY",
"valueFrom": "arn:aws:secretsmanager:us-east-1:account:secret:jwt-secret"
}
],
"healthCheck": {
"command": ["CMD-SHELL", "curl -f http://localhost:8000/health || exit 1"],
"interval": 30,
"timeout": 5,
"retries": 3,
"startPeriod": 60
}
}
]
}






Register the task definition:




aws ecs register-task-definition --cli-input-json file://task-definition.json









Step 4: Set Up Application Load Balancer






Create Application Load Balancer






# Create ALB
aws elbv2 create-load-balancer \
--name journal-api-alb \
--subnets subnet-xxxxxxxxx subnet-yyyyyyyyy \
--security-groups sg-xxxxxxxxx

# Create target group
aws elbv2 create-target-group \
--name journal-api-targets \
--protocol HTTP \
--port 8000 \
--vpc-id vpc-xxxxxxxxx \
--target-type ip \
--health-check-path /health \
--health-check-interval-seconds 30 \
--healthy-threshold-count 2 \
--unhealthy-threshold-count 3

# Create listener
aws elbv2 create-listener \
--load-balancer-arn arn:aws:elasticloadbalancing:... \
--protocol HTTP \
--port 80 \
--default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:...









Step 5: Create ECS Service with Auto Scaling






Create the Service






aws ecs create-service \
--cluster journal-api-cluster \
--service-name journal-api-service \
--task-definition journal-api-task:1 \
--desired-count 2 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxxxxxxxx,subnet-yyyyyyyyy],securityGroups=[sg-xxxxxxxxx],assignPublicIp=ENABLED}" \
--load-balancers targetGroupArn=arn:aws:elasticloadbalancing:...,containerName=journal-api,containerPort=8000









Set Up Auto Scaling






# Register scalable target
aws application-autoscaling register-scalable-target \
--service-namespace ecs \
--resource-id service/journal-api-cluster/journal-api-service \
--scalable-dimension ecs:service:DesiredCount \
--min-capacity 1 \
--max-capacity 10

# Create scaling policy
aws application-autoscaling put-scaling-policy \
--service-namespace ecs \
--resource-id service/journal-api-cluster/journal-api-service \
--scalable-dimension ecs:service:DesiredCount \
--policy-name journal-api-scaling-policy \
--policy-type TargetTrackingScaling \
--target-tracking-scaling-policy-configuration '{
"TargetValue": 70.0,
"PredefinedMetricSpecification": {
"PredefinedMetricType": "ECSServiceAverageCPUUtilization"
},
"ScaleOutCooldown": 300,
"ScaleInCooldown": 300
}'










Step 6: Database Migration and Testing






Run Database Migrations



You can run migrations as a one-time ECS task:




# Create migration task definition (similar to main task but with migration command)
# Then run it:
aws ecs run-task \
--cluster journal-api-cluster \
--task-definition journal-api-migration:1 \
--launch-type FARGATE \
--network-configuration "awsvpcConfiguration={subnets=[subnet-xxxxxxxxx],securityGroups=[sg-xxxxxxxxx],assignPublicIp=ENABLED}"









Test Your Deployment






# Get your load balancer DNS name
aws elbv2 describe-load-balancers --names journal-api-alb

# Test the API
curl http://your-alb-dns-name.us-east-1.elb.amazonaws.com/health
curl http://your-alb-dns-name.us-east-1.elb.amazonaws.com/docs









What I Learned: ECS vs EC2






The Good





  • Auto-scaling actually works: Traffic spikes don't crash your app


  • Zero-downtime deployments: Rolling updates happen automatically


  • Consistent environments: Same container runs everywhere


  • Better monitoring: CloudWatch integration is built-in


  • Less maintenance: No OS updates or server management






The Challenges





  • Learning curve: Understanding containers, task definitions, and ECS concepts


  • Debugging: Can't SSH into containers (but logs are better organized)


  • Cost: Slightly higher than EC2 for consistent workloads


  • Complexity: More moving parts than simple EC2 setup






Cost Comparison





  • EC2: ~$25-50/month (predictable)


  • ECS Fargate: ~$30-80/month (scales with usage)






Production Tips






1. Use Secrets Manager



Never put sensitive data in environment variables:




import boto3

def get_secret(secret_name):
client = boto3.client('secretsmanager')
response = client.get_secret_value(SecretId=secret_name)
return response['SecretString']









2. Set Up Proper Logging






import logging
import json

# Structured logging for CloudWatch
class JSONFormatter(logging.Formatter):
def format(self, record):
log_entry = {
'timestamp': self.formatTime(record),
'level': record.levelname,
'message': record.getMessage(),
'module': record.module
}
return json.dumps(log_entry)

# Configure logging
logging.basicConfig(level=logging.INFO)
handler = logging.StreamHandler()
handler.setFormatter(JSONFormatter())
logger = logging.getLogger(__name__)
logger.addHandler(handler)









3. Health Checks Matter



Make your health check meaningful:




@app.get("/health")
async def health_check():
try:
# Check database connection
await database.execute("SELECT 1")
return {"status": "healthy", "database": "connected"}
except Exception as e:
raise HTTPException(status_code=503, detail="Database connection failed")









Common Gotchas





  1. Memory limits: Set them appropriately or containers get killed


  2. Task definitions are immutable: You create new revisions, not edit existing ones


  3. Security groups: ALB and ECS tasks need different security group rules


  4. Logs: They go to CloudWatch, not local files






What's Next?



In Part 3, we'll explore the serverless approach with AWS Lambda. You'll see when it makes sense to go completely serverless and how to adapt your FastAPI application for Lambda.



We'll also compare all three approaches and help you decide which one fits your specific use case.






How has your experience been with containers? Are you using ECS, or have you tried other orchestration platforms? Share your thoughts in the comments!



Don't miss Part 3 - follow me to get notified when it's published!






Coming next: Part 3 - Going Serverless with Lambda



Previous: Part 1 - The EC2 Approach






fastapi #aws #docker #containers #ecs #devops #python #webdev #tutorial

1. Sofort-Triage & Abwehrmaßnahmen

SOC Incident Playbook: Remote Code Execution (RCE) Defense
Syntax validiert (0 Fehler)
title: Detect Exploitation - Deploying FastAPI to AWS: Part 2 - Containerizing with ECS Fargate
id: cebbed21-fff9-4bc0-ba59-2c6a5115ab8c
status: experimental
description: Automatisch generierte SIEM-Erkennungsregel basierend auf CTI Intelligence
references:
  - https://tsecurity.de/
author: iShareStuff CTI Automated Detection Engine
date: 2026-09-26
logsource:
  category: network_connection
  product: any
detection:
  selection:
      CommandLine|contains:
        - 'exploit'
  condition: selection
falsepositives:
  - Legitime administrative Zugriffe oder Penetrationstests
level: high
tags:
  - attack.initial_access
Syntax validiert (0 Fehler)
rule CTI_Threat_Indicator {
    meta:
        author = "iShareStuff CTI Automated Detection Engine"
        date = "2026-09-26"
        description = "YARA Signature for "
    strings:
        $str = "Deploying FastAPI to AWS: Part" ascii wide
    condition:
        any of them
}
Syntax validiert (0 Fehler)
index=security sourcetype IN ("cisco:asa", "pan:traffic", "zeek_conn", "suricata", "WinEventLog:Security")
("Deploying FastAPI to AWS Part 2 - Contai")
| stats count earliest(_time) as first_seen latest(_time) as last_seen by src_ip, dest_ip, dest_host, signature
| eval first_seen=strftime(first_seen, "%Y-%m-%d %H:%M:%S"), last_seen=strftime(last_seen, "%Y-%m-%d %H:%M:%S")
| sort - count
Syntax validiert (0 Fehler)
message: "*Deploying FastAPI to AWS Part 2 - Contai*"
Syntax validiert (0 Fehler)
CommonSecurityLog
| where Message has "Deploying FastAPI to AWS Part 2 - Contai"
| summarize EventCount = count(), FirstSeen = min(TimeGenerated), LastSeen = max(TimeGenerated) by SourceIP, DestinationIP, DestinationPort, Activity
| extend DetectionRule = "iShareStuff-CTI-Compiled"
| sort by EventCount desc

2. Cyber Threat Intelligence & Forensik

🎯
MITRE ATT&CK Matrix Navigator 14 Taktiken
Reconnaissance
-
Resource Development
-
Initial Access
Execution
Persistence
-
Privilege Escalation
Defense Evasion
Credential Access
-
Discovery
-
Lateral Movement
-
Collection
-
Command and Control
Exfiltration
-
Impact
tsecurity.de Cognitive Threat RAG
Fokus-Vektor:

Kognitive Analyse für identifizierte Bedrohung: Erhöhte Bedrohungslage im Bereich Deploying FastAPI to AWS: Part 2 - Conta.... Basierend auf 368k Vektor-Korrelationen werden sofortige Isolationsmaßnahmen für betroffene Endpunkte empfohlen.

🛡️ Angriffsfläche & Exposure

Netzwerk/Remote-Zugriff ohne Vorauthentifizierung möglich.

⚡ Empfohlene Sofortmaßnahmen
  • 1. Perimeter-Inspektion: Relevante Portfreigaben und exponierte Endpunkte unverzüglich scannen.
  • 2. Patch-Applikation: Hersteller-Hotfix einspielen oder betroffene Daemons in isolierte DMZ-Segmente überführen.
  • 3. Telemetrie & EDR-Alerts: Prozessaufrufe und Child-Processes auf anomale Shell-Spawns überwachen.
🔗 Semantisch verwandte Zero-Days MariaDB 11.7 VEC
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Deploying FastAPI to AWS: Part 2 - Containerizing with ECS Fargate

Thematisch verwandte Begriffe: Deploying, FastAPI, Part, Containerizing · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Zum Aktualisieren ziehen
ZERO-DAY CVE-2026-100537 | OpenClaw (npm package 'openclaw') before 2026.8.1 fails to apply the or…
Advisory →
tsecurity.de Icon
Offline-Lesen, Eilmeldungen & 0ms Ladezeit

Installiere tsecurity.de direkt auf deinen Home-Bildschirm für das ultimative Vollbild-Magazinerlebnis ohne Browser-Leisten.

Nächster Beitrag