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.shjboss-cliInfrastructure 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 ubunturouterosThis 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 namedprodebnf 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:
Build & push Docker image
Deploy image to EC2
GitHub Actions Workflow
name: Docker Image Deployment to EC2on:
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"yamlDeployment 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_numberbashThis ensures:
Zero-downtime replacement
Clean old containers
Versioned deployments
Triggering the Pipeline
Go to the Actions tab in GitHub
Select the workflow
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.