</>

Technology

GitHub Actions

Difficulty

Advanced

Interview Question

How do you build and push a Docker image using GitHub Actions?

Answer

Build and Push Docker Image with GitHub Actions

Push to Docker Hub

YAML
# .github/workflows/docker.yml
name: Build and Push Docker Image

on:
  push:
    branches: [main]
    tags: ['v*.*.*']     # also trigger on version tags like v1.2.3

env:
  IMAGE_NAME: myorg/myapp

jobs:
  docker:
    runs-on: ubuntu-latest

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

      # Extract metadata (tags, labels) for Docker
      - name: Extract Docker metadata
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.IMAGE_NAME }}
          tags: |
            type=sha,prefix=sha-                   # sha-abc1234
            type=ref,event=branch                  # main
            type=semver,pattern={{version}}        # 1.2.3  (from git tag)
            type=semver,pattern={{major}}.{{minor}} # 1.2
            type=raw,value=latest,enable={{is_default_branch}}

      # Login to Docker Hub
      - name: Login to Docker Hub
        uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}    # use token, not password

      # Set up Docker Buildx (for multi-platform builds + cache)
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      # Build and push
      - name: Build and push Docker image
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags:    ${{ steps.meta.outputs.tags }}
          labels:  ${{ steps.meta.outputs.labels }}
          cache-from: type=gha              # use GitHub Actions cache
          cache-to:   type=gha,mode=max    # save cache after build
          platforms: linux/amd64,linux/arm64   # multi-arch

Push to GitHub Container Registry (GHCR — Free)

YAML
env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}   # org/repo → ghcr.io/org/repo

jobs:
  docker:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write   # needed to push to GHCR

    steps:
      - uses: actions/checkout@v4

      - name: Login to GHCR
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}   # auto-provided, no setup needed!

      - name: Build and push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: ${{ github.ref == 'refs/heads/main' }}   # only push on main
          tags: |
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}

Full CI/CD: Test → Build → Push → Deploy

YAML
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-java@v4
        with: { java-version: '17', distribution: temurin, cache: maven }
      - run: mvn test

  build-push:
    needs: test
    runs-on: ubuntu-latest
    outputs:
      image-tag: ${{ github.sha }}

    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_TOKEN }}

      - uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: myorg/myapp:${{ github.sha }},myorg/myapp:latest
          cache-from: type=gha
          cache-to:   type=gha,mode=max

  deploy:
    needs: build-push
    runs-on: ubuntu-latest
    environment: production

    steps:
      - name: Deploy to server
        run: |
          ssh -i ${{ secrets.SSH_KEY }} user@server.example.com \
            "docker pull myorg/myapp:${{ github.sha }} && \
             docker stop myapp || true && \
             docker run -d --name myapp -p 8080:8080 myorg/myapp:${{ github.sha }}"

Dockerfile Example

DOCKERFILE
FROM openjdk:17-jre-slim
WORKDIR /app
COPY target/myapp-*.jar app.jar
EXPOSE 8080
HEALTHCHECK --interval=30s CMD curl -f http://localhost:8080/health || exit 1
ENTRYPOINT ["java", "-jar", "app.jar"]

Follow AutomateQA

Related Topics