DEV Community

Manthan Ankolekar
Manthan Ankolekar

Posted on

πŸš€ Deploying a Node.js CRUD App on AWS with Terraform

Managing cloud infrastructure manually can quickly become messy. Terraform allows us to define infrastructure as code (IaC), making deployments repeatable, version-controlled, and automated.

In this guide, we’ll build a Node.js CRUD API (with Express + MongoDB) and deploy it to AWS ECS Fargate using Terraform.


🧩 What We’re Building

  • A RESTful Todo API (CRUD endpoints with Express + MongoDB).
  • Dockerized app pushed to Amazon ECR.
  • AWS ECS Fargate service running containers in a managed VPC.
  • Application Load Balancer (ALB) to expose the service.
  • Secrets Manager for storing MongoDB credentials securely.
  • CloudWatch Logs for centralized logging.

Here’s the final architecture:

Client β†’ ALB β†’ ECS (Fargate Task) β†’ MongoDB (Atlas/Local)
        ↑
      CloudWatch (logs)
Enter fullscreen mode Exit fullscreen mode

πŸ› οΈ Tech Stack

Backend

  • Node.js + Express
  • MongoDB + Mongoose
  • CORS + dotenv

Infrastructure

  • Docker (containerization)
  • Terraform (IaC)
  • AWS ECR (image registry)
  • AWS ECS Fargate (orchestration)
  • AWS ALB (load balancing)
  • AWS Secrets Manager (secure DB credentials)
  • AWS IAM + Security Groups

πŸ“ Project Structure

Your project is split into two main parts: app code and infra code.

manthanank-nodejs-terraform/
β”œβ”€β”€ server.js                 # Express entry
β”œβ”€β”€ Dockerfile                # Build container
β”œβ”€β”€ config/db.js              # Mongo connection
β”œβ”€β”€ controllers/todoController.js
β”œβ”€β”€ models/Todo.js
β”œβ”€β”€ routes/todoRoutes.js
β”œβ”€β”€ terraform/                # Infrastructure as Code
β”‚   β”œβ”€β”€ vpc.tf                # VPC + networking
β”‚   β”œβ”€β”€ ecs.tf                # ECS cluster/service
β”‚   β”œβ”€β”€ ecr.tf                # ECR repo
β”‚   β”œβ”€β”€ iam.tf                # IAM roles/policies
β”‚   β”œβ”€β”€ security.tf           # Security groups
β”‚   β”œβ”€β”€ logs.tf               # CloudWatch logs
β”‚   β”œβ”€β”€ secrets.tf            # Secrets Manager
β”‚   β”œβ”€β”€ variables.tf          # Config vars
β”‚   β”œβ”€β”€ outputs.tf            # Useful outputs
β”‚   └── provider.tf           # AWS provider setup
Enter fullscreen mode Exit fullscreen mode

πŸ“ Step 1 β€” Build the Node.js API

Our CRUD endpoints live under /api/todos.
Example schema:

const todoSchema = new mongoose.Schema({
  title: { type: String, required: true, trim: true },
  description: { type: String, default: "" },
  completed: { type: Boolean, default: false },
  createdAt: { type: Date, default: Date.now },
});
Enter fullscreen mode Exit fullscreen mode

Start locally:

npm install
npm run dev
Enter fullscreen mode Exit fullscreen mode

Test:

curl http://localhost:5000/api/todos
Enter fullscreen mode Exit fullscreen mode

πŸ“¦ Step 2 β€” Dockerize the App

Dockerfile:

FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
ENV PORT=5000
EXPOSE 5000
CMD ["node", "server.js"]
Enter fullscreen mode Exit fullscreen mode

Build locally:

docker build -t nodejs-todo-api .
docker run -p 5000:5000 -e MONGO_URI=your_mongo_uri nodejs-todo-api
Enter fullscreen mode Exit fullscreen mode

☁️ Step 3 β€” Terraform AWS Infrastructure

Initialize

cd terraform
terraform init
Enter fullscreen mode Exit fullscreen mode

Key Resources

  • ECR β€” stores our container image (ecr.tf).
  • ECS Fargate Service β€” runs containers (ecs.tf).
  • ALB + Target Group β€” load balancing (ecs.tf).
  • VPC + Subnets β€” networking (vpc.tf).
  • IAM Roles β€” ECS execution permissions (iam.tf).
  • Secrets Manager β€” injects MONGO_URI securely (secrets.tf).
  • CloudWatch Logs β€” for application logs (logs.tf).

🐳 Step 4 β€” Push Image to ECR

  1. Create the repo:
terraform apply -target=aws_ecr_repository.app -auto-approve
Enter fullscreen mode Exit fullscreen mode
  1. Build & push:
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=ap-south-1
REPO=$AWS_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/node-crud-app
IMAGE_TAG=v1

docker build -t node-crud-app:$IMAGE_TAG .
aws ecr get-login-password --region $REGION | \
  docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com
docker tag node-crud-app:$IMAGE_TAG $REPO:$IMAGE_TAG
docker push $REPO:$IMAGE_TAG
Enter fullscreen mode Exit fullscreen mode

πŸš€ Step 5 β€” Deploy with Terraform

Apply the full infra:

terraform apply
Enter fullscreen mode Exit fullscreen mode

Grab the output:

terraform output alb_dns
Enter fullscreen mode Exit fullscreen mode

Test:

curl http://<alb_dns>/api/todos
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Step 6 β€” Deploy Updates

  1. Build and push a new image with IMAGE_TAG=v2.
  2. Update variables.tf β†’ image_tag = "v2".
  3. Run:
terraform apply -var="image_tag=v2"
Enter fullscreen mode Exit fullscreen mode

ECS performs a rolling update behind the ALB.


πŸ“Š Monitoring & Logs

  • CloudWatch Logs: All app logs stream automatically.
  • ALB Health Checks: Ensure tasks are healthy.
  • Troubleshooting: Check ECS task logs in CloudWatch if service won’t start.

πŸ”’ Security Best Practices

  • Use private subnets + NAT Gateway in production.
  • Restrict Secrets Manager IAM role to a single secret ARN.
  • Use immutable image tags (v1, v2) instead of latest.
  • Enable HTTPS on ALB with ACM certificates.

🎯 Conclusion

With just a few .tf files, we’ve automated provisioning of:

  • Containerized Node.js API
  • Secure AWS networking & IAM
  • Load-balanced ECS service
  • Secrets and logs managed by AWS

This approach scales to any microservice β€” just add more services, modules, or CI/CD pipelines.


✨ Pro tip: Next step, integrate GitHub Actions or GitLab CI to automatically build/push Docker images and run terraform apply for full CI/CD.


πŸ”— GitHub Repository: manthanank/nodejs-terraform
πŸ§‘β€πŸ’» Author: Manthan Ankolekar


Top comments (0)