AWSCICDFastAPIGithub Actions

    CI/CD with GitHub Actions + FastAPI + AWS EC2 (Complete Beginner Guide)

    In this guide, we'll build a complete CI/CD pipeline that automatically tests and deploys a FastAPI application to an AWS EC2 instance whenever code is merged into the main branch....

    Jul 31, 2026
    5 min read
    57 views
    CI/CD with GitHub Actions + FastAPI + AWS EC2 (Complete Beginner Guide)

    In this guide, we'll build a complete CI/CD pipeline that automatically tests and deploys a FastAPI application to an AWS EC2 instance whenever code is merged into the main branch.


    Table of Contents

    1. Introduction
    2. What is CI/CD?
    3. Architecture
    4. Prerequisites
    5. Step 1 -- Create an EC2 Instance
    6. Step 2 -- Allocate an Elastic IP
    7. Step 3 -- Connect to EC2 with SSH
    8. Step 4 -- Prepare the Server
    9. Step 5 -- Create a systemd Service
    10. Step 6 -- Configure GitHub Secrets
    11. Step 7 -- Generate an SSH Key for GitHub Actions
    12. Step 8 -- Build the CI Workflow
    13. Step 9 -- Build the CD Workflow
    14. Deployment Flow
    15. Troubleshooting
    16. Key Takeaways

    Introduction

    Deploying manually after every code change quickly becomes repetitive.

    Instead of logging into the server every time and running commands like:

    bash
    git pull
    uv sync
    sudo systemctl restart learn-ci-cd

    we can automate the entire process.

    After completing this setup:

    • Every Pull Request is automatically tested.
    • Every merge to main is automatically deployed.
    • The FastAPI service restarts automatically.
    • A health check verifies that deployment succeeded.

    What is CI/CD?

    Continuous Integration (CI)

    CI validates code quality before it reaches production.

    Our CI pipeline performs:

    • Ruff linting
    • Ruff formatting check
    • MyPy type checking
    • Pytest execution

    If any step fails, deployment never starts.


    Continuous Deployment (CD)

    Once code is merged into main:

    1. GitHub Actions connects to EC2 through SSH.
    2. Downloads the latest code.
    3. Installs production dependencies.
    4. Restarts the FastAPI service.
    5. Performs a health check.

    No manual deployment is required.


    Architecture

    text
    Developer
          │
    Feature Branch
          │
    Pull Request
          │
    GitHub Actions (CI)
          │
    CI Passed
          │
    Merge into main
          │
    GitHub Actions (CD)
          │
    SSH into EC2
          │
    git fetch
    git reset --hard origin/main
    uv sync --frozen --no-dev
    systemctl restart learn-ci-cd
    Health Check
          │
    Production Updated

    Prerequisites

    • AWS Account
    • GitHub Repository
    • FastAPI Project
    • Ubuntu EC2 Instance
    • GitHub Actions enabled

    Step 1 -- Create an EC2 Instance

    Launch an Ubuntu EC2 instance.

    Recommended:

    • Ubuntu 24.04 LTS
    • t2.micro (or t3.micro depending on availability)
    • 20 GB EBS
    • Security Group:
      • SSH (22)
      • HTTP (80)
      • HTTPS (443)
      • Custom TCP 8000 (for testing)

    Create an AWS Key Pair (.pem) to access the server.


    Step 2 -- Allocate an Elastic IP

    A normal public IP changes after stopping/starting an EC2 instance.

    An Elastic IP provides a permanent public IP.

    Example GitHub Secret:

    text
    EC2_HOST=100.48.xxx.xxx

    Step 3 -- Connect to EC2

    bash
    ssh -i fastapi-key.pem ubuntu@YOUR_ELASTIC_IP

    Install tools:

    bash
    sudo apt update
    sudo apt install git -y
    
    curl -LsSf https://astral.sh/uv/install.sh | sh
    source ~/.bashrc

    Step 4 -- Prepare the Server

    Clone your repository:

    bash
    git clone <repository-url>
    cd learn_ci_cd

    Create the environment file:

    bash
    cat > .env <<EOF
    DATABASE_URL=...
    SECRET_KEY=...
    EOF

    Install dependencies:

    bash
    uv sync

    Step 5 -- Configure systemd

    Create:

    text
    /etc/systemd/system/learn-ci-cd.service

    Example:

    ini
    [Unit]
    Description=Learn CI/CD FastAPI
    After=network.target
    
    [Service]
    User=ubuntu
    WorkingDirectory=/home/ubuntu/learn_ci_cd
    EnvironmentFile=/home/ubuntu/learn_ci_cd/.env
    ExecStart=/home/ubuntu/.local/bin/uv run uvicorn main:app --host 0.0.0.0 --port 8000
    Restart=always
    
    [Install]
    WantedBy=multi-user.target

    Enable it:

    bash
    sudo systemctl daemon-reload
    sudo systemctl enable learn-ci-cd
    sudo systemctl start learn-ci-cd

    Step 6 -- Configure GitHub Secrets

    Repository → Settings → Secrets and variables → Actions

    Create:

    • EC2_HOST
    • EC2_USER
    • EC2_SSH_KEY

    Never commit private keys to Git.


    Step 7 -- Generate a Deployment SSH Key

    Generate:

    bash
    ssh-keygen -t ed25519 -C "github-actions"

    Files created:

    text
    id_ed25519
    id_ed25519.pub
    • Add id_ed25519.pub to:
    text
    ~/.ssh/authorized_keys
    • Store id_ed25519 in the GitHub Secret:
    text
    EC2_SSH_KEY

    This key is dedicated to GitHub Actions and is separate from the AWS .pem key.


    Step 8 -- CI Workflow

    Our CI workflow runs:

    • Ruff
    • Ruff Format
    • MyPy
    • Pytest

    Only if all checks pass does deployment become eligible.


    Step 9 -- CD Workflow

    The deployment workflow listens for the successful completion of the CI workflow on main.

    Deployment script:

    bash
    cd /home/ubuntu/learn_ci_cd
    
    git fetch origin
    git reset --hard origin/main
    
    uv sync --frozen --no-dev
    
    sudo systemctl restart learn-ci-cd
    
    curl -f http://localhost:8000/health

    Deployment Flow

    text
    Developer
        │
    Push Feature Branch
        │
    Create Pull Request
        │
    CI
     ├── Ruff
     ├── Ruff Format
     ├── MyPy
     └── Pytest
        │
    Merge to main
        │
    Deploy
        │
    SSH into EC2
        │
    git fetch
    git reset
    uv sync
    systemctl restart
    Health Check
        │
    Production Updated

    Troubleshooting

    myapp.service not found

    Cause:

    Service didn't exist.

    Solution:

    Create a dedicated systemd service (learn-ci-cd.service).


    status=203/EXEC

    Cause:

    Incorrect ExecStart path.

    Solution:

    Use the actual uv executable:

    text
    /home/ubuntu/.local/bin/uv

    Cannot access port 8000

    Open the EC2 Security Group inbound rule for port 8000.


    GitHub cannot SSH

    Verify:

    • Public key is in ~/.ssh/authorized_keys
    • Private key is stored in EC2_SSH_KEY

    Key Takeaways

    By the end of this project we learned:

    • CI vs CD
    • GitHub Actions
    • AWS EC2
    • Elastic IP
    • SSH authentication
    • GitHub Secrets
    • systemd
    • FastAPI deployment
    • Automated production deployment
    • Health checks
    • Basic production troubleshooting

    Conclusion

    With this setup, every merge into the main branch automatically:

    1. Validates the code.
    2. Deploys to EC2.
    3. Restarts the FastAPI service.
    4. Confirms the application is healthy.

    This forms a simple but production-oriented CI/CD pipeline suitable for personal projects, portfolios, and small production services.

    J
    Written by

    Jobi S S

    Portfolio

    admin

    Sharing technical insights, engineering concepts, and practical modern software development guides.

    Community Discussion

    Enjoyed this read? Show your support or share your thoughts.

    Comments (0)

    No comments yet. Be the first to comment!

    📬 Enjoyed this article?

    Get new posts on Django, FastAPI, and system design straight to your inbox. No spam — unsubscribe whenever you want.