COPY . . command in Dockerfile for ASP.NET

The COPY . . copies the entire project, recursively into the container for the build.

The reason for the separation of the first 2 COPY commands with dotnet restore and then the complete COPY . . with dotnet build is a Docker caching trick to speed up container image builds. It is done this way so the project dependencies don't need to be reinstalled every time a code change is made.

Docker images are built in layers. Docker compares the contents and instructions that would make up the each new layer to previous builds. If they match the SHA256 checksum for the existing layer, the build step for that layer can be skipped.

Code changes a lot more than dependencies, and dependencies are usually fetched from a slow(ish) network now. If you copy the code after the dependency installs are completed then you don't bust the cached dependency layer for every other change.

This is a common theme across many languages with a dependency manager. Go, Python, Node.js etc. The Node.js equivalent does the package.json and package-lock.json before the rest of the application contents:

WORKDIR /app
COPY package.json package-lock.json /app/
RUN npm install
COPY . /app/
CMD ["node", "app/index.js"]

Some more pointers on the above from Scott Hanselman: https://www.hanselman.com/blog/OptimizingASPNETCoreDockerImageSizes.aspx

PRO TIP: Docker is smart about making intermediate images and doing the least work, but it's useful if we (the authors) do the right thing as well to help it out.

For example, see where we COPY the .csproj over and then do a "dotnet restore"? Often you'll see folks do a "COPY . ." and then do a restore. That doesn't allow Docker to detect what's changed and you'll end up paying for the restore on EVERY BUILD.

By making this two steps - copy the project, restore, copy the code, this means your "dotnet restore" intermediate step will be cached by Docker and things will be WAY faster.