Skip to main content

Creating standalone Golang apps in Docker

Most tutorials about dockerising Golang apps tell you to use the Golang image as the base so that you can compile it. Don't do that! Why would you want hundreds of megabytes of unnecessary cruft in your image, when you could just have a small standalone binary, running in a container by itself?

Build the binary on your development machine with a flag to statically link required libraries, and copy it directly into a scratch image. This will give you a wonderfully small, portable image with a minimal attack surface:

# Build a binary of your app.
CGO_ENABLED=0 go build -o yourappname *.go

# Make a tar archive of the binary and any other files it needs.
tar -cf yourappname.tar ./*

# Build a Docker image directly from the tar file.
docker import -c "ENTRYPOINT [\"/yourappname\"]" - yourappname < yourappname.tar

Automate build and Docker image creation with a makefile

But it's probably most convenient to automate the whole build and image creation process with a makefile. The command 'make container' will build the whole thing.

.DEFAULT_GOAL := build

fmt:
	go fmt ./...
lint: fmt
	golint ./...
vet: fmt
	go vet ./...
	shadow ./...
build: vet
	CGO_ENABLED=0 go build -o yourappname *.go
tar: build
	tar -cf yourappname.tar ./*
container: tar
	docker import -c "ENTRYPOINT [\"/yourappname\"]" - yourappname < yourappname.tar

Solution for "exec user process caused: no such file or directory"

If you are trying to build a minimalist scratch Docker container for a Golang app you may encounter the following rather infuriating error:

standard_init_linux.go:228: exec user process caused: no such file or directory

The solution is to prepend the flag CGO_ENABLED=0 to the build line as described above, eg:

CGO_ENABLED=0 go build -o yourappname *.go

This flag tells Go to build a statically-linked binary, which doesn't have any external dependencies. That's exactly what you need in a scratch (empty) container.

Copyright, all rights reserved.