DEV Community

Anupa Supul
Anupa Supul

Posted on

I Stopped Managing Servers Manually — Understanding Docker, Amazon ECR, ECS & Fargate

I Stopped Managing Servers Manually — Understanding Docker, Amazon ECR, ECS & Fargate

I had already learned how a web application can be built on AWS using EC2, Load Balancers, Auto Scaling, RDS, S3, SQS, and other services.

But there was still one question I kept coming back to:

What if I don't want to manage the server itself?

With EC2, I am still responsible for things like the operating system, installed software, runtime environment, and server configuration.

But modern applications are increasingly built around containers.

That led me to the next part of my AWS learning journey:

Docker → Amazon ECR → Amazon ECS → AWS Fargate

And once I understood how these four pieces connect, container deployment on AWS started making much more sense.


First, What Problem Are Containers Solving?

Imagine I build a Node.js application on my laptop.

It works perfectly.

Node.js version: 22
Express: installed
Dependencies: installed
Environment: configured
Application: running
Enter fullscreen mode Exit fullscreen mode

Then I give the application to another developer.

Suddenly:

“It works on my machine.”

Maybe they have a different Node.js version.

Maybe a dependency is missing.

Maybe an environment variable isn't configured.

Maybe the operating system behaves differently.

This is one of the problems containers help solve.

Instead of only shipping our application code, we can package the application together with the environment it needs to run.

That's where Docker comes in.


1. Docker — Package the Application

Docker allows us to package an application into a container image.

For a Node.js backend, the image can contain:

Node.js
Application code
Dependencies
Configuration needed to run
Enter fullscreen mode Exit fullscreen mode

A simplified flow looks like this:

Application Code
       │
       ▼
   Dockerfile
       │
       ▼
 Docker Image
       │
       ▼
   Container
Enter fullscreen mode Exit fullscreen mode

The important distinction is:

Image = packaged blueprint

Container = running instance of that image

For example:

Docker Image
     │
     ├── Container 1
     ├── Container 2
     └── Container 3
Enter fullscreen mode Exit fullscreen mode

This makes it much easier to run the same application consistently across different environments.


2. Dockerfile — The Instructions for the Image

A Docker image is usually created from a Dockerfile.

A simplified Node.js example could look like:

FROM node:22

WORKDIR /app

COPY package*.json ./
RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "start"]
Enter fullscreen mode Exit fullscreen mode

I don't need to manually install Node.js on every server.

The Docker image already defines what the application needs.

So instead of saying:

“Please configure a server exactly like my development machine.”

I can say:

“Run this container.”

That is a much more repeatable deployment model.


3. But Where Does the Docker Image Go?

Now we have a Docker image.

But AWS needs somewhere to store it.

This is where Amazon Elastic Container Registry (ECR) comes in.

ECR is AWS's managed container image registry.

The flow becomes:

Developer
    │
    ▼
Dockerfile
    │
    ▼
Docker Build
    │
    ▼
Docker Image
    │
    ▼
Amazon ECR
Enter fullscreen mode Exit fullscreen mode

Think of ECR as a private warehouse for container images.

For example, we might have:

my-backend
my-worker
my-payment-service
Enter fullscreen mode Exit fullscreen mode

inside an ECR repository.

When we build a new version of the application, we can push a new image to ECR.


4. ECR Doesn't Run Our Application

This distinction is important.

ECR stores the image.

It doesn't run the application.

For example:

ECR
 │
 │ stores
 ▼
Docker Image
Enter fullscreen mode Exit fullscreen mode

We still need a service that can take that image and actually run it.

That's where Amazon ECS comes in.


5. Amazon ECS — Running Containers on AWS

Amazon Elastic Container Service, or ECS, is a container orchestration service.

In simple terms:

ECS manages running containers for our application.

The architecture now starts looking like:

Code
  │
  ▼
Docker Image
  │
  ▼
ECR
  │
  ▼
ECS
  │
  ▼
Running Container
Enter fullscreen mode Exit fullscreen mode

But ECS introduces a few concepts that are worth understanding.


6. ECS Cluster

An ECS Cluster is a logical grouping of resources used to run our ECS workloads.

Think of it as the environment where our services run.

For example:

Production Cluster
       │
       ├── Backend Service
       ├── Worker Service
       └── Payment Service
Enter fullscreen mode Exit fullscreen mode

A cluster itself isn't simply “one server.”

It's more useful to think of it as a logical home for our container workloads.


7. ECS Task Definition

Now ECS needs to know how our container should run.

That's where a Task Definition comes in.

It can describe things such as:

Which Docker image?
How much CPU?
How much memory?
Which port?
Environment variables?
IAM permissions?
Logging configuration?
Enter fullscreen mode Exit fullscreen mode

For example:

Task Definition
       │
       ├── Image → ECR
       ├── CPU
       ├── Memory
       ├── Port
       └── Environment
Enter fullscreen mode Exit fullscreen mode

