How to Conditionally Copy a File in Dockerfile
Been playing with Docker images a lot lately… like a lot:
One thing I want to do when building a Docker image from a Dockerfile: only copy over a file when it is there, otherwise, skip the file.
Motivation
I want to be able to copy over certain files when building the Docker image if they exist in the current folder. Otherwise, the Docker image can take a different course of action, like generate a basic version of the optional file.
Requirements
To follow along, please install Docker.
It also helps to have a Dockerfile to test it out. If you don’t have
one, the following Dockerfile
file contents will suffice:
FROM ruby:2.5.1-slim
RUN mkdir /app
WORKDIR /app
All this does is start the ruby project and makes the working
directory /app
. We will append the COPY
command will at the end of
this Dockerfile
.
Short Answer
Specify any optional files for the COPY
command with *
along with
one real file, the COPY
command needs to copy at least one file
over, even if there is more than one optional file to copy with the
*
option.
Example
To have the COPY
command in the Dockerfile copy over another_file
,
which may or may not be there, write the command as:
COPY Dockerfile another_file* .
This will ensure the COPY
operation is successful. The Dockerfile
becomes:
FROM ruby:2.5.1-slim
RUN mkdir /app
WORKDIR /app
COPY Dockerfile another_file* .
Step 6/9 : COPY Dockerfile another_file* .
---> 434302cde3
It is vital to append *
to the optional files, otherwise, COPY
will throw an error:
Step 6/9 : COPY Dockerfile another_file .
COPY failed: stat /var/lib/docker/tmp/docker-builder068314739/another_file: no such file or directory
Details
I stumbled upon this answer when perusing the Docker
forum. It
didn’t look like anything special and I tried just COPY
another_file*
with no success.
What struck me was the answer had two arguments, one with the *
and
without. With no other solutions in sight, I tried the command again,
but exactly as typed.
Specifying a file that exists with another that may exist is exactly
the behavior I want. I got it working by running: COPY Dockerfile
another_file*
.
I found the easiest way to always this is to always copy over the
Dockerfile, which must exist. So, if I want to copy a Gemfile
for
Ruby projects or package.json
for node projects, I would specify in
the Dockerfile the command to be:
COPY Dockerfile Gemfile* package.json* .
Then building the image would result in:
Step 6/9 : COPY Dockerfile Gemfile* package.json* .
---> 9f2d0497c5a2
If Dockerfile
is not specifed, then COPY
throwns an error.
Step 6/9 : COPY Gemfile* package.json* .
COPY failed: no source files were specified
The Docker image build process stops.
Conclusion
Just by successfully copying over one file for Docker’s COPY
command, the Docker image build process will continue, even if only
one of multiple files specified are successfully copied.