Publishing Docker image to Docker Hub using Automated builds

Published March 8, 2019 in   docker, docker hub, sass, multi-stage, automation, labels,

Although there are some resources on how to create, label and publish images to, I have found myself in need to put all those pieces together. This article is the complete example of how to create, build and test, label and publish docker image using Automated Builds with source code hosted at

In this article we will:

  • create small docker image, build it and test locally
  • create and link repository with GitHub repository
  • setup automated builds of the image
  • alter the automated builds setup to include build-time labels
  • verify the build


First of all, we need to:

Create the Docker image

As an example software to bundle as a docker image, we will use SASS. At the time of writing, I was unable to find any docker image, which would distribute the dart-sass binary version. This may be, cause the ruby version of SASS is officially deprecated and will become unmaintained. Maybe majority of the world use sassc+libsass which already has its docker version. Nevertheless, I decided to create my own small docker image (~80MB) and publish it.


We will create a two stage Dockerfile as recommended by Dockerfile Best Practices. See the Multistage Builds for more information.


FROM ubuntu:18.04 as build
ADD /opt/
RUN tar -C /opt/ -xzvf /opt/dart-sass-1.17.2-linux-x64.tar.gz

FROM ubuntu:18.04 as final

COPY --from=build /opt/dart-sass /opt/dart-sass

CMD [ "/opt/dart-sass/sass", "--watch", "/sass:/css" ]

This Dockerfile first uses ubuntu:18.04 as a build stage, in which it just downloads the binary distribution of dart-sass and unpacks its contents to /opt/dart-sass directory.

This build stage is then thrown away and only the /opt/dart-sass directory and its contents are embedded into final docker image using COPY instruction.

The run command for this image is the one, which a web developer usually runs locally:

/opt/dart-sass/sass --watch /sass:/css

The docker image is designed, to be run by a web developer using bind mounted directories:

  • <source code>/sass (host machine) —> /sass (docker container)
  • <source code>/css (host machine) —> /css (docker container)

This way, the sass binary inside docker container will watch for changes from host machine rebuilding the CSS files, propagating the changes back to host machine.

The appropriate run command for this image would be:

docker run -v $PWD/sass:/sass/ -v $PWD/css:/css/ --init -it michalklempa/dart-sass:latest

Let us build the image:

docker build -t michalklempa/dart-sass .
Sending build context to Docker daemon  2.048kB
Step 1/6 : FROM ubuntu:18.04 as build
 ---> 47b19964fb50
Step 2/6 : ADD /opt/
Downloading [==================================================>]  11.84MB/11.84MB
 ---> db91f4f2622b
Step 3/6 : RUN tar -C /opt/ -xzvf /opt/dart-sass-1.17.2-linux-x64.tar.gz
 ---> Running in 0015f971339d
Removing intermediate container 0015f971339d
 ---> 42ab633cad2e
Step 4/6 : FROM ubuntu:18.04 as final
 ---> 47b19964fb50
Step 5/6 : COPY --from=build /opt/dart-sass /opt/dart-sass
 ---> 26645a730c07
Step 6/6 : CMD [ "/opt/dart-sass/sass", "--watch", "/sass:/css" ]
 ---> Running in dc5d4299b299
Removing intermediate container dc5d4299b299
 ---> 1dda340b7fe8
Successfully built 1dda340b7fe8
Successfully tagged michalklempa/dart-sass:latest

To test the image, we will need to create two directories: sass and css and create at least sass/main.sass file:

mkdir -p css
mkdir -p sass
cat >./sass/main.sass <<'EOL'
$font-stack:    Helvetica, sans-serif
$primary-color: #333

  font: 100% $font-stack
  color: $primary-color

And test image locally:

docker run -v $PWD/sass:/sass/ -v $PWD/css:/css/ --init -it michalklempa/dart-sass:latest

Expected output is:

Compiled sass/main.sass to css/main.css.
Sass is watching for changes. Press Ctrl-C to stop.

And the generated CSS file css/main.css:

