CID Meme Verse is a production-grade, 3-tier web application designed to generate, gallery, and vote on pop-culture memes inspired by India's legendary TV series CID. The application leverages a cutting-edge stack (React, Node.js, Express, MongoDB) integrated with Google's Gemini Generative AI for automated, context-aware dialogue generation.
This repository serves as a showcase for Enterprise DevOps Practices, featuring optimized Docker multi-stage builds, container orchestration, comprehensive automated test suites (Jest + Vitest), and a robust CI/CD pipeline that automatically builds, tests, pushes, and deploys the entire application stack to an AWS EC2 instance upon every git push.
The application is built using a modern 3-tier architecture, fully decoupled, containerized, and deployed behind a reverse proxy.
graph TD
User([π End User]) -->|HTTP / HTTPS| Nginx[π Nginx Reverse Proxy on EC2 Host]
subgraph AWS_EC2_Instance [AWS EC2 Instance - Ubuntu Host]
Nginx -->|Routes /api/* to Port 5000| Backend[π¦ Backend Container: Node/Express]
Nginx -->|Routes static/other to Port 3000| Frontend[π¦ Frontend Container: React/Vite]
Backend -->|Persists Memes & Characters| MongoDB[π¦ Database Container: MongoDB]
MongoDB <-->|Named Volume| Vol[(mongo-data)]
end
Backend -->|Generates Dialogues| Gemini[π€ Google Gemini AI API]
- Host-Level Reverse Proxy (Nginx): The entry point of the AWS EC2 instance. It acts as an SSL terminator and traffic router.
- Requests starting with
/apiare reverse-proxied to the Backend Container (running on port5000). - All other traffic is routed to the Frontend Container (running on port
3000), which serves the optimized production assets.
- Requests starting with
- Container Network Isolation: The application containers live in a single bridge network defined in
docker-compose.yml. They communicate securely via service-name DNS (e.g., the Backend connects to the database viamongodb://mongodb:27017/mydbrather than exposing database ports publicly). - Data Persistence: Database files are decoupled from the MongoDB container lifecycle using a named Docker volume (
mongo-data), ensuring high availability and zero data-loss during deployments or container restarts.
This project was built to illustrate production DevOps workflows:
- Automated Quality Gates: The pipeline triggers on pushes to the
mainbranch. It sets up Node.js v20 in a containerized environment, installs dependencies, and runs:- Backend Unit & Integration Tests: Jest and Supertest are used to validate the API endpoints (e.g., verifying character fetching, database responses, and API status codes).
- Frontend Component Tests: Vitest is used to render the React tree and assert proper rendering of UI elements (e.g., verifying that the app mounts successfully and the navigation works).
- Build-Once, Run-Anywhere: If all tests pass, the pipeline proceeds to the build phase, preventing broken or buggy code from reaching staging/production.
- React Frontend Dockerfile (Multi-Stage Build):
- Build Stage: Uses
node:18-alpineto compile the Vite static assets. Accepts build-time argumentVITE_API_URLto inject backend host IP dynamically. - Production Stage: Uses a lightweight static server (
serveutility) to run the static React bundle. The final image size is reduced by over 70% because dev-dependencies, Node package managers, and raw source code are excluded from the runtime container.
- Build Stage: Uses
- Backend Dockerfile: Engineered using
node:18-alpineas a minimal base image, ensuring a minimal vulnerability footprint and extremely fast container spin-up times. Layer caching is optimized by copyingpackage*.jsonand runningnpm installprior to copying source code.
- GitHub Actions Deploy Runner: Upon successful build, the workflow logs into Docker Hub securely using secrets, builds target images, tags them, and pushes them to the public/private registry.
- Remote Deployment via SSH: The deploy job uses
appleboy/ssh-actionto securely execute a deployment shell script on the AWS EC2 instance. - Zero-Downtime Deployment Flow:
- SSHs into the EC2 instance.
- Runs
docker compose pullto download the newly pushed images from Docker Hub. - Re-creates the containers gracefully in detached mode using
docker compose down && docker compose up -d, ensuring minimal downtime during service swap.
- Core: React 18, Vite (Fast build tool)
- Styling: Tailwind CSS v4, Shadcn/ui (Accessible buttons & layout components)
- Testing: Vitest, JSDOM,
@testing-library/react
- Runtime: Node.js, Express.js
- Database Client: Mongoose (MongoDB ODM)
- AI Orchestration:
@google/generative-ai(Google Gemini Pro integration for context-aware CID dialog generation) - Testing: Jest, Supertest
- Platform: AWS EC2 (Ubuntu 22.04 LTS)
- Containerization: Docker, Docker Compose
- CI/CD Orchestration: GitHub Actions
- Image Registry: Docker Hub
- Reverse Proxy: Nginx
Follow these steps to run the complete 3-tier application in your local environment.
- Install Docker & Docker Compose.
This is the simplest way to run the entire multi-container stack locally.
git clone https://github.com/anuj-gope/Cid.git
cd CidCreate a .env file inside the backend/ directory:
touch backend/.envAdd the following content:
MONGO_URL=mongodb://mongodb:27017/mydb
GEMINI_API_KEY=your_actual_google_gemini_api_keyRun the following command at the root of the project:
docker compose up --build -dThis command builds the frontend and backend Docker images, downloads the official MongoDB image, establishes the network, and runs the containers in detached mode.
To populate your MongoDB database with CID characters (ACP Pradyuman, Abhijeet, Daya, etc.) and their famous dialogues, run the seed script inside the backend container:
docker exec -it backend node seed.js- Frontend: Open
http://localhost:3000in your browser. - Backend API: Verify by visiting
http://localhost:5000/api. - MongoDB: Accessible locally on
localhost:27017.
If you wish to run the app outside of Docker containers for active development:
Ensure MongoDB is running locally on port 27017.
cd backend
npm install
# Create a local .env file
echo "MONGO_URL=mongodb://localhost:27017/mydb" > .env
echo "GEMINI_API_KEY=your_gemini_key" >> .env
# Seed initial data
npm run seed # runs 'node seed.js'
# Start dev server
node server.jsThe backend API is now running at http://localhost:5000.
cd ../frontend
npm install
npm run devVite will start the client on http://localhost:5173 (or another port outputted in the terminal).
Note: For local development, ensure Vite's server proxy routes /api requests to http://localhost:5000.
To replicate this deployment pipeline in your own cloud account:
- Launch an Ubuntu EC2 instance on AWS and allow ports
80(HTTP),22(SSH),5000(Backend API), and3000(Frontend Web) in the Security Group. - Install Docker and Docker Compose on the host:
sudo apt update sudo apt install docker.io docker-compose -y sudo usermod -aG docker ubuntu && newgrp docker - Create a directory named
appin the home directory (/home/ubuntu/app) and place the project'sdocker-compose.ymlfile there. This is where the CD SSH action will run command workflows.
Install Nginx:
sudo apt install nginx -yCreate a configuration file at /etc/nginx/sites-available/cid-meme:
server {
listen 80;
server_name your-ec2-public-ip-or-domain;
# Frontend Routing
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# Backend API Routing
location /api/ {
proxy_pass http://127.0.0.1:5000/api/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Link the file and restart Nginx:
sudo ln -s /etc/nginx/sites-available/cid-meme /etc/nginx/sites-enabled/
sudo systemctl restart nginxNavigate to Settings > Secrets and variables > Actions in your GitHub repository and add the following repository secrets:
DOCKER_USERNAME: Your Docker Hub username.DOCKER_PASSWORD: Your Docker Hub personal access token or password.EC2_HOST: The public IP or DNS domain name of your AWS EC2 instance.EC2_USER: The SSH username (usuallyubuntufor Ubuntu Server).EC2_SSH_KEY: The raw private SSH key (.pemfile) used to authenticate access to your EC2 instance.
Once configured, pushing changes to the main branch will automatically execute the end-to-end pipeline: Test β‘οΈ Containerize β‘οΈ Upload to Registry β‘οΈ SSH Deploy β‘οΈ Restart Container Stack.
You can run the built-in test suites locally to ensure system integrity.
The backend uses Jest and Supertest to assert server behavior.
cd backend
npm testThe frontend uses Vitest to execute React unit tests.
cd frontend
npm test -- --run- Production-Grade Secret Management: Transition away from storing
.envfiles manually on the EC2 host. Suggest migrating to AWS Secrets Manager or HashiCorp Vault, where credentials (such as the Gemini API Key) are fetched dynamically at runtime. - Container Security & Vulnerability Scanning: Integrate Trivy or Anchore scanning into the GitHub Actions CI pipeline to scan built Docker images for high/critical vulnerabilities before pushing them to Docker Hub.
- Infrastructure as Code (IaC): Replace the manual setting up of the AWS EC2 instance and Nginx reverse proxy with Terraform or AWS CloudFormation to achieve fully reproducible environment provisioning.
- Transition to Serverless Container Orchestration: Discuss migrating from Docker Compose to AWS ECS with Fargate or AWS EKS (Kubernetes) to achieve auto-scaling, high availability across multiple availability zones, and native rolling update strategies.
- Monitoring and Observability Stack: Propose mounting a Prometheus container alongside the stack to scrape Node.js API metrics (
express-prom-bundle) and database metrics, visualized on a centralized Grafana Dashboard for real-time monitoring and alerting.
This project is licensed under the MIT License - see the LICENSE file for details.
Made with π», π³, and plenty of "Kuch toh gadbad hai..." dialogues by the CID Forensic Lab!