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)