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)