softwarerror

CI/CD — GitHub → Docker → AWS Lightsail

This page walks through the real CI/CD pipeline behind this site: code changes are pushed to GitHub, built into Docker images, and deployed to an AWS Lightsail instance using an automated GitHub Actions workflow.

Get the Code

Review the GitHub Actions workflow, Docker configuration, and deployment script that pulls the latest image and restarts the container on Lightsail.

Review the workflow

Step 1 — Add a YAML workflow file

Create a workflow under .github/workflows/ in your repository (for example, deploy.yml). This file defines what happens when you push to the main branch.

.github/workflows/deploy.yml

Step 2 — Define when the pipeline runs

The workflow runs on pushes to main, so every commit to the primary branch automatically triggers a deployment.

on:
  push:
    branches: [main]

Step 3 — Define the deploy job and check out the code

The deploy job runs on ubuntu-latest and starts by checking out the repository so the workflow can build the Docker image from the latest code.

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v3

Step 4 — Set up Docker Buildx and log in to Docker Hub

The next steps prepare the build environment using docker/setup-buildx-action and authenticate to Docker Hub using credentials stored as GitHub Secrets.

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

      - name: Log in to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}

Using secrets keeps credentials out of the repository while still allowing the workflow to push images to your Docker Hub account.

Step 5 — Build and push the Docker image

The workflow then builds a Linux/amd64 Docker image for the app and pushes it to Docker Hub under mmfay3/softwarerror:latest.

      - name: Build and Push Docker Image (linux/amd64)
        run: |
          docker buildx build \
            --platform linux/amd64 \
            -t mmfay3/softwarerror:latest \
            --push .

Using buildx makes it easy to target the same architecture as your Lightsail instance and push in a single step.

Step 6 — SSH into AWS Lightsail and deploy the container

Finally, the workflow uses appleboy/ssh-action to SSH into the Lightsail instance, pull the latest image, stop/remove the old container, and run the new one with the correct environment variables.

      - name: SSH into Lightsail and deploy
        uses: appleboy/ssh-action@v1.0.0
        with:
          host: ${{ secrets.HOST }}
          username: ubuntu
          key: ${{ secrets.HOST_SSH_KEY }}
          script: |
            docker pull mmfay3/softwarerror:latest
            docker stop softwarerror || true
            docker rm softwarerror || true
            docker run -d --restart always --name softwarerror \
              -p 3000:3000 \
              -e EMAIL_HOST=${{ secrets.EMAIL_HOST }} \
              -e EMAIL_PORT=${{ secrets.EMAIL_PORT }} \
              -e EMAIL_USER=${{ secrets.EMAIL_USER }} \
              -e EMAIL_PASS=${{ secrets.EMAIL_PASS }} \
              mmfay3/softwarerror:latest

This keeps deployment repeatable: every push to main results in the Lightsail instance running the latest version of the app with the same port mapping and environment settings.

Step 7 — Health checks & next steps

Right now, verification is manual (hitting the site and confirming it's responding). This could be extended with an automated health check step in the workflow or external monitoring/alerting.

  • Add a simple curl-based health check after deployment.
  • Hook uptime monitoring to the public URL.
  • Extend logging/metrics for deeper visibility.