Today, I encountered an unexpected situation when copying a folder using COPY while writing a dockerfile. I’ll document the correct way to use it here.

Background Notes

Today, when I copied the folder to the image via dockerfile, I found that the contents of the folder were copied in.

The dockerfile looks like this.

1
2
3
4
5
FROM node:alpine
WORKDIR /usr/src/app
COPY dist node_modules package.json ./
EXPOSE 3000
CMD ["yarn", "start:prod"]

I wanted to copy both dist and node_modules folders into the mirror, but I didn’t want to use multiple COPYs to copy them separately, which would result in an extra layer, and found that dist and node_modules folders themselves were not copied into the mirror, but the contents of the folders were copied into the mirror separately.

After testing, I found that.

  • the ADD command and the COPY command behave the same when copying files
  • If the source of the COPY/ADD command is a folder, the contents of the folder are copied instead of the folder itself
  • Using * to match all files will keep the above logic if a folder is encountered, i.e. only the contents will be copied

This logic is bizarre and not what we would normally expect.

I found out that someone already asked a similar question six years ago.

Implementation methods

The following lists a few matter ways, you can refer to use.

Single folder copy, specify the target directory

One way is to copy one folder at a time, and then COPY it by specifying the specific directory in the image, for example by changing the dockerfile above to something like this.

1
2
3
4
5
6
7
FROM node:alpine
WORKDIR /usr/src/app
COPY dist ./dist
COPY node_modules ./node_modules
COPY package.json ./
EXPOSE 3000
CMD ["yarn", "start:prod"]

Put it in another folder and copy it uniformly

The above way of writing is cumbersome and increases the number of layers. Here is another way that is not very elegant.

We put all the folders we need to copy into a single folder, and then copy the folder in dockerfile, so that the directory structure under the folder can be maintained.

1
mkdir dockerPackages && mv dist node_modules dockerPackages
1
2
3
4
5
FROM node:alpine
WORKDIR /usr/src/app
COPY dockerPackages package.json ./
EXPOSE 3000
CMD ["yarn", "start:prod"]

Using the .dockerignore file

What we wrote above is really like accomplishing one thing, which is to copy only some of the content into the image and then ignore the rest. This can be done more elegantly by using the .dockerignore file. Ignore all files first, and then exclude the files we need to copy.

.dockerignore :

1
2
3
4
*
!dist
!node_modules
!package.json
1
2
3
4
5
FROM node:alpine
WORKDIR /usr/src/app
COPY . ./
EXPOSE 3000
CMD ["yarn", "start:prod"]

Reference https://www.iszy.cc/posts/13/