π Day 40: CI/CD with Flask + Docker + GitHub Actions | #100DaysOfDevOps


Welcome to Day 40 of my #100DaysOfDevOps! Today, I built a CI/CD pipeline for a Python Flask application, containerized it with Docker, and automated the build and deployment process using GitHub Actions. Letβs dive into this exciting journey! ππ³π₯ #DevOps #CI/CD
π§ Project Overview: Flask App with Docker CI/CD
The goal of this project is to create a simple Flask application, containerize it using Docker, and set up a GitHub Actions workflow to automatically build and push the Docker image to Docker Hub whenever code is pushed to the main
branch. #Flask #Docker #GitHubActions
π Project Structure
Hereβs the structure of the project:
flask-ci-cd-demo/
βββ app/
β βββ app.py # Flask app logic
β βββ requirements.txt # Python dependencies
βββ Dockerfile # Docker configuration
βββ .dockerignore # Ignore unnecessary files in Docker context
βββ .gitignore # Ignore local dev/config files
βββ .github/
β βββ workflows/
β βββ ci-cd.yml # GitHub Actions workflow file
βββ README.md # Project details
π οΈ Step 1: Set Up the Project
Create the project directory and initialize a Python environment:
mkdir flask-ci-cd-demo && cd flask-ci-cd-demo
mkdir app
π Step 2: Create the Flask Application
app/
app.py
Create a simple Flask app:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
return "π Hello from Flask CI/CD Pipeline!"
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
This code sets up a Flask server that responds with a message at http://localhost:5000
. #Python #Flask
app/requirements.txt
List the dependencies:
flask
π³ Step 3: Create the Dockerfile
Create a Dockerfile
to containerize the Flask app:
# Use official Python image
FROM python:3.10-slim
# Set work directory
WORKDIR /app
# Copy files and install dependencies
COPY app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy source code
COPY app/ .
# Run the Flask app
CMD ["python", "app.py"]
Why this structure?
Using
python:3.10-slim
keeps the image lightweight.Copying
requirements.txt
first allows Docker to cache the dependency layer, speeding up builds if onlyapp.py
changes. #Docker #Optimization
π¦ Step 4: Optimize with .dockerignore
and .gitignore
.dockerignore
Prevent unnecessary files from being included in the Docker image:
# Git and version control
.git
.gitignore
# Python bytecode
__pycache__/
*.pyc
# Virtual environments
env/
venv/
ENV/
# Editor settings
.vscode/
.idea/
# System files
.DS_Store
# GitHub Actions config
.github/
.gitignore
Ignore local development and temporary files:
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
env/
venv/
ENV/
*.env
# Editor settings
.vscode/
.idea/
# System files
.DS_Store
Thumbs.db
# Logs
*.log
# Docker artifacts
*.tar
These files ensure clean builds and repositories by excluding irrelevant files. #BestPractices
βοΈ Step 5: Set Up GitHub Actions Workflow
Create the workflow file at .github/workflows/ci-cd.yml
:
name: Flask CI/CD
on:
push:
branches:
- main
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: π₯ Checkout code
uses: actions/checkout@v4
- name: π Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: π¦ Install dependencies
run: pip install -r app/requirements.txt
- name: β
Run tests
run: echo "No tests yet"
- name: π³ Build Docker image
run: docker build -t ritesh355/flask-ci-cd-demo .
- name: π€ Push to Docker Hub
run: |
echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
docker tag ritesh355/flask-ci-cd-demo ritesh355/flask-ci-cd-demo:latest
docker push ritesh355/flask-ci-cd-demo:latest
Key Points:
Triggers on pushes to the
main
branch.Sets up Python 3.10 and installs dependencies.
Builds the Docker image and pushes it to Docker Hub.
Uses GitHub Secrets for secure Docker Hub authentication. #GitHubActions #Automation
π Step 6: Configure Docker Hub Secrets
To enable Docker Hub pushes, add these secrets to your GitHub repository:
DOCKER_USERNAME
: Your Docker Hub username (ritesh355
).DOCKER_PASSWORD
: Your Docker Hub access token (not your password).
Add them via:
Go to GitHub Repo β Settings β Secrets and variables β Actions.
Click New repository secret and add
DOCKER_USERNAME
andDOCKER_PASSWORD
. #Security #DockerHub
π Step 7: Push to GitHub
Push your project to GitHub:
git init
git remote add origin https://github.com/ritesh355/flask-ci-cd-demo.git
git add .
git commit -m "Add Flask CI/CD demo with Docker and GitHub Actions"
git push -u origin main
β Step 8: Verify the CI/CD Pipeline
Go to your GitHub repo β Actions tab.
Watch the Flask CI/CD workflow run. It will:
Install Python dependencies.
Build the Docker image.
Push the image to
ritesh355/flask-ci-cd-demo:latest
on Docker Hub.
Visit Docker Hub to confirm the image is published.
π§ͺ Step 9: Test the Docker Image Locally
To verify the image, run it locally (if you have Docker installed):
docker pull ritesh355/flask-ci-cd-demo:latest
docker run -p 5000:5000 ritesh355/flask-ci-cd-demo
Open your browser and visit http://localhost:5000. You should see:
"π Hello from Flask CI/CD Pipeline!"
π Learnings from Day 13
Built an end-to-end CI/CD pipeline for a Flask app using GitHub Actions.
Used
.dockerignore
and.gitignore
to optimize Docker builds and keep the repository clean.Secured Docker Hub credentials with GitHub Secrets.
Automated Docker image builds and pushes to Docker Hub. #Learning #DevOpsJourney
π Benefits
Automation: Push code, and GitHub Actions handles the rest.
Efficiency:
.dockerignore
reduces image size and build time.Security: Sensitive credentials are safely stored in GitHub Secrets.
Scalability: Ready to extend with tests or deployment steps.
π Useful Links
π» GitHub Repo
π¦ Docker Hub
βοΈ My Blog
#Portfolio #Networking
π Congratulations!
Youβve successfully built a CI/CD pipeline for a Flask app with Docker and GitHub Actions! Keep rocking your #100DaysOfDevOps journey! πͺ #KeepLearning
Need help? Drop a comment or question, and Iβll guide you through any issues!
Subscribe to my newsletter
Read articles from Ritesh Singh directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Ritesh Singh
Ritesh Singh
Hi, Iβm Ritesh π Iβm on a mission to become a DevOps Engineer β and Iβm learning in public every single day.With a full-time commitment of 8β10 hours daily, Iβm building skills in: β Linuxβ Git & GitHubβ Docker & Kubernetesβ AWS EC2, S3β Jenkins, GitHub Actionsβ Terraform, Prometheus, Grafana I post daily blogs on Hashnode, push projects to GitHub, and stay active on LinkedIn and Twitter/X. Letβs connect, collaborate, and grow together π #100DaysOfDevOps #LearningInPublic #DevOps