Undefined Title

Undefined Title

Image sizes created by buildpacks pack suggests

Buildpacks and pack

Buildpacks is a CNCF project in image build category. Without writing Dockerfile, you can make iamge if the given builder supports your platform.

$ pack build myapp:1 --builder heroku/buildpacks:18

pack is helpful to casually create docker images. Several companies now provide their own buildpack they maintain. I was interested in image size each buildpack creates especially for golang. I briefly checked them.


pack command has a sub-command, suggest-builders. It shows some available builders.

$ pack --version

$ pack suggest-builders
Suggested builders:
        Google:                gcr.io/buildpacks/builder:v1      Ubuntu 18 base image with buildpacks for .NET, Go, Java, Node.js, and Python
        Heroku:                heroku/buildpacks:18              heroku-18 base image with buildpacks for Ruby, Java, Node.js, Python, Golang, & PHP
        Paketo Buildpacks:     paketobuildpacks/builder:base     Ubuntu bionic base image with buildpacks for Java, NodeJS and Golang
        Paketo Buildpacks:     paketobuildpacks/builder:full     Ubuntu bionic base image with buildpacks for Java, .NET, NodeJS, Golang, PHP, HTTPD and NGINX
        Paketo Buildpacks:     paketobuildpacks/builder:tiny     Tiny base image (bionic build image, distroless run image) with buildpacks for Golang

There are five builders provided by three organizations. Google, Heroku and Paketo. Paketo provides three kinds of builders, base, full and tiny. You can guess it's named based on image size.

I created images for the next simple golang code using each builder.

package main

import (

var Commit string

func main() {
        msg := flag.String("msg", "", "message")
        fmt.Println("commit:", Commit)
        fmt.Println("msg:", *msg)

Commit is a variable to check how linker option works of each builder. Regarding flags, certainly it's for command line arguments.

Let's see how to run command one by one.

Google's builder


$ pack build \
  --env "GOOGLE_GOLDFLAGS=-X main.Commit=`git rev-parse HEAD`" \
  --builder gcr.io/buildpacks/builder \

Heroku's builder


$ pack build \
  --env "GO_LINKER_SYMBOL=main.Commit" \
  --env "GO_LINKER_VALUE=`git rev-parse HEAD`" \
  --builder heroku/buildpacks:18 \

Paketo's builder


$ pack build \
  --builder paketobuildpacks/builder:tiny \  # aware of buildpack.yml

$ cat buildpack.yml
    - -ldflags="-X main.Commit=some-value"

You need to generate buildpack.yml to embed commit. I like the way to use environment variable like Google and Heroku.

Image size?

Here is the result.

$ docker image ls | grep ^myapp | sort
myapp    google      ... 40 years ago        116MB
myapp    heroku      ... 40 years ago        535MB
myapp    paketo-base ... 40 years ago        91.8MB
myapp    paketo-full ... 40 years ago        706MB
myapp    paketo-tiny ... 40 years ago        22.7MB

The size of paketo-tiny is really small. paketo-base and google are about 100MB. heroku and paketo-full look not very small.

Next, let's see each execution.

$ docker run --rm myapp:google -msg hello
commit: 7aea0234dabc623302892677296858679463268a
msg: hello
$ docker run --rm myapp:heroku -msg hello
commit: 7aea0234dabc623302892677296858679463268a
$ docker run --rm myapp:paketo-tiny -msg hello  # same with full and base.
msg: hello
commit: some-value

Oh? heroku's one seems not to show the value given by -msg option. I've not checked the heroku buildpack document. It may not be able to pass any command line arguments. Google and paketo can show that. Actually paketo is not very smart to pass commit value.


  • For image size, paketobuildpacks/builder:tiny overwhelmingly wins. It's amazingly small.
  • Paketo's builder doesn't support environment variable for likner options. It's not very convenient actually.
  • Google's builder seems balanced for image size and its feature.
  • Heroku's builder looks not very convenient for me.