Skip to main content
In this lesson we deploy a simple multi-container Node.js application (Express API + MongoDB) to AWS ECS Fargate. The demo shows:
  • container-to-container communication inside a single ECS task,
  • persistent storage using Amazon EFS,
  • exposing the API through an Application Load Balancer (ALB) with health checks.
Useful links:

Architecture summary

Local example: Docker Compose

Use Docker Compose to prototype the multi-container setup locally. The same concepts (env variables, ports, volumes) are applied later in the ECS task definition.
Note: In Docker Compose, service names can act as DNS names (e.g., mongo). In ECS Fargate (awsvpc networking), containers in the same task share the network namespace and communicate via localhost:<port>.

Application overview (Express + Mongoose)

This minimal Express API uses Mongoose and exposes CRUD endpoints for notes. The important part is how the MongoDB connection string is built from environment variables provided to the task.
Containers in the same ECS task (Fargate uses awsvpc network mode) share the task network namespace. That means your API container can reach MongoDB at localhost:<container-port>. This differs from Docker Compose where services can use each other’s service names as DNS.

ECS setup walkthrough

1) Create a security group for ECS tasks

Create a security group that will be applied to ECS tasks. During setup you may temporarily allow wide access; later you will tighten it to accept traffic only from the ALB security group.
A screenshot of the AWS EC2 console showing the Security Groups page with a table of security group IDs, names, VPC IDs, descriptions, and owner IDs. The left sidebar shows EC2 navigation items like Instances, Images, and Elastic Block Store.
Create the security group (temporary open access for initial testing).
A screenshot of the AWS EC2 "Create security group" page with fields for security group name, description and VPC selection. The inbound and outbound rules sections are visible, showing an "All traffic" rule with source/destination 0.0.0.0/0.

2) Create the ECS task definition (FARGATE)

  • Create a FARGATE task definition and attach an execution role (e.g., ecsTaskExecutionRole).
  • Choose CPU / memory appropriate for the demo.
  • Add two containers in the same task: MongoDB and the Express API.
A screenshot of the AWS console "Create new Task Definition" page for ECS, showing configuration fields like Task definition name set to "ecs-project1" and Requires compatibilities = FARGATE. The Task role dropdown is open with options including "ecsTaskExecutionRole."
Container configuration overview When you add environment variables and health checks in the task definition, you can configure an HTTP health check for the API (e.g., /notes) or a simple command to ensure each container is healthy.

3) Add EFS for MongoDB persistence

Add a task volume backed by Amazon EFS and mount it into the MongoDB container at /data/db. Create the EFS file system in the same VPC and configure mount targets for the subnets you will use.
A screenshot of the AWS console showing an "Add volume" dialog configuring a volume named "mongo-db" with Volume type set to EFS and fields for file system ID, access point ID, root directory, and encryption options. The AWS service/sidebar configuration pages are visible dimly in the background.
Choose the correct VPC/subnets and replace the default security group. Create an EFS security group that allows NFS (port 2049) inbound only from the ECS tasks security group.
A screenshot of the AWS Management Console showing the Amazon EFS "Network access" setup page with a VPC selector and mount target configuration fields (availability zone, subnet ID, IP address, security groups). Two mount targets for us-east-1a and us-east-1b are shown.
Create an EFS security group and allow NFS inbound only from the ECS SG.
A screenshot of the AWS Management Console showing the Amazon EFS "Network access" setup page, with fields for Virtual Private Cloud, mount targets, availability zones, subnet IDs, IP address and security group selection. The UI displays two mount target rows and a dropdown list of security groups.
After creating the EFS, add it as a volume to the task definition and set the MongoDB container mount point to /data/db.
A screenshot of the AWS Management Console showing an "Add volume" dialog for configuring an Amazon EFS volume (named "mongo-db") with fields for file system ID, access point, root directory, and encryption options. The blurred background appears to be an ECS task/service configuration page.
Mount point configuration (inside the container)
A screenshot of the AWS console (ECS) showing an "Edit container" dialog with the Storage and Logging section open. It shows a mount point using source volume "mongo-db" mapped to the container path "/data/db" and CloudWatch log configuration options.
Finalize and register the task definition (example name used: ecs-project2).
Screenshot of the AWS Management Console showing an Amazon ECS Task Definition for "ecs-project2" (revision 4). The page displays task settings like task role ecsTaskExecutionRole, network mode awsvpc, operating system Linux, and EC2/FARGATE compatibility.

