How to Set Up a Powerful CI/CD Pipeline with GitHub Actions in 2025 (Step-by-Step Guide with AWS ECS Deployment)

In the fast-paced world of software development, automation is the key to delivering high-quality applications quickly and reliably. One of the most powerful and accessible tools for implementing CI/CD workflows is GitHub Actions. In this 2025 guide, we’ll walk through creating a CI/CD pipeline with GitHub Actions and show you how to take it a step further by automatically deploying Docker images to AWS ECS (Elastic Container Service) after pushing them to ECR (Elastic Container Registry).

Whether you’re deploying a personal project or managing a production-ready application in a corporate environment, this guide will help you streamline your DevOps workflow from end to end.


What Is GitHub Actions?

GitHub Actions is a built-in automation service in GitHub that lets you define custom workflows triggered by events in your repository. These workflows are written in YAML and can automate tasks such as building, testing, and deploying your applications.

Key Features in 2025:

  • Seamless integration with GitHub repositories
  • Extensive marketplace for reusable actions
  • Secure secrets management
  • OIDC support for short-lived cloud credentials
  • Support for all major languages and frameworks

πŸ”§ Prerequisites

Before you begin, for creating CI/CD Pipeline with GitHub Actions you’ll need:

  • A GitHub account
  • A repository to test (public or private)
  • Basic knowledge of Git and YAML
  • A deployment target

🌱 Personal Project : GitHub Pages or Docker Hub Deployment

Not every project needs the complexity of AWS. If you’re working on a personal or hobby project, you can still take full advantage of GitHub Actions for automated deployments.

βœ… Option 1: Deploy to GitHub Pages (Static Sites)

Perfect for frontend apps (React, Vue, etc.) or documentation sites (e.g., with MkDocs or Docusaurus).

Example:

name: Deploy to GitHub Pages

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Build Static Site
        run: |
          npm install
          npm run build
      - name: Deploy to GitHub Pages
        uses: peaceiris/actions-gh-pages@v3
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          publish_dir: ./build

βœ… Option 2: Push Docker Image to Docker Hub

If you prefer Docker Hub for simplicity:

name: Build and Push to Docker Hub

on:
  push:
    branches: [main]

jobs:
  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Log in to Docker Hub
        run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u "${{ secrets.DOCKER_USERNAME }}" --password-stdin
      - name: Build and Push
        run: |
          docker build -t yourusername/myapp:${{ github.sha }} .
          docker push yourusername/myapp:${{ github.sha }}

CI/CD Pipeline with GitHub Actions Workflow with AWS ECR and ECS (Corporate Environment)

While Docker Hub is great for public images, corporate environments often require secure, private registries. Amazon ECR offers private container image storage integrated with AWS IAM, and ECS provides scalable deployment options for microservices, either using EC2 instances or Fargate.

Using a CI/CD Pipeline with GitHub Actions, we can fully automate the entire development workflow:

  1. Build a Docker image
  2. Push it to Amazon ECR
  3. Update the ECS task definition
  4. Deploy the new task to an ECS service

Prerequisites

  • AWS account with ECR and ECS permissions
  • ECS cluster and service set up
  • GitHub repository for your project
  • GitHub Secrets or OIDC authentication configured

Project Example: Python API with ECS Deployment

Let’s use a simple Python FastAPI application as our example.

Project Structure

my-fastapi-app/
β”œβ”€β”€ app/
β”‚   └── main.py
β”œβ”€β”€ requirements.txt
β”œβ”€β”€ Dockerfile
β”œβ”€β”€ .github/
β”‚   └── workflows/
β”‚       └── ci-cd.yml
└── ecs-task-def.json

Dockerfile

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY ./app ./app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]

requirements.txt

fastapi
uvicorn

GitHub Actions Workflow (ci-cd.yml)

name: CI/CD to AWS ECS

on:
  push:
    branches: [ "main" ]

