Is there a more elegant way to copy specific files using Docker COPY to the working directory? Is there a more elegant way to copy specific files using Docker COPY to the working directory? docker docker

Is there a more elegant way to copy specific files using Docker COPY to the working directory?


2021: with BuildKit, see ".NET package restore in Docker cached separately from build" from Palec.


2018: Considering that wildcard are not well-supported by COPY (moby issue 15858), you can:

  • either experiment with adding .dockerignore files in the folder you don't want to copy (while excluding folders you do want): it is cumbersome
  • or, as shown here, make a tar of all the folders you want

Here is an example, to be adapted in your case:

find .. -name '*.csproj' -o -name 'Finomial.InternalServicesCore.sln' -o -name 'nuget.config' \  | sort | tar cf dotnet-restore.tar -T - 2> /dev/null

With a Dockerfile including:

ADD docker/dotnet-restore.tar ./

The idea is: the archive gets automatically expanded with ADD.


The OP sturmstrike mentions in the comments "Optimising ASP.NET Core apps in Docker - avoiding manually copying csproj files (Part 2)" from Andrew Lock "Sock"

The alternative solution actually uses the wildcard technique I previously dismissed, but with some assumptions about your project structure, a two-stage approach, and a bit of clever bash-work to work around the wildcard limitations.

We take the flat list of csproj files, and move them back to their correct location, nested inside sub-folders of src.

# Copy the main source project filesCOPY src/*/*.csproj ./  RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done

L01nl suggests in the comments an alternative approach that doesn't require compression: "Optimising ASP.NET Core apps in Docker - avoiding manually copying csproj files", from Andrew Lock "Sock".

FROM microsoft/aspnetcore-build:2.0.6-2.1.101 AS builderWORKDIR /slnCOPY ./*.sln ./NuGet.config  ./# Copy the main source project filesCOPY src/*/*.csproj ./RUN for file in $(ls *.csproj); do mkdir -p src/${file%.*}/ && mv $file src/${file%.*}/; done# Copy the test project filesCOPY test/*/*.csproj ./RUN for file in $(ls *.csproj); do mkdir -p test/${file%.*}/ && mv $file test/${file%.*}/; doneRUN dotnet restore# Remainder of build process

This solution is much cleaner than my previous tar-based effort, as it doesn't require any external scripting, just standard docker COPY and RUN commands.
It gets around the wildcard issue by copying across csproj files in the src directory first, moving them to their correct location, and then copying across the test project files.


One other option to consider is using a multi-stage build to prefilter / prep the desired files. This is mentioned on the same moby issue 15858.

For those building on .NET Framework, you can take it a step further and leverage robocopy.

For example:

FROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS prep# Gather only artifacts necessary for NuGet restore, retaining directory structureCOPY / /temp/RUN Invoke-Expression 'robocopy C:/temp C:/nuget /s /ndl /njh /njs *.sln nuget.config *.csproj packages.config'[...]# New build stage, independent cacheFROM mcr.microsoft.com/dotnet/framework/sdk:4.8 AS build# Copy prepped NuGet artifacts, and restore as distinct layerCOPY --from=prep ./nuget ./RUN nuget restore# Copy everything else, build, etcCOPY src/ ./src/RUN msbuild[...]

The big advantage here is that there are no assumptions made about the structure of your solution. The robocopy '/s' flag will preserve any directory structure for you.

Note the '/ndl /njh /njs' flags are there just to cut down on log noise.


In addition to VonC's answer (which is correct), I am building from a Windows 10 OS and targetting Linux containers. The equivalent to the above answer using Windows and 7z (which I normally have installed anyway) is:

7z a -r -ttar my_project_files.tar .\*.csproj .\*.sln .\*nuget.config

followed by the ADD in the Dockerfile to decompress.

Be aware that after installing 7-zip, you will need to add the installation folder to your environment path to call it in the above fashion.

Looking at the moby issue 15858, you will see the execution of the BASH script to generate the tar file and then the subsequent execution of the Dockerfile using ADD to extract.

Fully automate either with a batch or use the Powershell execution as given in the below example.

Pass PowerShell variables to Docker commands