Deploy a Containerized App on ECS Fargate

Deploy a containerized app on ECS Fargate in this AWS Tutorial lesson. Hands-on steps, troubleshooting, and next steps.

Focus: deploy a containerized app on ecs fargate

Sponsored

You've built a containerized app, tested it locally, and pushed it to a registry — but now comes the moment of truth: getting it running in the cloud without babysitting servers. If you've wrestled with EC2 instances, patching AMIs, or managing auto-scaling groups, you know the pain: infrastructure that demands constant attention. This lesson shows you how to deploy a containerized app on ECS Fargate — a fully managed container orchestration service that lets you run containers without provisioning or managing servers. By the end, you'll have a live, scalable app in the cloud and a clear mental model for when Fargate beats the alternatives.

The problem this lesson solves

Running containers in production traditionally means managing a cluster of EC2 instances. You are responsible for: - Patching the OS and Docker daemon on every node. - Scaling the cluster when traffic spikes — adding instances takes minutes. - Paying for idle capacity even when your app uses 5% of the CPU. - Watching for node failures and draining tasks manually.

That's a lot of undifferentiated heavy lifting — precisely the work AWS was designed to eliminate. ECS Fargate removes the server layer entirely. You define your container, its CPU and memory, and Fargate runs it for you. No EC2 instances, no cluster management, no manual scaling. It's the difference between renting a car and taking a taxi: with Fargate, you tell the driver where to go; the taxi company handles the vehicle, fuel, and maintenance.

This lesson gives you a repeatable, infrastructure-as-code path to deploy any containerized app on Fargate — whether it's a Python API, a Node service, or a batch job.

Core concept / mental model

Think of ECS Fargate as a three-layer cake:

  1. Task definition — the recipe. It declares which container image to use, how much CPU and memory to allocate, environment variables, ports, and log configuration.
  2. Service — the long-running orchestrator. It keeps a desired number of task copies running, handles rolling deployments, and integrates with a load balancer.
  3. Cluster — the logical grouping. Even though Fargate has no servers, the cluster is the container for your services and tasks.

Here's the flow: you push your image to Amazon ECR (or any registry) → define a task definition → create a service in a cluster → Fargate provisions the infrastructure and runs your container → traffic flows through an optional load balancer → your app is live.

Key terms to internalize: - Task — a running instance of a task definition. - Service — maintains the desired count of tasks; auto-heals failures. - Elastic Load Balancer (ALB) — distributes incoming HTTP traffic across tasks and does health checks. - Security group — virtual firewall for your tasks.

This serverless model means you pay only for the vCPU and memory your tasks consume while running — no idle node costs.

How it works step by step

Deploying to Fargate follows a predictable sequence. Here's the logical cause-and-effect chain:

1. Containerize your app

Your app must be a Docker image. If you haven't already, create a Dockerfile and build locally:

FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

2. Push the image to Amazon ECR

Fargate needs a registry to pull from. Amazon ECR is a private Docker registry integrated with IAM. You'll authenticate, tag, and push:

aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.us-east-1.amazonaws.com
docker tag my-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest

3. Define the task definition

This JSON tells Fargate exactly what to run. You specify the image, resources, and networking.

4. Create the service and cluster

The service pulls the image and starts the task on Fargate. If a task crashes, the service restarts it.

5. Expose the app

Attach an Application Load Balancer (ALB) and configure a security group to allow inbound traffic on port 80/443.

Each step depends on the previous — if the image isn't in ECR, the task can't start. If the security group blocks the port, the health check fails and the task is marked unhealthy.

Hands-on walkthrough

Let's deploy a simple Python FastAPI app step by step. You'll need the AWS CLI and Docker installed. We'll use a single command with the ecs CLI to keep it simple, but the concepts map to the console too.

Step 1: Write a minimal app

Create app.py in a new directory:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello from ECS Fargate!"}

Step 2: Build and push the image

Create the Dockerfile above, then build and push (replace the account ID and region):

docker build -t my-app:latest .
aws ecr create-repository --repository-name my-app --region us-east-1
docker tag my-app:latest 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest
docker push 123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest

Step 3: Register a task definition

Save the following as task-def.json:

{
  "family": "my-app-task",
  "networkMode": "awsvpc",
  "containerDefinitions": [
    {
      "name": "my-app-container",
      "image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest",
      "portMappings": [
        {
          "containerPort": 8000,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "my-app-logs",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "my-app"
        }
      }
    }
  ],
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512"
}

Register it:

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

Step 4: Run the service

Create a cluster and service (use existing security groups and subnets from your VPC):

aws ecs create-cluster --cluster-name my-cluster
aws ecs create-service \
  --cluster my-cluster \
  --service-name my-app-service \
  --task-definition my-app-task \
  --desired-count 1 \
  --launch-type FARGATE \
  --network-configuration "awsvpcConfiguration={subnets=[subnet-abc123],securityGroups=[sg-xyz789]}"

Step 5: Verify it's running

Check the status:

aws ecs list-tasks --cluster my-cluster

You'll see a task ARN. Once the task is RUNNING, find its public IP (if your subnets are public) and curl it:

curl http://<task-ip>:8000/
# Output: {"message":"Hello from ECS Fargate!"}

🎉 You've just deployed a containerized app on ECS Fargate.

Compare options / when to choose what