4) Create an ECS service and an Application Load Balancer (ALB)

  • Create a Fargate service from the task definition with the desired number of tasks (1 for demo).
  • Choose the ECS security group and appropriate VPC/subnets.
  • Create an internet-facing ALB that listens on port 80 and forwards to a target group configured for the API container.
Service creation form:
A screenshot of the AWS Management Console showing an Amazon ECS service configuration form. Visible fields include Operating system family (Linux), Task Definition "ecs-project2" (revision 4), Cluster "cluster1", Service name "notes-app", and deployment/scaling settings.
ALB / Target group guidance Create the target group for the ALB (example name notes-tg1), protocol HTTP, target type = IP, and set the health check path to /notes.
A screenshot of the AWS console showing the "Create target group" form for a load balancer, with the target group named "notes-tg1", protocol set to HTTP on port 80 and IPv4 selected. The page also displays VPC selection, protocol version options (HTTP1/HTTP2/gRPC) and a Health checks section below.
After creation you should see the target group listed:
Screenshot of the AWS EC2 console on the Target Groups page showing a successfully created target group named "notes-tg1." The list shows its ARN, port 80, protocol HTTP, target type IP, and that no load balancer is associated.
Attach the target group to the ALB and, in the ECS service networking settings, select the API container as the target for load-balanced traffic. The ALB will forward HTTP requests on port 80 to container port 3000 via the notes-tg1 target group. Load balancer configuration for the API container:
A screenshot of the AWS console showing load balancer settings for an ECS container: "web-api : 3000" with a production listener on port 80, target group "notes-tg1" and a health check path "/notes."
Create the ECS service. The service will launch tasks and register their ENI IPs in the target group. Monitor task startup and ALB health checks.

Verify tasks and containers are running

When the Fargate task is running you can inspect it to confirm both containers are running (mongo and notes-api).
A screenshot of the AWS Elastic Container Service (ECS) console showing the details page for a running Fargate task. It displays task and network information plus two running containers named "mongo" and "notes-api."

Test the API

  • Directly (diagnostics): use the task ENI IP and container port:
    • GET http://<task-ip>:3000/notes
    • Response:
  • Through the ALB (recommended):
    • POST http://<alb-dns>/notes with JSON:
    • Then GET http://<alb-dns>/notes returns:
Target group showing registered task IPs and health:
A screenshot of the AWS EC2 console showing the target group "notes-tg1" with details (HTTP:80, VPC, protocol version) and summary counts. Below it lists two registered IP targets with ports and health statuses — one healthy and one draining.
Load balancer listing and DNS name (use this to access your API):
A screenshot of the AWS EC2 console showing the Load Balancers page with a single load balancer named "notes-lb" (Active) and its basic configuration details (DNS name, VPC, availability zones). The left sidebar displays EC2 navigation options like Instances, Images, and Network & Security.

Tighten security: restrict ECS task SG to only accept traffic from the ALB

Once the ALB is functioning, update the ECS tasks security group to only allow inbound traffic on port 3000 from the ALB security group:
  1. Remove the broad inbound rule (e.g., 0.0.0.0/0).
  2. Add a Custom TCP rule for port 3000 with the ALB security group as the source.
Confirmation of the modified ECS security group:
A screenshot of the AWS VPC console showing details for a security group (ecs-sg, sg-0ffea13c587867f6f) with one inbound Custom TCP rule allowing port 3000. A green banner at the top confirms the inbound security group rules were successfully modified.
Do not use weak static credentials (for example mongo / password) in production. Use secrets management like AWS Secrets Manager or SSM Parameter Store. Always restrict security group rules to the minimal required sources and ports.

Wrap-up & key takeaways

  • Deploying an Express + MongoDB stack to ECS Fargate:
    • Place both containers in the same Fargate task so they can communicate over localhost:<port>.
    • Persist MongoDB data using Amazon EFS mounted as a task volume.
    • Use an internet-facing ALB (listener on port 80) and a target group (target type IP) to route requests to container port 3000.
    • Configure ALB health checks (e.g., /notes) and restrict the ECS task security group to accept traffic only from the ALB.
  • For production:
    • Use secure credentials via Secrets Manager or SSM.
    • Harden security groups and monitoring (CloudWatch logs, alarms).

References

Watch Video