How to Publish Releases to Docker Hub using GitHub Actions

REFERENCE
8 min read

Dolt is a version controlled relational database. It is a combination of Git and MySQL. We've received requests for an official Dolt Docker image from many customers who want to run Dolt in a container. We give our customers what they ask for, no matter what. 😎 Today, we announce our official Docker Images on Docker Hub.

  • dolthub/dolt gives you a container with Dolt installed and is equivalent to running the dolt command.
  • dolthub/dolt-sql-server runs a server in the container. It can be mounted with your local directory for storing your data locally and can be connected from the host system through port-mapping.

In this blog, we see how to build and push Docker Images both locally and through GitHub Actions. The examples are for building the dolthub/dolt image.

Let's look at how we publish Docker images locally

This is a quick guide on how to build and push Docker images from a local machine. First, we need a Dockerfile from which to build an image. Below is a simple Dockerfile for installing Dolt.

FROM --platform=linux/amd64 ubuntu:22.04

ARG DOLT_VERSION

ADD https://github.com/dolthub/dolt/releases/download/v${DOLT_VERSION}/dolt-linux-amd64.tar.gz dolt-linux-amd64.tar.gz
RUN tar zxvf dolt-linux-amd64.tar.gz && \
cp dolt-linux-amd64/bin/dolt /usr/local/bin && \
rm -rf dolt-linux-amd64 dolt-linux-amd64.tar.gz

ENTRYPOINT ["/usr/local/bin/dolt"]

To build a Docker image, we run the command below from the directory that the Dockerfile is in. This command builds an image named dolt with the tag name latest using the Dockerfile above and a build argument of DOLT_VERSION=0.50.8. The -t flag gets a value of <image_name>:<tag_name>, and we provide an argument DOLT_VERSION with any Dolt version we want. This is used to download a specific Dolt version for the image. Defining the the exact version of Dolt is a good practice because it lets us pin to the exact version we want, without worrying that future builds will pull in a new version we might not be expecting.

$ docker build -t dolt:latest --build-arg DOLT_VERSION=0.50.8 -f Dockerfile .

Now, we have built our image. We can use the image to run any dolt CLI commands on the host system. Running the image with argument version is equivalent as running dolt version if dolt was installed on our local machine. This verifies that the version we specified to build the image was successfully installed in the container.

$ docker run dolt:latest version
dolt version 0.50.8
no valid database in this directory

This is a simple example of how we can create Docker image locally and run it. If we want to publish our image, we can simply push the image we created locally to Docker Hub. To do this, we need to be logged in to Docker Hub and make sure the image name has the <user_name>/<image_name>:<tag_name> pattern with the user_name that is used to log in to Docker Hub. Since we did not create the initial image with the username specified like this, we need to create a new image with an appropriate name before we can push it to our Docker Hub account.

$ docker build -t jennifersp/dolt:latest --build-arg DOLT_VERSION=0.50.8 -f Dockerfile .
$ docker push jennifersp/dolt:latest

Now, we can see the Dolt Docker image was successfully pushed to Docker Hub.

docker

Let's automate Docker image push with GitHub Actions

I personally experienced how easy and flexible using GitHub Actions is, compared to setting up automated builds on Docker Hub for an organization's repository. I basically spent a week trying to figure out the whole process with automated builds including building the Dockerfile. This is from someone who was unfamiliar with Docker to begin with.

The goal is to create a GitHub Actions job that builds and pushes the Docker image on every release of Dolt. A little bonus addition to it is that we want to support multiple architectures for our customers, who need linux/amd64 and linux/arm64.

Here is breakdown of the yaml file for GitHub Actions to build and push the Docker image.

  1. We give the GitHub Actions job a good name that will be displayed on Actions tab of GitHub repository
name: Push Docker Image to Docker Hub
  1. We require an input for the Dolt version number we want to create the image with.