This is similar to giving ECS a set of instructions:

“This is the container I want you to run, and this is how it should run.”


8. ECS Service — Keeping Containers Running

A task definition tells ECS how to run a container.

But what if I want three copies of my backend running continuously?

That's where an ECS Service comes in.

For example:

ECS Service
     │
     ├── Task 1
     ├── Task 2
     └── Task 3
Enter fullscreen mode Exit fullscreen mode

The service maintains the desired number of tasks.

If a running task fails, ECS can replace it.

This is one of the important differences between simply running a container and having a container orchestration system manage it.


9. Where Does the Container Actually Run?

This is where AWS Fargate becomes interesting.

ECS can run containers using different compute options.

One option is managing the underlying EC2 instances yourself.

Another option is AWS Fargate.

Fargate is a serverless compute engine for containers.

With Fargate, AWS manages the underlying infrastructure required to run the containers.

So instead of thinking:

ECS
 ↓
EC2
 ↓
Operating System
 ↓
Container
Enter fullscreen mode Exit fullscreen mode

we can use:

ECS
 ↓
Fargate
 ↓
Container
Enter fullscreen mode Exit fullscreen mode

We still define the container's CPU, memory, networking, and other configuration, but we don't have to manage the underlying EC2 servers ourselves.


10. ECS + Fargate Architecture

Now our application starts looking like this:

                         USERS
                           │
                           ▼
                       Route 53
                           │
                           ▼
                      CloudFront
                           │
                           ▼
                          ALB
                           │
                           ▼
                     ECS Service
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
          Fargate       Fargate      Fargate
           Task          Task          Task
              │            │            │
              └────────────┼────────────┘
                           │
                           ▼
                           RDS
Enter fullscreen mode Exit fullscreen mode

The ALB distributes requests between the running ECS tasks.

If one task becomes unhealthy, the service can replace it.

If traffic increases, we can increase the desired number of tasks.


11. What Happens When We Deploy a New Version?

This is where the whole model becomes really powerful.

Suppose we update our Node.js application.

We make some changes:

Version 1
    ↓
Code changes
    ↓
Version 2
Enter fullscreen mode Exit fullscreen mode

We then build a new Docker image.

Source Code
     │
     ▼
Docker Build
     │
     ▼
New Docker Image
     │
     ▼
Amazon ECR
Enter fullscreen mode Exit fullscreen mode

ECS can then deploy the new version.

The simplified flow becomes:

Developer
    │
    ▼
GitHub
    │
    ▼
Build Docker Image
    │
    ▼
Amazon ECR
    │
    ▼
ECS Service
    │
    ▼
New Tasks
    │
    ▼
Production
Enter fullscreen mode Exit fullscreen mode

This is the foundation for modern container-based CI/CD pipelines.


12. What About the Database?

Containers are designed to be replaceable.

That means we generally shouldn't treat the container itself as the permanent place for important application data.

Instead:

ECS / Fargate
      │
      ├── Application
      │
      ▼
     RDS
      │
      ▼
Persistent Data
Enter fullscreen mode Exit fullscreen mode

If a container disappears and ECS starts a new one, the application data remains in RDS.

The same principle applies to files.

Instead of storing uploaded files inside the container:

Container ❌
Enter fullscreen mode Exit fullscreen mode

we can use:

S3 ✅
Enter fullscreen mode Exit fullscreen mode

So:

Application
    │
    ├── Database → RDS
    ├── Files → S3
    ├── Cache → ElastiCache
    └── Jobs → SQS
Enter fullscreen mode Exit fullscreen mode

This separation is extremely important when designing scalable applications.


13. Security Still Matters

Moving from EC2 to containers doesn't mean security disappears.

We still need to think about:

IAM

ECS tasks can use IAM roles to access AWS services without hardcoding AWS credentials into the application.

Security Groups

We can control network communication between:

ALB
 ↓
ECS
 ↓
RDS
Enter fullscreen mode Exit fullscreen mode

Secrets Manager

Sensitive values such as database credentials and API secrets can be stored securely instead of being hardcoded into the image.

VPC

Our ECS tasks can run inside private subnets while the ALB remains publicly reachable.

So the architecture can still follow a secure pattern:

Internet
    │
    ▼
   ALB
    │
    ▼
Private ECS Tasks
    │
    ├── RDS
    ├── S3
    └── Other AWS Services
Enter fullscreen mode Exit fullscreen mode

14. What Happens When Traffic Increases?

Let's say our application normally runs three tasks:

ECS Service

Task 1
Task 2
Task 3
Enter fullscreen mode Exit fullscreen mode

Suddenly, traffic increases.

We can configure ECS Service Auto Scaling to increase the number of running tasks.

Normal Traffic

Task 1
Task 2
Task 3
Enter fullscreen mode Exit fullscreen mode

Then:

High Traffic

Task 1
Task 2
Task 3
Task 4
Task 5
Task 6
Enter fullscreen mode Exit fullscreen mode

