If you want to compile your Program while building your Container but don’t want you source code to be part of your image, you need to use stages.
Why should I want that?
My biggest concern is, that your container image gets small if it does only contain the binary and not all of the source code and temporary file from building it. That helps with download and creation-speed.
On the other hand one might fear that his source code gets extracted if someone gets access to the image.
How to use it
In my example I am building a simple Go Application and my Dockerfile looks like this.
FROM golang:1.21-bullseye
ENV GOPATH=/app/dependencies
WORKDIR /app
COPY . .
RUN go build
RUN chmod +x my-app
CMD /app/my-app
First of all we will add a name to the first phase right behind the FROM
-Statement. Lets call it Build. That way we can reference it in the next phase.
We can also drop the CMD
-Statement at bottom, since we will not run this container.
FROM golang:1.21-bullseye as build
ENV GOPATH=/app/dependencies
WORKDIR /app
COPY . .
RUN go build
RUN chmod +x my-app
Next we will add our second phase below it. The great thing of multiple phases is, that i can use different images for different phases. In this case I will compile in a golang container and execute in an alpine container.
FROM golang:1.21-bullseye as build
ENV GOPATH=/app/dependencies
WORKDIR /app
COPY . .
RUN go build
FROM alpine:3.10
# Go links to libc -> you need to install it
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY --from=build /app/my-app /app/
RUN chmod +x /app/my-app
CMD /app/my-app
You can see that i reference the build
phase in the COPY
-Statement of the second phase. This way i can copy only the compiled binary from phase one.
I also moved the chmod
to the second stage instead of the first.
Now we can build our image as usual.
docker build -t my-app:latest .
After that we can use our Container-Image and it will only have the binary in it and not the whole source-code.
docker run my-app:latest