A production-style DevOps project demonstrating a Flask + MySQL application containerized with Docker, automated with Jenkins, validated with GitHub Actions, and deployed on AWS EC2.
This project demonstrates an end-to-end CI/CD workflow for a two-tier Flask application.
The application consists of:
- A Flask web application responsible for the application layer.
- A MySQL database responsible for persistent data storage.
The application is packaged using Docker, orchestrated with Docker Compose, automatically built and deployed using Jenkins, validated using GitHub Actions, and hosted on an AWS EC2 Ubuntu server.
The project was built as a practical DevOps project with a focus on real deployment workflows, Linux administration, CI/CD automation, containerization, troubleshooting, and cloud deployment.
- Build a Flask web application.
- Connect Flask with a MySQL database.
- Containerize the application using Docker.
- Orchestrate multiple services with Docker Compose.
- Build a Jenkins CI/CD pipeline.
- Automatically build Docker images.
- Automatically deploy containers.
- Validate the application using health checks.
- Use GitHub Actions for continuous integration.
- Deploy the application on AWS EC2.
- Practice real-world Linux and Jenkins troubleshooting.
βββββββββββββββββββββββ
β Developer β
β β
β Write Code β
β Commit Changes β
ββββββββββββ¬βββββββββββ
β
β git push
βΌ
βββββββββββββββββββββββ
β GitHub Repository β
β β
β Source Code β
β Jenkinsfile β
β Docker Files β
ββββββββββββ¬βββββββββββ
β
βββββββββββββββββ΄βββββββββββββββββ
β β
βΌ βΌ
βββββββββββββββββββ βββββββββββββββββββ
β GitHub Actions β β Jenkins β
β β β CI/CD Server β
β Validate Code β β β
β Install Python β β Checkout Code β
β Run CI β β Build Image β
βββββββββββββββββββ β Deploy App β
ββββββββββ¬βββββββββ
β
βΌ
ββββββββββββββββββββββββββββββ
β AWS EC2 β
β Ubuntu Linux β
β β
β ββββββββββββββββββββββββ β
β β Flask Container β β
β β β β
β β Application Layer β β
β β Port: 5000 β β
β ββββββββββββ¬ββββββββββββ β
β β β
β βΌ β
β ββββββββββββββββββββββββ β
β β MySQL Container β β
β β β β
β β Database Layer β β
β β Port: 3306 β β
β ββββββββββββββββββββββββ β
ββββββββββββββββ¬ββββββββββββββ
β
βΌ
βββββββββββββββββββββββ
β End User β
β β
β Flask Application β
βββββββββββββββββββββββ
Developer
β
β Push Code
βΌ
GitHub Repository
β
ββββββββββββββββββββββββββββββββ
β β
βΌ βΌ
GitHub Actions Jenkins Pipeline
β β
β Validate Application β Checkout Source
β β
β βΌ
β Build Docker Image
β β
β βΌ
β Docker Compose
β β
β βΌ
β Deploy Containers
β β
β βΌ
β Health Check
β β
β βΌ
ββββββββββββββββββββββββΊ Deployment Successful
β
βΌ
AWS EC2 Server
β
βββββββββββ΄ββββββββββ
βΌ βΌ
Flask App MySQL DB
- Flask web application.
- MySQL database integration.
- Dockerized application.
- Docker Compose multi-container deployment.
- Jenkins declarative pipeline.
- Automated Docker image builds.
- Automated container deployment.
- GitHub Actions continuous integration.
- AWS EC2 deployment.
- Linux Ubuntu server administration.
- Application health check endpoint.
- REST API endpoint.
- Version endpoint.
- Metrics endpoint.
- Persistent MySQL volume.
- Dedicated Docker network.
- Real-world Jenkins and Docker troubleshooting.
| Technology | Purpose |
|---|---|
| Python | Application development |
| Flask | Web framework |
| MySQL | Relational database |
| Docker | Application containerization |
| Docker Compose | Multi-container orchestration |
| Jenkins | CI/CD automation |
| GitHub Actions | Continuous integration |
| GitHub | Source code management |
| AWS EC2 | Cloud deployment |
| Ubuntu Linux | Server operating system |
| Git | Version control |
two-tier-flask-cicd/
β
βββ .github/
β βββ workflows/
β βββ ci.yml
β
βββ app/
β βββ app.py
β βββ requirements.txt
β
βββ database/
β βββ ...
β
βββ docker/
β βββ Dockerfile
β βββ docker-compose.yml
β
βββ docs/
β βββ ...
β
βββ diagrams/
β βββ architecture-diagram.png
β
βββ jenkins/
β βββ ...
β
βββ .env.example
βββ .gitignore
βββ Jenkinsfile
βββ LICENSE
βββ README.md
| Endpoint | Description |
|---|---|
/ |
Application home page |
/health |
Application health check |
/api/messages |
Returns application messages |
/version |
Displays application version |
/metrics |
Displays application metrics |
curl http://localhost:5000/healthExample:
{
"status": "healthy"
}curl http://localhost:5000/api/messagescurl http://localhost:5000/versioncurl http://localhost:5000/metricsgit clone https://github.com/revanth-bl/devops-two-tier-flask-cicd.git
cd devops-two-tier-flask-cicdpython -m venv venvLinux/macOS:
source venv/bin/activateWindows PowerShell:
venv\Scripts\Activate.ps1pip install -r app/requirements.txtpython app/app.pyOpen:
http://localhost:5000
docker build -t two-tier-flask-app -f docker/Dockerfile .docker compose -f docker/docker-compose.yml up -d --builddocker psdocker imagesdocker compose -f docker/docker-compose.yml logsdocker compose -f docker/docker-compose.yml downdocker compose -f docker/docker-compose.yml down -vcurl http://localhost:5000/healthThe application uses two primary services:
βββββββββββββββββββββββββββββββββββββββββββ
β Docker Network β
β β
β ββββββββββββββββββββ β
β β Flask Container β β
β β β β
β β Python + Flask ββββββββ β
β β Port 5000 β β β
β ββββββββββββββββββββ βΌ β
β ββββββββββββββββ β
β β MySQL β β
β β Container β β
β β β β
β β Port 3306 β β
β ββββββββ¬ββββββββ β
β β β
β Persistent Volume β
βββββββββββββββββββββββββββββββββββββββββββ
The Flask application communicates with MySQL through the Docker Compose network.
The database uses a persistent volume to prevent data loss when the container is recreated.
The application is deployed on an Ubuntu EC2 instance.
AWS
βββ EC2 Instance
βββ Ubuntu Linux
βββ Docker
βββ Docker Compose
βββ Java
βββ Jenkins
βββ Flask Container
βββ MySQL Container
- Launch an Ubuntu EC2 instance.
- Configure the EC2 security group.
- Connect to the server using SSH.
- Update the Ubuntu package index.
- Install Docker.
- Install Docker Compose.
- Install Java.
- Install Jenkins.
- Add the Jenkins user to the Docker group.
- Clone the GitHub repository.
- Configure the Jenkins pipeline.
- Run the pipeline.
- Build the Docker image.
- Deploy the containers.
- Verify the application.
ssh -i <key-file>.pem ubuntu@<EC2-PUBLIC-IP>docker --versiondocker compose versionsudo systemctl status jenkinsThe Jenkins pipeline automates the deployment process.
Checkout Source
β
βΌ
Build Docker Image
β
βΌ
Deploy with Docker Compose
β
βΌ
Verify Running Containers
β
βΌ
Deployment Successful
- Checkout source code from GitHub.
- Build the Docker image.
- Start or recreate the application containers.
- Start the MySQL database.
- Verify the deployment.
- Display the running containers.
- Report pipeline success or failure.
The deployment uses commands similar to:
docker build ...and:
docker compose -f docker/docker-compose.yml up -d --buildThe pipeline then verifies the running services:
docker psA successful deployment should show:
two-tier-flask-app
mysql
GitHub Actions is used for continuous integration.
The CI workflow performs tasks such as:
- Checking out the repository.
- Installing Python.
- Installing application dependencies.
- Validating the application.
- Running CI checks.
Push Code
β
βΌ
GitHub Repository
β
βΌ
GitHub Actions
β
βββ Checkout Repository
β
βββ Setup Python
β
βββ Install Dependencies
β
βββ Validate Application
One of the real deployment issues encountered during this project was Docker permission management.
Jenkins executes pipeline commands using the jenkins Linux user.
Docker commands require access to:
/var/run/docker.sock
If the Jenkins user does not have permission, the pipeline may fail with:
permission denied while trying to connect to the Docker daemon socket
Add Jenkins to the Docker group:
sudo usermod -aG docker jenkinsRestart Jenkins:
sudo systemctl restart jenkinsVerify:
sudo -u jenkins docker psThis allows Jenkins to execute Docker commands inside the pipeline.
This project included several practical troubleshooting scenarios.
Error:
docker: not found
Possible causes:
- Docker was not installed.
- Docker was not available in the PATH.
- Jenkins was executing on a different machine.
Verification:
which docker
docker --version
sudo -u jenkins which dockerError:
permission denied while trying to connect to the Docker API
Solution:
sudo usermod -aG docker jenkins
sudo systemctl restart jenkins
sudo -u jenkins docker psThe Docker CLI may work for the Ubuntu user but fail for Jenkins.
The important distinction is:
ubuntu user β jenkins user
The Jenkins pipeline executes as:
jenkins
Therefore, Docker access must be tested specifically as Jenkins:
sudo -u jenkins docker psDocker Compose V2 can be verified with:
docker compose versionOn Ubuntu systems where the package is available:
sudo apt update
sudo apt install -y docker-compose-v2Verify:
docker compose versionJenkins can fail if the configured Jenkinsfile path does not match the repository structure.
For example:
Jenkinsfile
is different from:
jenkins/Jenkinsfile
The Jenkins job configuration and repository structure must match exactly.
SSH private keys must have appropriate permissions.
Example:
chmod 400 key.pemIf the key is not found, verify the actual file path:
ls -la ~/.sshOn Windows with WSL, Windows paths can be accessed through:
/mnt/c/
or:
/mnt/e/
depending on the drive location.
When local and remote branches contain different histories, Git may reject a push.
The repository should be checked carefully before synchronizing:
git status
git branch
git remote -vThe following screenshots document the actual project deployment and CI/CD workflow.
Shows the AWS EC2 instance used to host the Ubuntu server and deployment environment.
Shows the security group configuration used to control inbound access to the EC2 instance.
Shows the Jenkins interface used to manage and execute the pipeline.
Shows the Docker image and running containers created during deployment.
Shows a Failed Jenkins build.
Shows the Docker Compose deployment process executed by Jenkins.
Shows Jenkins checking out the source code from GitHub.
Shows the final successful Jenkins build result.
Shows the deployed application running successfully.
Shows the flow from source code to automated validation, build, deployment, and the running application.
Create the Flask application and define the required endpoints.
app/
βββ app.py
βββ requirements.txt
Define the environment required to run the Flask application.
docker/Dockerfile
Define the Flask and MySQL services.
docker/docker-compose.yml
Build and run the application:
docker compose -f docker/docker-compose.yml up -d --buildVerify:
docker psDefine the CI/CD pipeline stages.
The pipeline performs:
Checkout
β
Build
β
Deploy
β
Verify
git add .
git commit -m "Add two-tier Flask CI/CD project"
git push origin mainConfigure Jenkins to use the GitHub repository and the Jenkinsfile.
Install and configure:
- Docker
- Docker Compose
- Java
- Jenkins
sudo usermod -aG docker jenkins
sudo systemctl restart jenkinsJenkins:
- Checks out the repository.
- Builds the Docker image.
- Starts the MySQL container.
- Starts the Flask container.
- Verifies the deployment.
docker psThen test:
curl http://localhost:5000/healthA successful deployment should show both application services running.
CONTAINER ID IMAGE STATUS PORTS
xxxxxxxx docker-flask Up 0.0.0.0:5000->5000/tcp
xxxxxxxx mysql:8.0 Up 0.0.0.0:3306->3306/tcp
The Jenkins pipeline should finish with:
Finished: SUCCESS
The application should then be accessible through:
http://<EC2-PUBLIC-IP>:5000
The final deployment successfully demonstrated:
GitHub Repository
β
βΌ
Jenkins Pipeline
β
βΌ
Docker Image Build
β
βΌ
Docker Compose Deployment
β
βββ Flask Application Container
β
βββ MySQL Database Container
β
βΌ
Application Running on AWS EC2
- Jenkins pipelines execute commands as the Jenkins Linux user.
- Jenkins must have the correct permissions to access Docker.
- A running Jenkins service does not automatically mean the pipeline environment is correctly configured.
- Docker installation and Docker permissions are separate problems.
- The Docker daemon socket controls access to the Docker engine.
- Docker Compose simplifies multi-container deployments.
- Users, groups, permissions, and services are essential for DevOps troubleshooting.
- Restarting a service may be required after changing group membership.
- System logs are valuable when diagnosing service problems.
- Security groups control network access.
- SSH access requires correct private key permissions.
- EC2 provides the infrastructure, but the server still requires proper configuration.
- Branch synchronization is important.
- Repository structure must match CI/CD configuration.
- Jenkins can automatically retrieve source code from GitHub.
- Automation reduces manual deployment steps.
- Every stage of the pipeline should be observable.
- Deployment verification is just as important as deployment itself.
During development, the following issues were encountered and resolved:
- Jenkins GPG key and repository configuration issues.
- Java installation and version configuration.
- Jenkinsfile path mismatch.
- Docker command not found inside Jenkins.
- Docker socket permission denied.
- Jenkins user missing Docker group access.
- Docker Compose installation issues.
- EC2 SSH private key path problems.
- SSH private key permission problems.
- GitHub SSH and HTTPS authentication differences.
- Git branch synchronization issues.
- Docker Compose deployment troubleshooting.
These challenges were valuable because they represented real DevOps problems rather than only theoretical examples.
Planned improvements include:
- Kubernetes deployment.
- Terraform infrastructure as code.
- Prometheus monitoring.
- Grafana dashboards.
- NGINX reverse proxy.
- HTTPS with Let's Encrypt.
- Container image scanning.
- Docker image publishing to a registry.
- Blue-green deployment.
- Rolling deployment strategy.
- Automated integration tests.
- Centralized logging.
- AWS VPC architecture.
- Load balancing.
- Auto Scaling.
- Secrets management.
The project can be extended into:
ββββββββββββββββ
β Developer β
ββββββββ¬ββββββββ
β
βΌ
ββββββββββββββββ
β GitHub β
ββββββββ¬ββββββββ
β
βββββββββββ΄ββββββββββ
βΌ βΌ
GitHub Actions Jenkins
Continuous CI/CD
Integration β
βΌ
Docker Registry
β
βΌ
AWS ECR
β
βΌ
Kubernetes
β
ββββββββββββββββ΄βββββββββββββββ
βΌ βΌ
Flask Application MySQL
A two-tier application separates the application layer from the database layer.
In this project:
Flask Application β MySQL Database
Docker packages the application and its dependencies into a consistent container environment.
Docker Compose allows multiple services to be defined and managed together.
Jenkins automates the CI/CD workflow, including source checkout, Docker image building, and deployment.
The Jenkins process executes Docker commands. Therefore, the jenkins user must be allowed to communicate with the Docker daemon.
The Jenkins user was not initially a member of the Docker group.
The solution was:
sudo usermod -aG docker jenkins
sudo systemctl restart jenkinsThe health endpoint provides a simple way to verify whether the application is running correctly.
GitHub Actions performs continuous integration checks when code changes are pushed.
EC2 provides a cloud-based Linux server on which the application, Docker, and Jenkins can run.
Contributions are welcome.
git clone https://github.com/revanth-bl/devops-two-tier-flask-cicd.gitgit checkout -b feature/your-featureImplement your improvement or fix.
git add .
git commit -m "Add your feature"git push origin feature/your-featureCreate a pull request with:
- A clear description.
- The reason for the change.
- Testing information.
- Screenshots where applicable.
This project is licensed under the terms of the MIT License.
See:
LICENSE
for more information.
Revanth B L
GitHub:
LinkedIn:
https://www.linkedin.com/in/revanth-b-l-05294a253/
If you found this project useful, consider giving the repository a β on GitHub.
This project demonstrates a complete practical DevOps workflow:
Developer
β
βΌ
Git
β
βΌ
GitHub
β
ββββββββββββββββΊ GitHub Actions
β β
β βΌ
β CI Validation
β
βΌ
Jenkins
β
βΌ
Docker Image Build
β
βΌ
Docker Compose
β
ββββββββββββββββΊ Flask Application
β
ββββββββββββββββΊ MySQL Database
β
βΌ
AWS EC2 Ubuntu
β
βΌ
Running Application
The project combines application development, containerization, CI/CD automation, Linux administration, cloud infrastructure, and real-world troubleshooting into one complete deployment workflow.