on:
  workflow_dispatch:
    inputs:
      version:
        description: 'SemVer format release tag, i.e. 0.24.5'
        required: true
  repository_dispatch:
    types: [ push-docker-image ]
  1. The last part is the list of steps to take in the GitHub Actions job.
  • It starts with checking out the latest version of GitHub repository under workspace.
  • Next, it logs into DockerHub using previously set secrets on GitHub, so it's kept secure and ready to be used. This is a part that lead me to try out automated builds on DockerHub instead of GitHub Actions job. I will include why I tried using DockerHub automated builds web UI and ended up not using it at the end of the blog.
  • After getting logged in to Docker Hub, we need to set up QEMU, which allows us to build images for different architectures on a single machine regardless of its architecture. In our case, it allows us to build images for linux/amd64 and linux/arm64 on a linux/amd64 system.
  • Then, it sets up Docker Buildx tool. This is an experimental tool, and it needs specific setup to run locally. I did not find any guide on how to enable this mode on docker documentations, but I found this GitHub README, which was helpful.
  • Finally, it builds and pushes the image at the same time. This step builds the image using two specified platforms, the Dockerfile and tag names we want to publish. In our case, we want to update and push latest tag and create new tag with the new release version.
  • The last step is to update the README on our Docker Hub repository with specified path to file that we want to use.
jobs:
  docker-image-push:
    name: Push Docker Image
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Login to Docker Hub
        uses: docker/login-action@v1
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
      - name: Set up QEMU
        uses: docker/setup-qemu-action@v2
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
        with:
          platforms: linux/amd64,linux/arm64
      - name: Build and push dolt image
        uses: docker/build-push-action@v3
        with:
          platforms: linux/amd64,linux/arm64
          context: .
          file: ./docker/Dockerfile
          push: true
          tags: dolthub/dolt:${{ github.event.inputs.version || github.event.client_payload.version }} , dolthub/dolt:latest
          build-args: |
            DOLT_VERSION=${{ github.event.inputs.version || github.event.client_payload.version }}
      - name: Update Docker Hub Readme for dolt image
        uses: peter-evans/dockerhub-description@v3
        with:
          username: ${{ secrets.DOCKER_HUB_USERNAME }}
          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
          repository: dolthub/dolt
          readme-filepath: ./docker/README.md

I have broken down a single file into three section above. The source of these files can be found in Dolt repository.

With above GitHub Actions job, let's use the same Dockerfile we used to publish the image locally. We can use this Dockerfile for building images for both amd64 and arm64 architectures. BUILDPLATFORM and BUILDARCH is a global environment variable that is defined during build depending on the platform the image is build on. For example, when building image for platform,linux/amd64, the values are set as BUILDPLATFORM=linux/amd64 and BUILDARCH=amd64. This allows us to install the correct binary distributor to be installed in the container on specific platform.

FROM --platform=$BUILDPLATFORM ubuntu:22.04

ARG DOLT_VERSION
ARG BUILDARCH

ADD https://github.com/dolthub/dolt/releases/download/v${DOLT_VERSION}/dolt-linux-${BUILDARCH}.tar.gz dolt-linux-${BUILDARCH}.tar.gz
RUN tar zxvf dolt-linux-${BUILDARCH}.tar.gz && \
cp dolt-linux-${BUILDARCH}/bin/dolt /usr/local/bin && \
rm -rf dolt-linux-${BUILDARCH} dolt-linux-${BUILDARCH}.tar.gz

ENTRYPOINT ["/usr/local/bin/dolt"]

Why GitHub Actions is better

I first set up the 'organization' account for dolthub, but I did not find where the access token was for this account, so I thought it was not possible to make a GitHub Actions job to push images to the organization repository. What made me more convinced was that there is automated builds set up web UI on Docker Hub. It was definitely challenging as there is very little Docker documentation for how I can use automated builds on the Docker Hub web UI for organization repositories. I like to look at examples and follow along to understand usages of some features. This took a while for me to get something working, and when I was finishing up, I got stuck with not being able to disable the root README of GitHub dolt repository from updating the Docker Hub dolt repository README. This was frustrating because I found I could use .dockerignore to ignore the README in the root, but it does not work for automated builds. Another downside of using the Docker Hub web UI for automated builds was that it needed a lot more files including pre- and post-build scripts and separate Dockerfiles for each platform I wanted to support.

This is why I had to look for a different way to resolve this and came back to a GitHub Actions job, which I initially thought was not possible. The good part is that I learned a lot about both the case of automated build and pushing Docker images either through Docker Hub or through GitHub Actions.

If you're interested in learning more about Dolt, try out our Docker Images. If you have any questions about Dolt, join our Discord channel to chat with our team.

SHARE

JOIN THE DATA EVOLUTION

Get started with Dolt

Or join our mailing list to get product updates.