Minimal Docker Container

Nov 10, 2015

Most Docker images start from the Docker Hub, with its set of base OS images and application images built from them. With Docker’s layered architecture for images, the fact that typical images are 50MB to 100MB is not a major issue.

At the same time, there are uses for Docker where building from scratch is desirable, and when building from scratch, the image might as well be as small as possible.

Of course, the reason the base OS images are the size they are is because they include base utilities and dynamically linked libraries. If our purpose is just to run a statically linked application, only the application file needs to be included in the Docker image.

The Go programming language provides statically linked executables (with the right compiler flags). I’ve been working with Go and figured it would be interesting to try out packing a small sample application into a Docker container.

The Go application is basic:

package main

import (

func ArgServer(w http.ResponseWriter, req *http.Request) {
        fmt.Fprintln(w, os.Args)

func main() {
        http.Handle("/args", http.HandlerFunc(ArgServer))
        log.Fatal(http.ListenAndServe(":8080", nil))

This application uses the net/http library, which is one library that Go potentially links dynamically, so it makes a good example for creating a statically linked Go executable. It also uses command-line arguments, allowing those to be demonstrated as well.

To create a fully statically linked version of this application, I used the following compile command:

CGO_ENABLED=0 GOOS=linux go build -a -tags netgo -ldflags '-w' ...argserver

According to this page this form only works with Go 1.3 or older, but for me this command worked fine while running Go 1.5.1. If you do have trouble, the linked page suggests using installsuffix to keep static versions of components separate in newer versions of Go.

With the argserver executable and a Dockerfile in the current directory, this command will create and tag our minimal Docker image:

docker build -t argserver .

Here is the Dockerfile:

FROM scratch

ADD argserver /argserver

ENTRYPOINT ["/argserver"]

CMD ["a", "b", "c", "d"]

Note that we start from “scratch”, we add only the single executable, and we set it as the entry point. The CMD line provides default arguments which we can override.

When running this image, it is necessary to expose port 8080 outside the container so we can access the HTTP server:

docker run -d -p 8081:8080 argserver

This command will run the container in the background and map port 8081 on the host to port 8080 in the container. We can then access the HTTP server:

> curl http://localhost:8081/args
[/argserver a b c d]

Or, we could override the command line args:

> docker run -d -p 8081:8080 argserver g h i j
> curl http://localhost:8081/args
[/argserver g h i j]

When testing this with Ubuntu Wily running Go 1.5.1, the resulting Docker image was 4.9MB. Most of this is static libraries, so we could add quite a bit more HTTP server functionality without the image getting much larger.