In Dockerfile, COPY all contents of current directory except one directory

Viewed 1682

In my Dockerfile, I have the following:

COPY . /var/task

...which copies my app code into the image.

I need to exclude the vendor/ directory when performing this copy.

  • I cannot add vendor/ to .dockerignore, because that directory needs to be part of the image when it gets built within the image with a RUN composer install.
  • I cannot specify every file and directory that should be copied, because they may change and I can't rely on other developers to keep the list updated.

I've tried the following, with the following errors:

COPY [^vendor$]* /var/task

When using COPY with more than one source file, the destination must be a directory and end with a /

COPY [^vendor$]*/ /var/task

COPY failed: no source files were specified

2 Answers

It is actually enough to add the vendor directory to the .dockerignore file.

You can broadly follow the flow of files through docker build in three phases:

  1. docker build reads files from the directory you name, ignoring things in the .dockerignore file, and sends them to the Docker daemon as the build context.
  2. The COPY instruction copies files from the build context into the container filesystem.
  3. RUN instructions do further transformation or processing.

If you put vendor in the .dockerignore file, it prevents the directory from being included in the build context. The build will go somewhat faster, and COPY won't have the files to copy into the image. It won't prevent a RUN composer install step later on from creating its own vendor directory in the image.

I don't think there is an easy solution to this problem.

If you need vendor for RUN composer install and you're not using a multistage build then it doesn't matter if you remove the vendor folder in the copy command. If you've copied it into the build earlier then it's going to be present in your final image, even if you don't copy it over in your COPY step.

One way to get around this is with multi-stage builds, like so:

FROM debian as base
COPY . /var/task/
RUN rm -rf /var/task/vendor
FROM debian
COPY --from=base /var/task /var/task

If you can use this pattern in your larger build file then the final image will contain all the files in your working directory except vendor.

There's still a performance hit though. You're still going to have to copy the entire vendor directory into the build, and depending on what docker features you're using that will still take a long time. But if you need it for composer install then there's really no way around this.

Related