DEV Community

Cover image for Automating Flask Deployment with GitHub Actions and Docker
David Oyewole
David Oyewole

Posted on • Edited on

Automating Flask Deployment with GitHub Actions and Docker

Introduction

Continuous Integration and Continuous Deployment (CI/CD) are essential for modern software development. In this tutorial, I will walk you through setting up a CI/CD pipeline for a Flask application using GitHub Actions and Docker. By the end of this guide, you will have an automated workflow that builds, tests, and pushes a Docker image to Docker Hub.

⚙️ Prerequisites

  • Python 3.x
  • pip
  • Docker
  • GitHub Repository
  • Docker Hub Account
  • AWS EC2 Instance with Docker installed
  • SSH Access to the EC2 instance

Step 1: Setting Up the Flask Application

Let's start by creating a simple Flask application.

  1. Create a project directory:

    mkdir flask-cicd && cd flask-cicd
    
  2. Create a virtual environment and activate it:

    python3 -m venv venv
    source venv/bin/activate  # On macOS/Linux
    venv\Scripts\activate  # On Windows
    
  3. Install Flask:

    pip install flask
    
  4. Create an app.py file:

    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def home():
        return "Hello, Flask CI/CD!"
    
    if __name__ == '__main__':
        app.run(host='0.0.0.0', port=5000)
    
  5. Create a requirements.txt file:

    flask
    

Step 2: Dockerizing the Flask Application

Create a Dockerfile in the project directory:

# Use official Python image
FROM python:3.10

WORKDIR /app

COPY requirements.txt requirements.txt
RUN pip install --no-cache-dir -r requirements.txt

COPY . .
EXPOSE 5000
CMD ["python", "app.py"]
Enter fullscreen mode Exit fullscreen mode

To test the Docker container locally, build and run it:

docker build -t flask-cicd .
docker run -p 5000:5000 flask-cicd
Enter fullscreen mode Exit fullscreen mode

Step 3: Setting Up GitHub Actions for CI/CD

Now, we will create a GitHub Actions workflow to automate building and pushing our Docker image to Docker Hub.

🔄 CI/CD Pipeline (GitHub Actions)
✅ CI — Continuous Integration
GitHub Actions automatically runs the following on each push to main:

Clone repository

Set up Python and install dependencies

Run unit tests

Build Docker image

Push image to Docker Hub

🚀 CD — Continuous Deployment to EC2
After a successful Docker push, a second GitHub Actions job connects to your EC2 server via SSH and:

Logs into Docker (if private image)

Pulls the latest image from Docker Hub

Stops and removes the old container

Runs the updated container

This enables automatic deployment of your latest code to a live server!

  1. Inside your project, create .github/workflows/main.yml and add the following:
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python

name: Python application

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

permissions:
  contents: read

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v4
    - name: Set up Python 3.10
      uses: actions/setup-python@v3
      with:
        python-version: "3.10"
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install flake8 pytest
        if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
    - name: Lint with flake8
      run: |
        # stop the build if there are Python syntax errors or undefined names
        flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
        # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
        flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
    - name: Log in to Docker Hub
      uses: docker/login-action@v3
      with:
        username: ${{ secrets.DOCKER_USERNAME }}
        password: ${{ secrets.DOCKER_PASSWORD }}

    - name: Set up Docker Buildx
      uses: docker/setup-buildx-action@v3

    - name: Build and Push Docker Image
      uses: docker/build-push-action@v5
      with:
        context: .
        file: Dockerfile  # Ensure this path is correct
        push: true
        tags: ${{ secrets.DOCKER_USERNAME }}/practice:latest

  deploy:
    runs-on: ubuntu-latest
    steps:
      - name: Deploy on EC2 via SSH
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.EC2_HOST }}
          username: ${{ secrets.EC2_USER }}
          key: ${{ secrets.EC2_SSH_KEY }}
          script: |
            docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
            docker pull ${{ secrets.DOCKER_USERNAME}}/practice:latest

            docker stop flask-app || true
            docker rm flask-app || true

            docker run -d \
              --name flask-app \
              -p 5000:5000 \
              ${{ secrets.DOCKER_USERNAME}}/practice:latest


Enter fullscreen mode Exit fullscreen mode

Once pushed, GitHub Actions will automatically run the workflow. You can check the status under the Actions tab in your repository.

🔐 GitHub Secrets Configuration

Remember to add the appropriate secrets to your github secret, go to Actions>secrets and add the following with correct values

Secret Name Purpose
DOCKER_USERNAME Docker Hub username
DOCKER_PASSWORD Docker Hub password/token
EC2_HOST Public IP or DNS of your EC2
EC2_USER SSH username (e.g., ubuntu)
EC2_SSH_KEY Private SSH key for EC2 access

Conclusion

Congratulations! 🎉 You have successfully set up a CI/CD pipeline for your Flask application using GitHub Actions and Docker. Now, every time you push code, your application will be tested, built, and deployed automatically.

Feel free to share your thoughts and improvements in the comments below! 🚀

Top comments (0)