Building and deploying Docker images with GitHub Actions

Slowly moving my projects from a selfhosted GitLab instance to GitHub, while doing so I'm learning how to use GitHub Actions to build and delpoy Docker images to the GitHub Container Registry (ghcr.io).

After having most of my personal projects hosted on a Docker server at home, I decided to move everything out again after a power outage and multiple fiber outages.

One of the steps involved is moving projects from a self hosted GitLab instance to GitHub and using GitHub Actions to build and deploy Docker images to the GitHub Container Registry (ghcr.io).

In this blog post I will show you how to build and deploy a Docker image to the GitHub Container Registry using GitHub Actions. I’ll use the workflow used to build and deploy this website as an example to get you started with GitHub Actions and Docker.

Website repository: github.com/jeroenvanwissen/jeroenvanwissen.nl

Create a Dockerfile

First, create a Dockerfile in the root of your repository. This file will be used to build the Docker image. Here is the Dockerfile used to build the Docker image of this website, which is an Astro.build application deployed using Nginx:

FROM node:lts-alpine as build-stage

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

RUN npm run build

FROM nginx:alpine

COPY --from=build-stage /app/dist /usr/share/nginx/html

COPY ./conf/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

HEALTHCHECK --start-period=30s --interval=5m --timeout=1s --retries=5 CMD curl -sf http://localhost/health || exit 1

CMD ["nginx", "-g", "daemon off;"]

This Dockerfile uses a multi-stage build to first build the Astro.build application using Node.js, and then copies the built files to an Nginx container. The Nginx container serves the static HTML files generated by the Astro.build application.

Create a GitHub Actions workflow

Next, create a GitHub Actions workflow file in the .github/workflows directory of your repository. This file will define the steps to build and deploy the Docker image. Here is the GitHub Actions workflow file used to build and push the Docker image of this website to the GitHub Container Registry:

name: Build and push Docker image to ghcr.io

on:
  push:
    tags: [ 'v*.*.*' ]

env:
  REGISTRY: ghcr.io
  # github.repository as <account>/<repo>
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/[email protected]

      - name: Set up Docker Buildx
        uses: docker/[email protected]
 
      - name: Log into registry ${{ env.REGISTRY }}
        if: github.event_name != 'pull_request'
        uses: docker/[email protected]
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/[email protected]
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build and push Docker image
        id: build-and-push
        uses: docker/[email protected]
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: |
            ${{ steps.meta.outputs.tags }}
            ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest
          labels: ${{ steps.meta.outputs.labels }}

This GitHub Actions workflow file defines a workflow that is triggered when a new tag is pushed to the repository. It uses the docker/build-push-action action to build and push the Docker image to the GitHub Container Registry. It tags the image in the registry with the given version tag AND ‘latest’ tag, so the latest versionned image will always have the ‘latest’ tag. The workflow logs into the registry using the GitHub token, which is automatically provided by GitHub Actions.

Trigger the workflow

Now that you have created the Dockerfile, GitHub Actions workflow, you can trigger the workflow by adding a tag to your repository. Once the workflow is triggered, it will build the Docker image and push it to the GitHub Container Registry.

To use this Docker image in my Docker Compose file, I simply referenced the image:

...
services:
  website:
    image: ghcr.io/jeroenvanwissen/jeroenvanwissen.nl:latest
...

That’s it! You have successfully built and deployed a Docker image to the GitHub Container Registry using GitHub Actions. You can now use this workflow as a template for building and deploying other Docker images in your projects.

I hope this blog post was helpful to you. If you have any questions or feedback, feel free to contact me on Twitter/X or Mastodon. I’d love to hear from you!

← back to all posts