Fargate isn't the only way to run containers on AWS. Here's how it stacks up against the alternatives:

Option Management Scaling Cost Best for
ECS Fargate Fully managed — no servers Fast (seconds) Pay per task vCPU/memory Most container apps, microservices, batch jobs
ECS on EC2 You manage the instances Slower — need to add instances Pay for instances even when idle Steady, high-utilization workloads, GPU needs
EKS (Kubernetes) More control, more complexity Similar to ECS but with K8s scaling Cluster control plane cost + nodes Heavy K8s adoption, portability requirements
Lambda Serverless but limits runtime to 15 min Instant Pay per invocation Event-driven, short-lived jobs

When to choose Fargate: - You want zero infrastructure management. - Your app runs longer than Lambda's max duration (15 minutes) or needs persistent connections. - You need a consistent, repeatable deployment without K8s overhead.

When to avoid Fargate: - You need GPU instances (Fargate doesn't support GPUs). - You have a massive, steady workload where EC2's lower per-hour cost beats Fargate's per-task pricing. - You're heavily invested in Kubernetes and need its ecosystem.

Troubleshooting & edge cases

Deployments fail — here are the common culprits and how to fix them.

CannotPullContainerError: Access denied

  • Cause: The task's IAM role (ecsTaskExecutionRole) lacks ecr:GetDownloadUrlForLayer and ecr:BatchGetImage permissions.
  • Fix: Attach the managed policy AmazonEC2ContainerRegistryReadOnly to your task execution role.

Task stays PENDING forever

  • Cause: The subnet has no route to the internet or to ECR (if using a private subnet, you need a NAT gateway or VPC endpoints).
  • Fix: Use public subnets or add a NAT gateway. Verify the security group allows outbound HTTPS (port 443).

Health check fails — task restarts in a loop

  • Cause: The container's health check endpoint isn't what the ALB expects, or the security group blocks traffic.
  • Fix: Ensure your app has a / or /health endpoint; configure the ALB's health check path accordingly. Open the container port on the security group.

Can't reach the app from the internet

  • Cause: You exposed only the container port, not a load balancer or public IP assignment.
  • Fix: For a single task, assign a public IP (assignPublicIp: ENABLED). For production, attach an ALB and open the ALB's security group to the world.

Task exits immediately with ResourceInitializationError

  • Cause: The log group doesn't exist, or the task execution role lacks logs:CreateLogStream and logs:PutLogEvents.
  • Fix: Create the CloudWatch log group and attach the policy AWSOpsWorksCloudWatchLogs or custom IAM permissions.

Pro tip: Always watch the service events (aws ecs describe-services) — AWS often includes the exact root cause there.

What you learned & what's next

You now understand how to deploy a containerized app on ECS Fargate: from a Docker image to a live URL, with a task definition, service, and load balancer. You can debug common failures and choose Fargate wisely against EC2, EKS, or Lambda. You've met the learning objectives: you can explain the core idea — serverless containers with per-task pricing — and you've completed a practical deployment.

Next in the AWS Tutorial, you'll learn how to set up a CI/CD pipeline for ECS — automating your builds and deployments so every git push ships to Fargate without manual steps. You'll use CodePipeline and CodeBuild to take your new Fargate skill into the world of continuous delivery.

Practice recap

Now try it on your own: deploy the same FastAPI app but add an Application Load Balancer and set the desired count to 2. Then test auto-healing by killing one task (aws ecs stop-task) and watch the service replace it. This exercise cements the rolling-deployment and self-repair behavior you'll rely on in production.

Common mistakes

  • Forgetting to attach the ecsTaskExecutionRole with ECR and logs permissions — tasks fail with CannotPullContainerError.
  • Using a private subnet without a NAT gateway — tasks hang in PENDING forever because they can't reach ECR or the internet.
  • Not opening the security group's inbound port for the container — the ALB health check returns 503 and the task keeps swapping.
  • Setting the task's CPU/memory too low — your app OOMs and restarts in a loop; always check CloudWatch logs for Killed.

Variations

  1. Use AWS Copilot — a higher-level CLI that abstracts ECS/Fargate and generates task definitions, services, and pipelines for you (perfect for quick tests).
  2. Use Terraform or AWS CDK to define your ECS infrastructure as code — the same task definition JSON becomes part of a reproducible IaC template.
  3. Run a scheduled task (like a cron job) instead of a service — Fargate supports one-off tasks triggered by CloudWatch Events for batch processing.

Real-world use cases

  • Deploy a FastAPI REST API for a mobile backend, scaling with ALB traffic spikes automatically.
  • Run a nightly ETL batch job using a one-off Fargate task triggered by CloudWatch Events — no server to keep alive.
  • Host a multi-container web app (Flask + Redis) with sidecar containers for logging or metrics on Fargate.

Key takeaways

  • ECS Fargate runs containers without servers — you pay only for the vCPU and memory your tasks use.
  • A task definition is the recipe; a service keeps the desired number of tasks running and handles rollouts.
  • The awsvpc network mode gives each task a private IP and requires proper subnets and security groups.
  • Failures are almost always IAM permissions, network routing, or health check misconfiguration — check service events first.
  • Fargate is the sweet spot for most containerized workloads, but not for GPU or steady high-load scenarios — compare against EC2 and EKS.
  • You can automate the deployment with AWS Copilot, Terraform, or CDK for production-ready infrastructure.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.