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)
π οΈ 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
π 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 },
});
Start locally:
npm install
npm run dev
Test:
curl http://localhost:5000/api/todos
π¦ 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"]
Build locally:
docker build -t nodejs-todo-api .
docker run -p 5000:5000 -e MONGO_URI=your_mongo_uri nodejs-todo-api
βοΈ Step 3 β Terraform AWS Infrastructure
Initialize
cd terraform
terraform init
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
- Create the repo:
terraform apply -target=aws_ecr_repository.app -auto-approve
- 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
π Step 5 β Deploy with Terraform
Apply the full infra:
terraform apply
Grab the output:
terraform output alb_dns
Test:
curl http://<alb_dns>/api/todos
π Step 6 β Deploy Updates
- Build and push a new image with
IMAGE_TAG=v2
. - Update
variables.tf
βimage_tag = "v2"
. - Run:
terraform apply -var="image_tag=v2"
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 oflatest
. - 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)