// Cloud & AWS

Deploy Node.js + MongoDB to AWS ECS Fargate (IAM, ECR, Task Definitions)

Ghost Dev Explains
Updated July 21, 2026
5 min readintermediate
// quick answer

Package an Express and MongoDB app with Docker, push the image to Amazon ECR, and run it on ECS Fargate using a task definition, an execution role, and a service with a public IP. No EC2 instances to manage.

// video

This is the full deployment from the video: an Express app that accepts form submissions, a MongoDB database, Docker for packaging, Amazon ECR for the image, and ECS Fargate for compute. The goal is not just to make it run once. The goal is to understand why each piece exists so you can debug it when something breaks.

There is no EC2 instance in this setup. Fargate runs the container for you, which removes server maintenance but adds a few concepts you need to get right: the execution role, the task definition, and the network configuration.

What you are building

  • An Express app running on port 3000
  • A MongoDB database the app can reach
  • A Docker image stored in Amazon ECR
  • An ECS cluster with a Fargate task and service
  • A public IP so you can open the app from a browser

Prerequisites

Install these before you start:

  • Docker Desktop
  • Node.js 20 or newer
  • AWS CLI v2
  • An AWS account with credits or a payment method

The command reference from the video is available in the Ghost Dev deployment doc. Use it alongside this post so you can copy the exact commands.

Step 1: Create a scoped IAM user

Do not use the root account for the CLI. Create an IAM user with programmatic access:

  1. Open IAM in the AWS console.
  2. Create a user such as ghostdev-cli.
  3. Attach the permissions needed for this walkthrough. For learning, ECR and ECS access is enough. Scope it down before you use this in a real account.
  4. Create an access key and download the CSV once.

Configure the CLI:

aws configure

Enter the access key, secret key, default region, and json as the output format. Verify it:

aws sts get-caller-identity

If this fails, fix the credentials before you touch ECS.

Step 2: Dockerize the app

A small production-style Dockerfile is enough:

FROM node:20-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --omit=dev

COPY . .

EXPOSE 3000
CMD ["node", "server.js"]

Build and run it locally first:

docker build -t ghostdev-node-app .
docker run --env-file .env -p 3000:3000 ghostdev-node-app

If the app does not work locally, it will not work on Fargate. Test the form submission and the database write before you push the image.

Step 3: Push the image to Amazon ECR

Create a repository:

aws ecr create-repository --repository-name ghostdev-node-app

Log in Docker to ECR:

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

Build, tag, and push:

docker build -t ghostdev-node-app .
docker tag ghostdev-node-app:latest YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/ghostdev-node-app:latest
docker push YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/ghostdev-node-app:latest

Copy the image URI from the ECR console. You will paste it into the task definition.

Step 4: Create the task execution role

ECS needs a role to pull the private image and send logs to CloudWatch. Create a role for ECS tasks and attach the AmazonECSTaskExecutionRolePolicy managed policy.

If you skip this step, the service starts and then fails with an image pull or log error. The task execution role is not the same as the task role. The execution role is used by the ECS agent, not by your application code.

Step 5: Create the cluster

Create an ECS cluster with the Fargate infrastructure. A cluster is just a logical grouping. It does not cost anything until you run tasks or services.

Step 6: Write the task definition

The task definition is the blueprint for the container. A minimal Fargate definition looks like this:

{
  "family": "ghostdev-node-app",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "executionRoleArn": "arn:aws:iam::YOUR_ACCOUNT_ID:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "name": "app",
      "image": "YOUR_ACCOUNT_ID.dkr.ecr.us-east-1.amazonaws.com/ghostdev-node-app:latest",
      "essential": true,
      "portMappings": [{ "containerPort": 3000, "protocol": "tcp" }],
      "environment": [
        { "name": "PORT", "value": "3000" },
        { "name": "MONGODB_URI", "value": "YOUR_CONNECTION_STRING" }
      ],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/ghostdev-node-app",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ]
}

Create the CloudWatch log group first, or remove the log configuration for your first test. The CPU and memory values must be a valid Fargate combination.

Step 7: Create the service and networking

Create a service that runs one task:

  1. Choose the cluster and task definition.
  2. Set the desired count to 1.
  3. Choose a VPC and a public subnet.
  4. Create or select a security group that allows inbound traffic on port 3000.
  5. Enable a public IP for the test.
  6. Create the service.

Fargate tasks in a public subnet need a route to the internet gateway if they must pull images or call external APIs. If the service keeps restarting, open the stopped task and read the stopped reason.

Step 8: Open the app

Wait for the task to reach RUNNING, then copy the public IP from the task details. Open:

http://PUBLIC_IP:3000

If the app loads but the database write fails, you have a MongoDB problem, not an ECS problem. Check the CloudWatch logs.

The MongoDB bug to watch for

The video debugs a real MongoDB authentication failure on camera. The usual suspects are:

  • Using localhost inside a multi-container task. Each container has its own network namespace. Use the other container's name or a managed database host.
  • A wrong or unescaped password. Special characters in the password must be URL encoded in the connection string.
  • A missing auth source. Many setups need authSource=admin in the URI.
  • A security group that blocks the database port. The app container cannot reach the database if the network path is closed.

For a quick test, MongoDB Atlas is easier than running the database inside the same task. It removes the networking problem and gives you a stable connection string.

Clean up

Delete the ECS service, then the cluster, then the ECR repository if you do not need the image. Fargate charges while tasks are running. Cleaning up is part of the tutorial, not an optional step.

Final thoughts

The moving parts look intimidating the first time, but the flow is always the same: build the image, store it, describe the task, run the service, and check the logs. Once you have done it once, the next deployment is mostly copy, paste, and change the names.

// key takeaways
  • Create a scoped IAM user for the AWS CLI instead of using the root account.
  • Push the Docker image to ECR before creating the ECS service.
  • The task execution role lets ECS pull the image and write logs to CloudWatch.
  • Use awsvpc networking and a public IP for a quick test deployment.
  • Inside a multi-container task, use the container name or a real host instead of localhost.
// FAQ
Do I need EC2 for ECS Fargate?
No. Fargate is serverless compute for containers. AWS runs the underlying hosts and you only define the task.
Why does MongoDB fail inside the task?
The most common causes are using localhost instead of a reachable host, a wrong password, or a missing authSource=admin in the connection string.
How much does this cost?
Fargate bills per vCPU and memory while the task runs. Delete the service when you finish testing, or you will keep paying.
Should I run MongoDB on Fargate?
For a demo it works. For production use MongoDB Atlas or another managed database instead of self-hosting the database in the same task.