Stage 12: Microservices, lesson 7 of 7

CI/CD with GitHub Actions

Intermediate3 min readall versions
Explain it forThe essentials plus production detail and pitfalls.

Continuous integration builds and tests every push and pull request, so problems surface in minutes instead of on release day. Continuous delivery packages the tested build and releases it automatically, or with one approval.

A typical pipeline for a Spring Boot service:

  1. Check out the code and set up Java.
  2. Build and run the tests (./mvnw verify), with Testcontainers for integration tests.
  3. Run static analysis and dependency vulnerability checks.
  4. Build a Docker image tagged with the commit SHA and push it to a registry.
  5. Deploy to Railway, a VPS or Kubernetes, then run a quick smoke test.

Keep secrets such as registry tokens in the CI system's secret store, never in the repository. Protect the main branch so merges need a passing pipeline and a review.

Example

YAML
# .github/workflows/ci.yml
name: ci
on:
  push:
    branches: [main]
  pull_request:

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '25'
          cache: maven
      - run: ./mvnw -B verify                         # compile, unit and integration tests
      - name: Build and push the image
        if: github.ref == 'refs/heads/main'
        run: |
          echo "${{ secrets.GHCR_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
          docker push ghcr.io/${{ github.repository }}:${{ github.sha }}

Common mistake

Deploying images tagged latest. You can't tell which code is running or roll back reliably; tag with the commit SHA.

Under the hood

Fast pipelines get used: cache dependencies, run unit tests first and slower integration tests in parallel, and fail early. Tag images with the commit SHA instead of latest, so you always know what's running and can roll back instantly. Database migrations (Flyway, Liquibase) should run during deployment and stay backward compatible for one release, so old and new versions can run side by side.

Check yourself

What should a production Docker image usually be tagged with?

How this connects

Where this leads

You've reached the end of this thread. Try a learning path for what's next.

Part of Job-ready backend developer, Microservices and production.

Was this lesson helpful?

Finished reading? Mark it complete to track your progress.