hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses Deploying Node.js Apps with Docker

Why Docker

  • The "Works on My Machine" Problem
  • Docker Concepts

Your First Dockerfile

  • Writing a Dockerfile
  • Multi-Stage Builds
  • The .dockerignore File

Running Containers

  • Running and Managing Containers
  • Environment Variables and Secrets
  • Health Checks

Multi-Container Apps

  • Docker Compose
  • Adding a Reverse Proxy
  • Persistent Data

Deploying to Production

  • Deploying to a VPS
  • HTTPS with Let's Encrypt
  • Zero-Downtime Deploys

CI/CD

  • Building Images in CI
  • Automated Deployment

Production Hardening

  • Container Security
  • Logging and Monitoring
  • Deployment Checklist and Capstone

Building Images in CI

Automate the build

Building Docker images locally and pushing them manually works for one developer. With a team, you want every merge to main to automatically build and push a new image.

GitHub Actions workflow

# .github/workflows/build.yml
name: Build and Push

on:
  push:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - 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 }}

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

What each step does

Checkout code — Pulls your repository so Docker can access the Dockerfile and source code.

Set up Docker Buildx — Buildx is the modern Docker build engine. It supports layer caching across CI runs, which makes builds much faster.

Log in to Docker Hub — Authenticates with the registry. The username and password come from GitHub repository secrets (Settings → Secrets → Actions).

Build and push — Builds the image from the Dockerfile and pushes it to Docker Hub with two tags: latest (always the newest) and the git commit SHA (a unique, immutable tag for this specific build).

cache-from / cache-to — Each CI run starts with a clean machine. Without caching, every build reinstalls all dependencies from scratch (the RUN npm ci layer). type=gha stores Docker build layers in GitHub Actions’ built-in cache. On the next build, Docker pulls cached layers and skips unchanged steps — just like building locally after the first time. This can cut build times from minutes to seconds.

Setting up secrets

In your GitHub repository:

  1. Go to Settings → Secrets and variables → Actions
  2. Add DOCKER_USERNAME (your Docker Hub username)
  3. Add DOCKER_PASSWORD (your Docker Hub password or access token)

For GitHub Container Registry (ghcr.io), the login is simpler:

- name: Log in to GitHub Container Registry
  uses: docker/login-action@v3
  with:
    registry: ghcr.io
    username: ${{ github.actor }}
    password: ${{ secrets.GITHUB_TOKEN }}

No extra secrets needed — GITHUB_TOKEN is provided automatically.

Adding tests before building

Run your tests before building the image. If tests fail, the image is not built:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 22 }
      - run: npm ci
      - run: npx tsx --test tests/*.test.ts

  build:
    needs: test # Only runs if tests pass
    runs-on: ubuntu-latest
    steps:
      # ... build and push steps

needs: test means the build job only runs if the test job succeeds. A failing security test blocks the build and the deploy.

Tagging strategy

latest — The most recent build. Convenient but not specific. If you pull latest on two servers at different times, they might get different images.

git SHA — The commit hash (e.g., abc123def). Unique and immutable. You can always trace an image back to exactly which code built it.

Semantic version — 1.2.3. For releases. Requires manual tagging or a release process.

Use both latest (for convenience) and SHA (for traceability) on every build.

Exercises

Exercise 1: Create the GitHub Actions workflow. Push to main and verify the image appears on Docker Hub.

Exercise 2: Add the test job. Push code with a failing test. Verify the build does not run.

Exercise 3: Check the image tags on Docker Hub. You should see latest and the commit SHA.

Why tag images with the git commit SHA in addition to 'latest'?

← Zero-Downtime Deploys Automated Deployment →

© 2026 hectoday. All rights reserved.