--- title: Mounts description: Introduction to cache mounts and bind mounts in builds keywords: > build, buildkit, buildx, guide, tutorial, mounts, cache mounts, bind mounts --- {% include_relative nav.html selected="4" %} This section describes how to use cache mounts and bind mounts with Docker builds. Cache mounts let you specify a persistent package cache to be used during builds. The persistent cache helps speed up build steps, especially steps that involve installing packages using a package manager. Having a persistent cache for packages means that even if you rebuild a layer, you only download new or changed packages. Cache mounts are created using the `--mount` flag together with the `RUN` instruction in the Dockerfile. To use a cache mount, the format for the flag is `--mount=type=cache,target=`, where `` is the location of the cache directory that you wish to mount into the container. ## Add a cache mount The target path to use for the cache mount depends on the package manager you’re using. The application example in this guide uses Go modules. That means that the target directory for the cache mount is the directory where the Go module cache gets written to. According to the [Go modules reference](https://go.dev/ref/mod#module-cache), the default location for the module cache is `$GOPATH/pkg/mod`, and the default value for `$GOPATH` is `/go`. Update the build steps for downloading packages and compiling the program to mount the `/go/pkg/mod` directory as a cache mount: ```diff # syntax=docker/dockerfile:1 FROM golang:{{site.example_go_version}}-alpine AS base WORKDIR /src COPY go.mod go.sum . - RUN go mod download + RUN --mount=type=cache,target=/go/pkg/mod/ \ + go mod download -x COPY . . FROM base AS build-client - RUN go build -o /bin/client ./cmd/client + RUN --mount=type=cache,target=/go/pkg/mod/ \ + go build -o /bin/client ./cmd/client FROM base AS build-server - RUN go build -o /bin/server ./cmd/server + RUN --mount=type=cache,target=/go/pkg/mod/ \ + go build -o /bin/server ./cmd/server FROM scratch AS client COPY --from=build-client /bin/client /bin/ ENTRYPOINT [ "/bin/client" ] FROM scratch AS server COPY --from=build-server /bin/server /bin/ ENTRYPOINT [ "/bin/server" ] ``` The `-x` flag added to the `go mod download` command prints the download executions that take place. Adding this flag lets you see how the cache mount is being used in the next step. ## Rebuild the image Before you rebuild the image, clear your build cache. This ensures that you're starting from a clean slate, making it easier to see exactly what the build is doing. ```console $ docker builder prune -af ``` Now it’s time to rebuild the image. Invoke the build command, this time together with the `--progress=plain` flag, while also redirecting the output to a log file. ```console $ docker build --target=client --progress=plain . 2> log1.txt ``` When the build has finished, inspect the `log1.txt` file. The logs show how the Go modules were downloaded as part of the build. ```console $ awk '/proxy.golang.org/' log1.txt #11 0.168 # get https://proxy.golang.org/github.com/charmbracelet/lipgloss/@v/v0.6.0.mod #11 0.168 # get https://proxy.golang.org/github.com/aymanbagabas/go-osc52/@v/v1.0.3.mod #11 0.168 # get https://proxy.golang.org/github.com/atotto/clipboard/@v/v0.1.4.mod #11 0.168 # get https://proxy.golang.org/github.com/charmbracelet/bubbletea/@v/v0.23.1.mod #11 0.169 # get https://proxy.golang.org/github.com/charmbracelet/bubbles/@v/v0.14.0.mod #11 0.218 # get https://proxy.golang.org/github.com/charmbracelet/bubbles/@v/v0.14.0.mod: 200 OK (0.049s) #11 0.218 # get https://proxy.golang.org/github.com/aymanbagabas/go-osc52/@v/v1.0.3.mod: 200 OK (0.049s) #11 0.218 # get https://proxy.golang.org/github.com/containerd/console/@v/v1.0.3.mod #11 0.218 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.0.mod #11 0.219 # get https://proxy.golang.org/github.com/charmbracelet/bubbletea/@v/v0.23.1.mod: 200 OK (0.050s) #11 0.219 # get https://proxy.golang.org/github.com/atotto/clipboard/@v/v0.1.4.mod: 200 OK (0.051s) #11 0.219 # get https://proxy.golang.org/github.com/charmbracelet/lipgloss/@v/v0.6.0.mod: 200 OK (0.051s) ... ``` Now, in order to see that the cache mount is being used, change the version of one of the Go modules that your program imports. By changing the module version, you're forcing Go to download the new version of the dependency the next time you build. If you weren’t using cache mounts, your system would re-download all modules. But because you've added a cache mount, Go can reuse most of the modules and only download the package versions that doesn't already exist in the `/go/pkg/mod` directory. Update the version of the `chi` package that the server component of the application uses: ```console $ docker run -v $PWD:$PWD -w $PWD golang:{{site.example_go_version}}-alpine \ go get github.com/go-chi/chi/v5@v5.0.8 ``` Now, run another build, and again redirect the build logs to a log file: ```console $ docker build --target=client --progress=plain . 2> log2.txt ``` Now if you inspect the `log2.txt` file, you’ll find that only the `chi` package that was changed has been downloaded: ```console $ awk '/proxy.golang.org/' log2.txt #10 0.143 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.mod #10 0.190 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.mod: 200 OK (0.047s) #10 0.190 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.info #10 0.199 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.info: 200 OK (0.008s) #10 0.201 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.zip #10 0.209 # get https://proxy.golang.org/github.com/go-chi/chi/v5/@v/v5.0.8.zip: 200 OK (0.008s) ``` ## Add bind mounts There are a few more small optimizations that you can implement to improve the Dockerfile. Currently, it's using the `COPY` instruction to pull in the `go.mod` and `go.sum` files before downloading modules. Instead of copying those files over to the container’s filesystem, you can use a bind mount. A bind mount makes the files available to the container directly from the host. This change removes the need for the additional `COPY` instruction (and layer) entirely. ```diff # syntax=docker/dockerfile:1 FROM golang:{{site.example_go_version}}-alpine AS base WORKDIR /src - COPY go.mod go.sum . RUN --mount=type=cache,target=/go/pkg/mod/ \ + --mount=type=bind,source=go.sum,target=go.sum \ + --mount=type=bind,source=go.mod,target=go.mod \ go mod download -x COPY . . FROM base AS build-client RUN --mount=type=cache,target=/go/pkg/mod/ \ go build -o /bin/client ./cmd/client FROM base AS build-server RUN --mount=type=cache,target=/go/pkg/mod/ \ go build -o /bin/server ./cmd/server FROM scratch AS client COPY --from=build-client /bin/client /bin/ ENTRYPOINT [ "/bin/client" ] FROM scratch AS server COPY --from=build-server /bin/server /bin/ ENTRYPOINT [ "/bin/server" ] ``` Similarly, you can use the same technique to remove the need for the second `COPY` instruction as well. Specify bind mounts in the `build-client` and `build-server` stages for mounting the current working directory. ```diff # syntax=docker/dockerfile:1 FROM golang:{{site.example_go_version}}-alpine AS base WORKDIR /src RUN --mount=type=cache,target=/go/pkg/mod/ \ --mount=type=bind,source=go.sum,target=go.sum \ --mount=type=bind,source=go.mod,target=go.mod \ go mod download -x - COPY . . FROM base AS build-client RUN --mount=type=cache,target=/go/pkg/mod/ \ + --mount=type=bind,target=. \ go build -o /bin/client ./cmd/client FROM base AS build-server RUN --mount=type=cache,target=/go/pkg/mod/ \ + --mount=type=bind,target=. \ go build -o /bin/server ./cmd/server FROM scratch AS client COPY --from=build-client /bin/client /bin/ ENTRYPOINT [ "/bin/client" ] FROM scratch AS server COPY --from=build-server /bin/server /bin/ ENTRYPOINT [ "/bin/server" ] ``` ## Summary This section has shown how you can improve your build speed using cache and bind mounts. Related information: - [Dockerfile reference](../../engine/reference/builder.md#run---mount) - [Bind mounts](../../storage/bind-mounts.md) ## Next steps The next section of this guide is an introduction to making your builds configurable, using build arguments. [Build arguments](build-args.md){: .button .primary-btn }