11 min read

Deploy Applications with GitHub Actions: A Beginner’s CI/CD Guide Using AWS EC2

Nasrul Hasan
Nasrul Hasan
Nasrul Hasan
Cover Image for Deploy Applications with GitHub Actions: A Beginner’s CI/CD Guide Using AWS EC2

Deploy Applications with GitHub Actions: A Beginner’s CI/CD Guide

GitHub Actions is one of the most powerful and developer-friendly CI/CD tools available today. It allows you to automate workflows directly from your GitHub repository using simple YAML files.

In this guide, you’ll learn how to build a real-world CI/CD pipeline using GitHub Actions that:

  • Builds a Docker image

  • Pushes it to Docker Hub

  • Deploys it to an AWS EC2 instance automatically

👉 The application used is a simple Flask app, but the focus is on GitHub Actions, and the same pipeline works for any containerized application.


What Is GitHub Actions?

GitHub Actions is a CI/CD and automation platform built directly into GitHub.

With GitHub Actions, you can:

  • Build and deploy applications

  • Run tests automatically

  • Automate repository tasks

  • Integrate with cloud providers

Why teams love GitHub Actions:

  • Native GitHub integration

  • Huge marketplace of prebuilt actions

  • Simple and readable YAML syntax

  • Easy secrets and environment management


Prerequisites

Before starting, make sure you have:

  • An AWS EC2 instance

  • A GitHub account

  • A Docker Hub account

  • Docker installed on the EC2 instance

  • An IDE (VS Code recommended)

  • Terraform (optional, for infrastructure provisioning)


Project Structure

GitHub Actions workflows must be placed inside the .github/workflows directory.

project-root/
├── .github/
│   └── workflows/
│       └── ec2-deploy.yaml
├── server/
│   └── Dockerfile
├── scripts/
│   ├── deploy.sh
│   └── env.sh
jboss-cli

Infrastructure Preparation (High-Level)

You can:

  • Provision EC2 manually, or

  • Use Terraform (recommended for automation)

Docker Installation Script (env.sh)

#
!/bin/bash
sudo apt-get update
sudo apt-get install docker.io -y
sudo systemctl start docker && sudo systemctl enable docker
sudo usermod -aG docker ubuntu
routeros

This script installs Docker on the EC2 instance.


GitHub Secrets and Variables Setup

GitHub Actions relies heavily on secrets and environments for security.

Repository Secrets

  • EC2_SSH_KEY → Private SSH key for EC2

  • DOCKERHUB_USERNAME

  • DOCKERHUB_PASSWORD

Environment

Create an environment named
prod
ebnf
and attach Docker Hub secrets.

Repository Variables

  • EC2_SERVER_IP → Public IP of EC2 instance

This ensures:

  • No secrets are hardcoded

  • Environment-specific deployments are clean


GitHub Actions CI/CD Workflow

Create the workflow file:

.github/workflows/ec2-deploy.yaml

Workflow Overview

This pipeline has two jobs:

  1. Build & push Docker image

  2. Deploy image to EC2


GitHub Actions Workflow

name: Docker Image Deployment to EC2
on:
  workflow_dispatch:
jobs:
  build-and-push:
    name: Build and Push Docker Image
    runs-on: ubuntu-latest
    environment: prod
    env:
      user: ${{ secrets.DOCKERHUB_USERNAME }}
      pass: ${{ secrets.DOCKERHUB_PASSWORD }}
    steps:
      - uses: actions/checkout@v2
      - name: Build Docker Image
        run: cd server && docker build -t flask-simple-app .
      - name: Tag Docker Image
        run: docker tag flask-simple-app $user/flask-simple-app:${{ github.run_number }}
      - name: Docker Login
        run: docker login -u $user -p $pass
      - name: Push Image
        run: docker push $user/flask-simple-app:${{ github.run_number }}
  deploy-to-ec2:
    name: Deploy to EC2
    runs-on: ubuntu-latest
    needs: build-and-push
    environment: prod
    env:
      sshKey: ${{ secrets.EC2_SSH_KEY }}
      serverIp: ${{ vars.EC2_SERVER_IP }}
      user: ${{ secrets.DOCKERHUB_USERNAME }}
    steps:
      - uses: actions/checkout@v2
      - name: Copy deployment script
        run: |
          mkdir -p ~/.ssh
          echo "$sshKey" > ~/.ssh/id_rsa
          chmod 600 ~/.ssh/id_rsa
          scp -o StrictHostKeyChecking=no ./scripts/deploy.sh ubuntu@$serverIp:/home/ubuntu/deploy.sh
      - name: Execute deployment
        run: |
          ssh -o StrictHostKeyChecking=no ubuntu@$serverIp \
          "bash /home/ubuntu/deploy.sh ${{ github.run_number }} $user"
yaml

Deployment Script (deploy.sh)

This script runs on the EC2 instance, not in GitHub Actions.

#!/bin/bash
existing_container=$(docker ps -q -f label=flask-app)
if [ -n "$existing_container" ]; then
  docker stop $existing_container
fi
docker image prune -a -f
run_number="$1"
docker_user="$2"
docker run -d -p 3000:3000 \
  -l flask-app \
  $docker_user/flask-simple-app:$run_number
bash

This ensures:

  • Zero-downtime replacement

  • Clean old containers

  • Versioned deployments


Triggering the Pipeline

  1. Go to the Actions tab in GitHub

  2. Select the workflow

  3. Click Run workflow

Your application is now automatically built and deployed 🚀


Why This GitHub Actions Setup Is Powerful

  • Fully automated CI/CD

  • Secure secrets management

  • Environment-based deployments

  • Dockerized and cloud-agnostic

  • Easily extendable to Kubernetes, ECS, or EKS


Conclusion

In this guide, you learned how to:

  • Use GitHub Actions as a CI/CD tool

  • Securely manage secrets and environments

  • Build and push Docker images

  • Deploy applications to EC2 automatically

Although we used Flask as an example, this pipeline works for any Dockerized application.