Export binaries

Did you know that you can use Docker to build your application to standalone binaries? Sometimes, you don’t want to package and distribute your application as a Docker image. Use Docker to build your application, and use exporters to save the output to disk.

The default output format for docker build is a container image. That image is automatically loaded to your local image store, where you can run a container from that image, or push it to a registry. Under the hood, this uses the default exporter, called the docker exporter.

To export your build results as files instead, you can use the --output flag, or -o for short. the --output flag lets you change the output format of your build.

Export binaries from a build

If you specify a filepath to the docker build --output flag, Docker exports the contents of the build container at the end of the build to the specified location on your host's filesystem. This uses the local exporter.

The neat thing about this is that you can use Docker's powerful isolation and build features to create standalone binaries. This works well for Go, Rust, and other languages that can compile to a single binary.

The following example creates a simple Rust program that prints "Hello, World!", and exports the binary to the host filesystem.

  1. Create a new directory for this example, and navigate to it:

    $ mkdir hello-world-bin
    $ cd hello-world-bin
    
  2. Create a Dockerfile with the following contents:

    # syntax=docker/dockerfile:1
    FROM rust:alpine AS build
    WORKDIR /src
    COPY <<EOT hello.rs
    fn main() {
        println!("Hello World!");
    }
    EOT
    RUN rustc -o /bin/hello hello.rs
    
    FROM scratch
    COPY --from=build /bin/hello /
    ENTRYPOINT ["/hello"]

    Tip

    The COPY <<EOT syntax is a here-document. It lets you write multi-line strings in a Dockerfile. Here it's used to create a simple Rust program inline in the Dockerfile.

    This Dockerfile uses a multi-stage build to compile the program in the first stage, and then copies the binary to a scratch image in the second. The final image is a minimal image that only contains the binary. This use case for the scratch image is common for creating minimal build artifacts for programs that don't require a full operating system to run.

  3. Build the Dockerfile and export the binary to the current working directory:

    $ docker build --output=. .
    

    This command builds the Dockerfile and exports the binary to the current working directory. The binary is named hello, and it's created in the current working directory.

Exporting multi-platform builds

You use the local exporter to export binaries in combination with multi-platform builds. This lets you compile multiple binaries at once, that can be run on any machine of any architecture, provided that the target platform is supported by the compiler you use.

Continuing on the example Dockerfile in the Export binaries from a build section:

# syntax=docker/dockerfile:1
FROM rust:alpine AS build
WORKDIR /src
COPY <<EOT hello.rs
fn main() {
    println!("Hello World!");
}
EOT
RUN rustc -o /bin/hello hello.rs

FROM scratch
COPY --from=build /bin/hello /
ENTRYPOINT ["/hello"]

You can build this Rust program for multiple platforms using the --platform flag with the docker build command. In combination with the --output flag, the build exports the binaries for each target to the specified directory.

For example, to build the program for both linux/amd64 and linux/arm64:

$ docker build --platform=linux/amd64,linux/arm64 --output=out .
$ tree out/
out/
├── linux_amd64
│   └── hello
└── linux_arm64
    └── hello

3 directories, 2 files

Additional information

In addition to the local exporter, there are other exporters available. To learn more about the available exporters and how to use them, see the exporters documentation.