Why Docker for Node.js
Before Docker, deploying Node.js at IVTREE meant SSH-ing into the server, pulling from git, running npm install, and praying the Node version matched. Docker eliminates all of that unpredictability — what runs locally runs in production.
The Dockerfile
# Multi-stage build keeps the final image small
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:20-alpine AS runner
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
CMD ["node", "server.js"]docker-compose for Local Dev
version: '3.8'
services:
api:
build: .
ports:
- "3000:3000"
environment:
- NODE_ENV=development
- MONGO_URI=mongodb://mongo:27017/appdb
volumes:
- .:/app
- /app/node_modules
depends_on:
- mongo
mongo:
image: mongo:7
ports:
- "27017:27017"
volumes:
- mongo_data:/data/db
volumes:
mongo_data:GitHub Actions CI/CD to AWS EC2
name: Deploy to EC2
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build Docker image
run: docker build -t myapp:latest .
- name: Deploy to EC2
uses: appleboy/ssh-action@v0.1.10
with:
host: ${{ secrets.EC2_HOST }}
username: ubuntu
key: ${{ secrets.EC2_SSH_KEY }}
script: |
cd /app
git pull origin main
docker compose down
docker compose up -d --buildEC2 Setup Checklist
- Use a t3.small or t3.medium for most Node.js apps (t2.micro is too slow)
- Set up an Elastic IP so your IP doesn't change on restart
- Use Nginx as a reverse proxy in front of your Node container
- Set up SSL with Certbot (Let's Encrypt) — it's free and takes 5 minutes
- Configure security groups to only expose ports 80 and 443 publicly
Containers don't solve bad architecture — they just make deployment consistent. Make sure your app is well-structured before you containerize it.