body {
  font: 100% Helvetica, sans-serif;
  color: #333;

/*# */

Now that we have our image, we can push it to GitHub repository and set-up the link between and GitHub.

Create a GitHub repository

Let us create a repository named docker-dart-sass and make it public so the could access the source files of our repository.

As we already have done some work, we do not clone the repository, just link our directory with it.

Initialize empty repository in our current directory:

git init

and add remote:

git remote add origin

Now we can checkout master branch:

git checkout master

Add our files:

git add Dockerfile
git commit -m"Our dockerfile"
git push -u origin master

Setup Automated Build

At settings link your account to GitHub account. At the Builds tab of the Docker Repository Settings page, set up Automated Builds.

Automated Builds Setup
Automated Builds Setup

We should see a build being done:

Building in Docker Cloud's infrastructure...
Cloning into '.'...
Warning: Permanently added the RSA host key for IP address '' to the list of known hosts.
Reset branch 'master'
Your branch is up-to-date with 'origin/master'.
KernelVersion: 4.4.0-1060-aws
Components: [{u'Version': u'18.03.1-ee-3', u'Name': u'Engine', u'Details': {u'KernelVersion': u'4.4.0-1060-aws', u'Os': u'linux', u'BuildTime': u'2018-08-30T18:42:30.000000000+00:00', u'ApiVersion': u'1.37', u'MinAPIVersion': u'1.12', u'GitCommit': u'b9a5c95', u'Arch': u'amd64', u'Experimental': u'false', u'GoVersion': u'go1.10.2'}}]
Arch: amd64
BuildTime: 2018-08-30T18:42:30.000000000+00:00
ApiVersion: 1.37
Platform: {u'Name': u''}
Version: 18.03.1-ee-3
MinAPIVersion: 1.12
GitCommit: b9a5c95
Os: linux
GoVersion: go1.10.2
Starting build of
Step 1/13 : FROM ubuntu:18.04 as build
---> 47b19964fb50
Step 2/13 : ADD /opt/

If everything went well, we can now pull the image and inspect it:

docker pull michalklempa/dart-sass:latest
docker inspect michalklempa/dart-sass

Search for the Config.Labels:

 "Config": {
            "Hostname": "",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
            "Cmd": [
            "ArgsEscaped": true,
            "Image": "sha256:26645a730c0736c512fd65bf4f61355fd10321f8ef002f5fb0322e7091ed8ae8",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": null

As we can see, labels are empty at the moment. Labels add metadata to the image which are used also by user interface (e.g. repository URL).

Add metadata to image using labels

Some set of labels and the steps needed are described in Ross Fairbanks blog on labels together with the code. We will do basically same steps and add more labels. For those interested, look at for full list of labels.

Great example to start with is Dockerfile made by Justin J. Novack:

FROM scratch

ARG COMMIT="local-build"
ARG DATE="1970-01-01T00:00:00Z"

LABEL org.label-schema.schema-version="1.0" \$DATE \
    org.label-schema.vendor="Justin J. Novack" \"jnovack/dockerhub-hooks" \
    org.label-schema.description="Sample Docker Hub Build Hooks" \
    org.label-schema.version="$VERSION" \
    org.label-schema.vcs-url=$URL \
    org.label-schema.vcs-branch=$BRANCH \


We will extend this set of labels with:

    org.label-schema.url="" \
    org.label-schema.docker.cmd="docker run -v $PWD/sass:/sass/ -v $PWD/css:/css/ --init -it michalklempa/dart-sass:latest"

and also add MAINTAINER to our Dockerfile:

FROM ubuntu:18.04 as build
ADD /opt/
RUN tar -C /opt/ -xzvf /opt/dart-sass-1.17.2-linux-x64.tar.gz

FROM ubuntu:18.04 as final


LABEL org.label-schema.schema-version="1.0" \$DATE \
    org.label-schema.vendor="Michal Klempa" \"michalklempa/dart-sass" \
    org.label-schema.description="sass/dart-sass docker image for web development purposes. Runs sass --watch on provided volumes." \
    org.label-schema.url="" \
    org.label-schema.docker.cmd="docker run -v $PWD/sass:/sass/ -v $PWD/css:/css/ --init -it michalklempa/dart-sass:latest" \
    org.label-schema.version="$VERSION" \
    org.label-schema.vcs-url=$URL \
    org.label-schema.vcs-branch=$BRANCH \

COPY --from=build /opt/dart-sass /opt/dart-sass

CMD [ "/opt/dart-sass/sass", "--watch", "/sass:/css" ]

Now building this image will require us to specify multiple --build-arg arguments. To achieve these arguments appear on command line during automated build, we will override a build hook. Create file ./hooks/build and put the great hook from Justin there:

# hooks/build

# $IMAGE_NAME var is injected into the build so the tag is correct.
echo "[***] Build hook running"

docker build \
  --build-arg VERSION=$(git describe --tags --always) \
  --build-arg COMMIT=$(git rev-parse HEAD) \
  --build-arg URL=$(git config --get remote.origin.url) \
  --build-arg BRANCH=$(git rev-parse --abbrev-ref HEAD) \
  --build-arg DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") \

Now commit and push the changes. We should see the metadata added on also on Or we can re-pull the image and inspect it again:

docker pull michalklempa/dart-sass
docker inspect michalklempa/dart-sass

            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {
                "": "2019-03-08T15:57:06Z",
                "org.label-schema.description": "sass/dart-sass docker image for web development purposes. Runs sass --watch on provided volumes.",
                "org.label-schema.docker.cmd": "docker run -v /sass:/sass/ -v /css:/css/ --init -it michalklempa/dart-sass:latest",
                "": "michalklempa/dart-sass",
                "org.label-schema.schema-version": "1.0",
                "org.label-schema.url": "",
                "org.label-schema.vcs-branch": "master",
                "org.label-schema.vcs-ref": "dfc9fec637a677aaa267a290d267c9afda0ed6d4",
                "org.label-schema.vcs-url": "",
                "org.label-schema.vendor": "Michal Klempa",
                "org.label-schema.version": "v1.17.2-02-1-gdfc9fec"
        "Architecture": "amd64",
        "Os": "linux",


We have created and published our Docker image using Automated builds with properly assigned labels on it. All the source code is available at under GPLv3. The docker image is available at

Any comments are welcome.

Note 1: We have not been discussing git tagging the source repository to generate corresponding tags in automated builds. However, example source repository and automated builds rule are set properly, to accommodate git tags in format: v1.0.0-06 into proper docker build tags. Version format is similar to one used in Debian packaging (v<upstream>-<package_maintainer_version> see deb-version)

Posted by Michal Klempa on 8 March 2019
automation, docker, docker hub,, labels, multi-stage, sass