jobs:
  deploy:
    name: Build & Deploy to ECS
    runs-on: ubuntu-latest

    permissions:
      id-token: write
      contents: read

    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ${{ secrets.AWS_REGION }}

      - name: Login to Amazon ECR
        uses: aws-actions/amazon-ecr-login@v2

      - name: Build, Tag, and Push Image
        env:
          ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
          ECR_REPOSITORY: my-fastapi-repo
          IMAGE_TAG: ${{ github.sha }}
        run: |
          docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
          docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG

      - name: Render Task Definition
        id: task-def
        uses: aws-actions/amazon-ecs-render-task-definition@v1
        with:
          task-definition: ecs-task-def.json
          container-name: fastapi-container
          image: ${{ steps.login-ecr.outputs.registry }}/my-fastapi-repo:${{ github.sha }}

      - name: Deploy to ECS
        uses: aws-actions/amazon-ecs-deploy-task-definition@v1
        with:
          cluster: my-cluster
          service: my-fastapi-service
          task-definition: ${{ steps.task-def.outputs.task-definition }}

ecs-task-def.json (Demo)

{
  "family": "fastapi-task",
  "networkMode": "awsvpc",
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "containerDefinitions": [
    {
      "name": "fastapi-container",
      "image": "PLACEHOLDER_IMAGE",
      "essential": true,
      "portMappings": [
        {
          "containerPort": 80,
          "hostPort": 80,
          "protocol": "tcp"
        }
      ]
    }
  ]
}

πŸ”— You can find the complete CI/CD workflow code and project folder structure in the linked GitHub repository.

πŸ“Œ For more on DevOps workflows, check out AWS Lambda cost-saving tips and the DevOps Maturity Model guide.

Security Considerations

  • Use GitHub Secrets to store AWS credentials if not using OIDC
  • Restrict repo write access to trusted team members
  • Enable branch protection rules to prevent workflow tampering
  • Prefer OIDC authentication to avoid long-lived secrets
  • Rotate credentials periodically

FAQ on CI/CD Pipeline with GitHub Actions

❓ Are GitHub Secrets Secure?

Yes β€” they are encrypted and masked during runtime. However, any user with write access to the repo can modify workflows and misuse secrets, so restrict permissions tightly.

❓ Can I Use This Setup with OIDC Instead of Secrets?

Absolutely. GitHub Actions now supports OIDC (OpenID Connect), which lets you assume roles in AWS using temporary credentials β€” much safer for enterprise use.

❓ What if My Deployment Fails?

Check the Actions log. Common issues include:

  • Incorrect AWS credentials or permissions
  • Malformed ecs-task-def.json
  • Incorrect image tag
  • ECS cluster or service name mismatch

❓ What Type of ECS Launch Mode Is This?

The above example uses Fargate, but it also works with EC2-backed ECS β€” just modify requiresCompatibilities and add executionRoleArn/taskRoleArn if needed.

❓ Can I Roll Back Automatically?

Yes, but that’s an advanced topic. You’d need to integrate CloudWatch alarms and ECS deployment strategies or use a blue/green deployment tool like AWS CodeDeploy.


Bonus Brainstorm Questions (and Answers)

❓ Can I deploy multiple containers in one task?

Yes β€” define multiple container definitions in your ecs-task-def.json.

❓ Can I deploy different branches to different environments?

Yes β€” use GitHub Environments with branch-based triggers (e.g., main for production, dev for staging).

❓ How do I test workflows before pushing?

Use the CLI tool act to simulate GitHub Actions locally.

❓ Can I trigger rollback from within GitHub Actions?

Yes β€” combine a health check post-deploy and, on failure, re-deploy the last stable task definition.

❓ How do I manage different versions of the image?

Use semantic versioning in your tags, and pass the tag via workflow inputs or GitHub releases.


Final Thoughts

With this setup, you now have a complete CI/CD pipeline with GitHub Actions that not only builds and tests your app but also securely pushes Docker images to Amazon ECR and deploys them to Amazon ECS β€” fully automated.

This modern, cloud-native workflow is ideal for startups and enterprises alike, enabling fast, secure, and scalable deployments.

Leave a Comment

Your email address will not be published. Required fields are marked *