Full-Stack Web Application with Automated CI/CD Pipeline and Infrastructure as Code
A comprehensive hands-on DevOps project demonstrating real-world CI/CD automation, containerization, and infrastructure management using Jenkins, Docker, Ansible, and Nginx.
- About This Project
- Technologies Used
- Techniques & Concepts
- Architecture
- Prerequisites
- Getting Started
- Setup Instructions
- Deployment
- Verification
- Screenshots
- Troubleshooting
- License
- Author
This project is built for learning DevOps through hands-on experience with industry-standard tools and real-world workflows. It demonstrates how to build, deploy, and maintain a full-stack web application using automated CI/CD pipelines, containerization, and infrastructure as code.
What You'll Learn:
- Setting up a complete CI/CD pipeline with Jenkins
- Implementing chained pipeline architecture for separation of concerns
- Containerizing applications with Docker and Docker Compose
- Automating infrastructure configuration with Ansible
- Managing SSL certificates with Let's Encrypt
- Setting up reverse proxy with Nginx
- Implementing monitoring with Prometheus and Grafana
Live Demo: https://devops2.himanmanduja.fun
GitHub Repository: https://github.com/HimanM/DevOps-Project-2
Docker Hub:
- Frontend: himanm/devops-project-2-frontend
- Backend: himanm/devops-project-2-backend
- Framework: Next.js 14 (React)
- Styling: Tailwind CSS, Shadcn UI
- Animation: Framer Motion
- Deployment: Docker container (port 3000)
- Framework: Python Flask
- API: RESTful endpoints with Prometheus metrics
- Deployment: Docker container (port 5000)
- Containerization: Docker, Docker Compose
- CI/CD: Jenkins (chained pipelines)
- Configuration Management: Ansible
- Reverse Proxy: Nginx (port 80 → 57002)
- SSL: Let's Encrypt (Certbot)
- Monitoring: Prometheus, Grafana
This project demonstrates the following DevOps techniques and best practices:
- Chained Pipeline Architecture: Separate Jenkins jobs for CI, CD, and Infrastructure, with automatic triggering between stages
- Continuous Integration (CI): Automated linting, testing, building, and Docker image publishing on every code commit
- Continuous Deployment (CD): Automated deployment to production VPS triggered by successful CI builds
- Infrastructure as Code (IaC): Ansible playbooks for automated Nginx and SSL configuration
- Containerization: Docker containers for consistent development and production environments
- Docker Compose Orchestration: Multi-container application management
- Automated SSL Management: Let's Encrypt certificate generation and renewal
- Reverse Proxy Configuration: Nginx for routing and load balancing
- Monitoring & Observability: Prometheus metrics collection and Grafana dashboards
- Secret Management: Jenkins credentials for secure handling of sensitive data
- Git-based Workflows: SCM integration with automatic pipeline triggers
The project implements a multi-stage CI/CD pipeline with automated infrastructure provisioning and deployment.
The complete workflow from code commit to production deployment:
- Code Push: Developer commits code to GitHub repository
- CI Pipeline (DevOps-CI): Jenkins detects changes via webhook, runs linting and tests, builds Docker images, and pushes to Docker Hub
- CD Trigger: Successful CI build automatically triggers the CD pipeline
- CD Pipeline (DevOps-CD): Jenkins connects to VPS via SSH, pulls latest images, and restarts containers
- Infrastructure Setup (DevOps-InitDomain): Ansible configures Nginx reverse proxy and SSL certificates (manual trigger for initial setup)
This project uses a chained pipeline architecture with three distinct Jenkins jobs that work together:
Purpose: Continuous Integration - builds and publishes Docker images
Trigger: GitHub webhook on push to main branch
Stages:
- Checkout: Pulls source code from GitHub
- Code Quality: Runs ESLint on frontend code
- Unit Tests: Executes pytest for backend validation
- Build Docker Images: Builds frontend and backend containers
- Push to DockerHub: Publishes images to Docker Hub registry
- Trigger CD: Automatically triggers DevOps-CD on success
Jenkinsfile Path: Jenkinsfile
Purpose: Continuous Deployment - deploys application to production VPS
Trigger: Upstream job (DevOps-CI) success
Stages:
- SSH Connection: Establishes connection to VPS
- Prepare Environment: Creates project directory structure
- Pull Images: Downloads latest Docker images from registry
- Deploy: Executes
docker compose up -dto restart services
Jenkinsfile Path: Jenkinsfile.deploy
Purpose: Infrastructure as Code - configures web server and SSL
Trigger: Manual (one-time setup or domain changes)
Stages:
- Install Ansible: Ensures Ansible is available on Jenkins agent
- Run Playbook: Executes Ansible configuration
- Configure Nginx: Sets up reverse proxy (port 80 → 57002)
- SSL Certificates: Generates Let's Encrypt certificates for HTTPS
Jenkinsfile Path: Jenkinsfile.initdomain
Before setting up this project, ensure you have the following:
- Docker: Version 20.10 or higher
- Docker Compose: Version 2.0 or higher
- Node.js: Version 18 or higher (for local frontend development)
- Python: Version 3.9 or higher (for local backend development)
- Git: For cloning the repository
- VPS/Server: Ubuntu 20.04 or higher with root access
- Jenkins: For CI/CD automation (installation covered below)
- Docker Hub Account: For storing Docker images
- Domain Name: For SSL certificate (optional but recommended)
Follow these steps to clone and replicate this project on your local machine or server.
First, clone the repository from GitHub to your local machine:
git clone https://github.com/HimanM/DevOps-Project-2.git
cd DevOps-Project-2This command downloads the entire project including all source code, Dockerfiles, Jenkinsfiles, and Ansible playbooks.
Start all services using Docker Compose for local testing:
docker compose -f docker-compose.local.yml up --buildThe
--buildflag rebuilds images to include any local changes. This command starts the frontend and backend services in containers for testing.
Once the containers are running, access the services:
- Frontend: http://localhost:3000
- Backend API: http://localhost:5000
Note
The first startup may take a few minutes as Docker downloads base images and installs dependencies.
Note
The monitoring services (Prometheus and Grafana) are commented out in the docker-compose files by default. To enable them, uncomment the monitoring sections in docker-compose.yml.
This section provides detailed step-by-step instructions for setting up the complete CI/CD pipeline with Jenkins.
Follow these steps to install Jenkins on Ubuntu/Debian systems.
Important
The Jenkins machine must have Node.js (npm) and Python (pip) installed to run frontend linting and backend tests in the CI pipeline. These are installed in Steps 2 and 3 below.
Jenkins requires Java Runtime Environment. OpenJDK 17 is recommended.
sudo apt update
sudo apt install fontconfig openjdk-17-jre
java -versionWhat this does: Updates package lists and installs OpenJDK 17, which is the Java runtime required by Jenkins. The final command verifies the installation.
Required for frontend linting in the CI pipeline.
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
node -v
npm -vWhat this does: Downloads and runs the NodeSource setup script to add Node.js 18.x repository, installs Node.js and npm, then verifies the installation. This is essential for running ESLint on the frontend code during CI builds.
Required for backend testing in the CI pipeline.
sudo apt install python3 python3-pip -y
python3 --version
pip3 --versionWhat this does: Installs Python 3 and pip (Python package manager), then verifies the installation. This is required for running pytest on the backend code during CI builds.
sudo wget -O /usr/share/keyrings/jenkins-keyring.asc \
https://pkg.jenkins.io/debian-stable/jenkins.io-2023.key
echo "deb [signed-by=/usr/share/keyrings/jenkins-keyring.asc] \
https://pkg.jenkins.io/debian-stable binary/" | sudo tee \
/etc/apt/sources.list.d/jenkins.list > /dev/nullWhat this does: Downloads the Jenkins GPG key for package verification and adds the official Jenkins Debian repository to your system's sources list. This ensures you get the latest stable Jenkins version.
sudo apt-get update
sudo apt-get install jenkinsWhat this does: Refreshes the package list to include Jenkins packages from the newly added repository, then installs Jenkins along with its dependencies.
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkinsWhat this does: Enables Jenkins to start automatically on system boot, starts the Jenkins service immediately, and displays its current status to verify it's running correctly.
Retrieve the initial admin password:
sudo cat /var/lib/jenkins/secrets/initialAdminPasswordWhat this does: Displays the auto-generated admin password needed for first-time Jenkins setup.
Navigate to http://<your-server-ip>:8080 and enter the password to complete the initial setup wizard. Install the suggested plugins when prompted.
Navigate to Manage Jenkins > Plugins > Available Plugins and install the following:
| Plugin Name | Purpose |
|---|---|
| Docker | Build and manage Docker containers in Jenkins pipelines |
| Docker Pipeline | Enables using Docker commands in pipeline scripts |
| NodeJS | Provides Node.js and npm for frontend linting (if not using Docker agent) |
| Pipeline: Stage View | Visualize pipeline execution stages in the UI |
| Ansible | Execute Ansible playbooks from Jenkins (optional) |
| SSH Agent | Enables SSH credentials for remote server deployment |
After installation, restart Jenkins if prompted.
Note
The Docker and Docker Pipeline plugins are essential for this project as all build and deployment steps use Docker containers.
Navigate to Manage Jenkins > Credentials > System > Global credentials (unrestricted).
Create the following credentials:
| ID | Kind | Description | Example Value |
|---|---|---|---|
dockerhub-username |
Username with password | Docker Hub authentication | Username: himanmPassword: <access-token> |
vps-credentials |
Username with password | VPS SSH access | Username: rootPassword: <server-password> |
vps-ip |
Secret text | VPS IP address | 165.22.54.216 |
DEVOPS2_DOMAIN |
Secret text | Domain name | devops2.himanmanduja.fun |
Important
For Docker Hub, use an Access Token instead of your password. Generate one at Docker Hub Security Settings. This is more secure and can be easily revoked if compromised.
Note
Ensure the VPS user has Docker permissions by running: sudo usermod -aG docker <username> and then log out and back in for the changes to take effect.
Create the Continuous Integration pipeline that builds and publishes Docker images:
- Click New Item in Jenkins dashboard
- Enter name:
DevOps-CI - Select Pipeline and click OK
- Under Pipeline section:
- Definition: Pipeline script from SCM
- SCM: Git
- Repository URL:
https://github.com/HimanM/DevOps-Project-2.git(or your fork) - Branch:
*/main - Script Path:
Jenkinsfile
- Click Save
What this does: Creates a pipeline that pulls the Jenkinsfile from your repository and executes it. This pipeline runs linting, tests, builds Docker images, pushes them to Docker Hub, and triggers the CD pipeline.
Create the Continuous Deployment pipeline that deploys to your VPS:
- Click New Item in Jenkins dashboard
- Enter name:
DevOps-CD - Select Pipeline and click OK
- Under Build Triggers:
- Check Build after other projects are built
- Projects to watch:
DevOps-CI - Trigger only if build is stable
- Under Pipeline section:
- Definition: Pipeline script from SCM
- SCM: Git
- Repository URL:
https://github.com/HimanM/DevOps-Project-2.git(or your fork) - Branch:
*/main - Script Path:
Jenkinsfile.deploy
- Click Save
What this does: Creates a pipeline that automatically runs when DevOps-CI completes successfully. It connects to your VPS via SSH, pulls the latest Docker images, and restarts the containers with the new versions.
Create the Infrastructure as Code pipeline for Nginx and SSL setup:
- Click New Item in Jenkins dashboard
- Enter name:
DevOps-InitDomain - Select Pipeline and click OK
- Under Pipeline section:
- Definition: Pipeline script from SCM
- SCM: Git
- Repository URL:
https://github.com/HimanM/DevOps-Project-2.git(or your fork) - Branch:
*/main - Script Path:
Jenkinsfile.initdomain
- Click Save
What this does: Creates a pipeline that runs Ansible playbooks to configure Nginx reverse proxy and generate Let's Encrypt SSL certificates. This is typically run once during initial setup or when changing domain names.
Follow these steps to deploy the application to production:
Manually trigger the DevOps-InitDomain job in Jenkins:
# From Jenkins UI: Click on DevOps-InitDomain > Build NowWhat this does: Executes Ansible playbooks to configure Nginx as a reverse proxy and generates Let's Encrypt SSL certificates for HTTPS. This is a one-time setup unless you change domain names.
Note
Before running this pipeline, ensure your domain's DNS A record points to your VPS IP address.
Push code to the GitHub repository to trigger the CI pipeline automatically via webhook:
git add .
git commit -m "Initial commit"
git push origin mainWhat this does: GitHub webhook notifies Jenkins of the push, which triggers the DevOps-CI pipeline. The pipeline runs linting, tests, builds Docker images, and pushes them to Docker Hub.
Alternatively, you can manually trigger the DevOps-CI job from the Jenkins UI by clicking Build Now.
Upon successful CI build, the DevOps-CD pipeline automatically triggers and deploys to your VPS.
What this does: Jenkins connects to your VPS via SSH, pulls the latest Docker images from Docker Hub, and runs docker compose up -d to start/restart the containers with the new images.
After deployment, verify that the application is running correctly:
SSH into your VPS and verify all containers are running:
# SSH into VPS
ssh root@<vps-ip>
# Check running containers
docker ps
# Expected output should show containers for:
# - frontend (port 57002 mapped to 3000)
# - backend (port 5000)What this does: Lists all running Docker containers to ensure the deployment was successful.
Note
The monitoring services (Prometheus and Grafana) are commented out by default. If you enabled them in your docker-compose.yml, you'll also see those containers.
Check logs for any errors:
# View frontend logs
docker logs devops-project-2-frontend-1
# View backend logs
docker logs devops-project-2-backend-1
# View Nginx logs (if configured)
sudo tail -f /var/log/nginx/access.logWhat this does: Displays real-time logs from the containers to troubleshoot any startup or runtime issues.
Test the application from your local machine:
# Test frontend (via domain with SSL)
curl -I https://your-domain.com
# Test backend API
curl https://your-domain.com/api/data
# Test backend metrics
curl https://your-domain.com/metricsWhat this does: Verifies that the application is accessible via HTTPS and the API endpoints are responding correctly.
If you enabled the monitoring services in docker-compose.yml:
- Grafana Dashboard:
http://<vps-ip>:3001(credentials: admin/admin) - Prometheus Metrics:
http://<vps-ip>:9090
Note
The monitoring services are commented out by default. To enable them, uncomment the prometheus and grafana sections in docker-compose.yml and redeploy.
Jenkins dashboard showing all three pipeline jobs
Overview of Jenkins jobs with build history
CI Pipeline stages showing the complete build workflow
CD Pipeline stages showing deployment workflow
Infrastructure Pipeline stages showing Ansible configuration
DevOps-CI pipeline configuration with SCM settings
DevOps-CD pipeline configuration with upstream trigger
DevOps-InitDomain pipeline configuration
Jenkins credentials management showing required secrets
The project includes a pre-configured monitoring stack for observability (currently commented out by default).
To enable Prometheus and Grafana monitoring:
- Uncomment the
prometheusandgrafanaservices indocker-compose.yml - Redeploy the application with
docker compose up -d
- URL:
http://<vps-ip>:9090 - Configuration:
monitoring/prometheus.yml - Metrics Endpoint: Backend exposes metrics at
/metrics - Targets: Monitors backend Flask application
- URL:
http://<vps-ip>:3001 - Default Credentials:
admin/admin - Data Source: Pre-configured Prometheus connection
- Dashboards: Import from
monitoring/grafana/dashboards/
Note
Remember to change the default Grafana admin password after first login for security.
Error: permission denied while trying to connect to the Docker daemon socket
Cause: The Jenkins user doesn't have permission to access the Docker daemon.
Solution:
# Add jenkins user to docker group
sudo usermod -aG docker jenkins
# Restart Jenkins service to apply changes
sudo systemctl restart jenkinsWhat this does: Grants the Jenkins user permission to run Docker commands without sudo.
Error: Host key verification failed
Cause: SSH strict host key checking prevents connection to unknown hosts.
Solution: The pipelines use -o StrictHostKeyChecking=no to bypass this. If issues persist, manually SSH once:
ssh root@<vps-ip>
# Type 'yes' when prompted to add the host to known_hostsWhat this does: Adds the VPS host key to the known_hosts file, preventing future verification prompts.
Error: Nginx returns 502 Bad Gateway error
Cause: Backend service is not running or not accessible.
Solution:
# Check if backend container is running
docker ps | grep backend
# Check backend logs for errors
docker logs devops-project-2-backend-1
# Check backend API endpoint
curl http://localhost:5000/api/data
# Restart containers if needed
cd ~/devops-project-2
docker compose restartWhat this does: Diagnoses whether the backend service is running and accessible, then restarts it if necessary.
Error: EACCES: permission denied or npm: command not found
Cause: Node.js/npm is not installed on the Jenkins agent.
Solution:
# Install Node.js on Jenkins machine
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejsWhat this does: Installs Node.js and npm on the Jenkins agent, allowing the CI pipeline to run frontend linting.
Error: Certificate generation fails in DevOps-InitDomain pipeline
Cause: Domain DNS not properly configured or rate limit reached.
Solution:
# Verify DNS points to your VPS
nslookup your-domain.com
# Check Certbot logs
sudo tail -f /var/log/letsencrypt/letsencrypt.log
# Test with dry-run mode
sudo certbot --nginx --dry-run -d your-domain.comWhat this does: Verifies DNS configuration and tests certificate generation without hitting rate limits.
Error: container exited with code 1
Cause: Environment variables missing or incorrect configuration.
Solution:
# Check environment variables
cat docker-compose.yml
# View detailed logs
docker compose logs backend
docker compose logs frontend
# Recreate containers
docker compose down
docker compose up -d --force-recreateWhat this does: Inspects configuration and logs to identify the root cause, then recreates containers with fresh configuration.
This project is open source and available for educational purposes. Feel free to use, modify, and distribute it for learning DevOps practices.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software.
This project is intended for educational and learning purposes. It demonstrates DevOps best practices and is not intended for production use without proper security hardening and configuration.
Name: Himan Manduja
GitHub: @HimanM
Repository: github.com/HimanM/DevOps-Project-2
Live Demo: devops2.himanmanduja.fun
For questions, suggestions, or collaboration opportunities:
- GitHub Issues: Open an issue
- Email: Contact via GitHub profile
Contributions are welcome! If you'd like to improve this project:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
Learning Resource: This project was created as a hands-on learning exercise for DevOps practices. Feel free to use it as a reference for your own DevOps journey!
Star this repository if you find it helpful!