π End-to-End DevOps Project: Deploying a Node.js App on AWS EC2 with Docker, Kubernetes & Ingress
A frelance mern stack developer at fiver and aspiring devops engineer
Today I completed a full real-world DevOps deployment where I built, containerized, and deployed a Node.js application using Docker, Kubernetes, Minikube, Ingress, HTTPS, and RBAC, all running on AWS EC2.
This project helped me connect all core DevOps concepts into one working system.
π§ Tech Stack Used
Node.js β Application layer
Docker (Multi-Stage Build) β Optimized container image
Docker Hub β Image registry
AWS EC2 (Linux) β Cloud infrastructure
Kubernetes (Minikube) β Container orchestration
kubectl β Cluster management
Kubernetes Deployment & Service
NodePort Service
Ingress Controller
HTTPS (TLS Certificates)
Docker Volumes & Networks
RBAC (Role & RoleBinding)
π§± Step 1: Node.js Application
I started with a simple Node.js web application that serves a static page to confirm deployment status.
π³ Step 2: Docker Multi-Stage Build
To keep the image lightweight and production-ready, I used a multi-stage Docker build:
FROM node:18 AS build WORKDIR /app COPY package*.json ./ RUN npm install COPY . .
FROM node:18-alpine WORKDIR /app COPY --from=build /app . EXPOSE 3000 CMD ["node", "app.js"]
Benefits:
Smaller image size
Faster deployments
Better security
π¦ Step 3: Push Image to Docker Hub docker build -t /devops-node . docker login docker push /devops-node
Now the image is accessible from anywhere, including Kubernetes.
βοΈ Step 4: AWS EC2 Setup
Launched an EC2 Linux instance
Installed:
Docker
kubectl
Minikube
Ran Minikube inside EC2 to simulate a real Kubernetes environment
βΈοΈ Step 5: Kubernetes Deployment
Created a Deployment with multiple replicas:
apiVersion: apps/v1 kind: Deployment metadata: name: node-deploy spec: replicas: 2 selector: matchLabels: app: node template: metadata: labels: app: node spec: containers: - name: node image: /devops-node ports: - containerPort: 3000
Applied using:
kubectl apply -f deployment.yaml
π Step 6: Kubernetes Service (NodePort) apiVersion: v1 kind: Service metadata: name: node-service spec: type: NodePort selector: app: node ports:
- port: 80 targetPort: 3000
This exposed the app internally within the cluster.
π Step 7: Ingress Controller
Enabled ingress in Minikube:
minikube addons enable ingress
Ingress configuration:
apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: node-ingress spec: rules:
host: devops.local http: paths:
- path: / pathType: Prefix backend: service: name: node-service port: number: 80
Mapped the domain in /etc/hosts:
<MINIKUBE_IP> devops.local
π Step 8: HTTPS with TLS
Generated certificates:
openssl req -x509 -nodes -days 365 -newkey rsa:2048
-keyout key.pem -out cert.pem
Created Kubernetes TLS secret:
kubectl create secret tls node-tls --cert=cert.pem --key=key.pem
Updated ingress with TLS configuration and re-applied it.
The site now runs on HTTPS.
π¦ Step 9: Docker Volumes & Network
Created Docker volume for persistent data
Used Docker networks for container communication
Ensured clean separation of concerns
π Step 10: RBAC Security
Created a Role:
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: devops-role rules:
- apiGroups: [""] resources: ["pods"] verbs: ["get", "list"]
Bound it using RoleBinding:
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: devops-binding subjects:
- kind: User name: devops-user roleRef: kind: Role name: devops-role apiGroup: rbac.authorization.k8s.io
This ensured least-privilege access inside the cluster.
β Final Outcome
β Node.js app running on AWS EC2 β Docker multi-stage image pushed to Docker Hub β Kubernetes Deployment & Service β Ingress routing with HTTPS β Persistent storage using volumes β Secure access using RBAC
π― What I Learned
How real DevOps components connect end-to-end
Debugging Kubernetes ImagePullBackOff issues
Difference between NodePort, Service & Ingress
HTTPS using Kubernetes TLS secrets
Importance of RBAC in production clusters