The ALB distributes traffic across the available healthy tasks.

This gives us a scalable container architecture without manually logging into servers and starting applications ourselves.


15. ECS vs EC2 — What's the Difference?

This was one of the comparisons that helped me understand the purpose of ECS.

EC2 ECS + Fargate
Manage virtual servers Manage containers
OS management required AWS manages underlying infrastructure with Fargate
Install runtime manually Runtime packaged in container
Server-focused Container-focused
More infrastructure control Less infrastructure management
Scaling servers Scaling container tasks

Neither is automatically “better.”

The right choice depends on the application's requirements, team experience, control requirements, and operational needs.


16. ECS vs Lambda

Another useful comparison is ECS vs Lambda.

Lambda is excellent for event-driven, short-lived workloads where we don't want to manage servers or containers directly.

ECS is useful when we want more control over a containerized application and its runtime.

A simplified comparison:

ECS Lambda
Container-based Function-based
Long-running services are common Event-driven execution is common
More runtime control Less infrastructure management
Good for containerized APIs/workers Good for many event-driven workloads

Again, neither service replaces the other.

The architecture should determine the choice.


17. The Complete Flow

Now we can connect everything together.

                         USER
                           │
                           ▼
                       Route 53
                           │
                           ▼
                      CloudFront
                           │
                           ▼
                          ALB
                           │
                           ▼
                    ECS Service
                           │
              ┌────────────┼────────────┐
              ▼            ▼            ▼
          Fargate       Fargate      Fargate
           Task          Task          Task
              │            │            │
              └────────────┼────────────┘
                           │
             ┌─────────────┼─────────────┐
             ▼             ▼             ▼
            RDS           S3        ElastiCache
         Database      File Storage      Cache
                           │
                           │
                           ▼
                          SQS
                           │
                           ▼
                     Worker Tasks
Enter fullscreen mode Exit fullscreen mode

And the deployment side looks like:

Developer
    │
    ▼
GitHub
    │
    ▼
CI/CD Pipeline
    │
    ▼
Docker Build
    │
    ▼
Amazon ECR
    │
    ▼
Amazon ECS
    │
    ▼
Fargate
    │
    ▼
Production
Enter fullscreen mode Exit fullscreen mode

Now we have two important flows:

Application flow

User → ALB → ECS → AWS Services
Enter fullscreen mode Exit fullscreen mode

Deployment flow

Code → Docker → ECR → ECS → Fargate
Enter fullscreen mode Exit fullscreen mode

That distinction made the whole architecture much easier for me to understand.


The Mental Model I Keep Now

I don't think of ECS, ECR, and Fargate as three unrelated AWS services anymore.

I think of them as a chain:

Docker
   │
   │ Packages
   ▼
ECR
   │
   │ Stores
   ▼
ECS
   │
   │ Manages
   ▼
Fargate
   │
   │ Runs
   ▼
Container
Enter fullscreen mode Exit fullscreen mode

In simple words:

Docker → Package my application

ECR → Store my image

ECS → Manage my containers

Fargate → Run those containers without managing the underlying servers

That mental model is much easier to remember than four separate definitions.


What Changed My Perspective?

Before learning containers, my mental model was mostly:

Code
 ↓
EC2
 ↓
Run application
Enter fullscreen mode Exit fullscreen mode

Now it looks more like:

Code
 ↓
Docker
 ↓
Container Image
 ↓
ECR
 ↓
ECS
 ↓
Fargate
 ↓
Production
Enter fullscreen mode Exit fullscreen mode

And when we connect that with the architecture from my previous AWS article:

Users
  ↓
Route 53
  ↓
CloudFront
  ↓
ALB
  ↓
ECS / Fargate
  ↓
RDS / S3 / SQS / ElastiCache
Enter fullscreen mode Exit fullscreen mode

AWS starts looking less like a collection of services and more like a complete platform for running applications.


Quick Cheat Sheet

Technology / Service Main Job
Docker Packages applications into containers
Dockerfile Defines how an image is built
Docker Image Packaged application blueprint
Container Running instance of an image
ECR Stores container images
ECS Orchestrates containers
Task Definition Defines how containers should run
ECS Service Maintains desired running tasks
Fargate Runs containers without managing servers
ALB Distributes traffic
RDS Stores relational data
S3 Stores files and objects
SQS Handles asynchronous jobs
ElastiCache Caches frequently accessed data
IAM Controls permissions
VPC Provides network isolation

Final Thoughts

The biggest shift in my AWS learning wasn't memorizing more services.

It was learning to ask:

“What problem is this service solving, and where does it fit in the architecture?”

Once I started thinking that way, AWS stopped feeling like a huge list of unrelated services.

Instead, it started looking like a set of building blocks.

And when those building blocks are combined properly, we can build applications that are scalable, secure, highly available, and easier to operate.

That, for me, is where learning AWS started becoming much more interesting.


Top comments (0)