How to make a build arg mandatory during Docker build?
DockerDocker Problem Overview
Is there any way to make a build argument mandatory during docker build
? The expected behaviour would be for the build to fail if the argument is missing.
For example, for the following Dockerfile:
FROM ubuntu
ARG MY_VARIABLE
ENV MY_VARIABLE $MY_VARIABLE
RUN ...
I would like the build to fail at ARG MY_VARIABLE
when built with docker build -t my-tag .
and pass when built with docker build -t my-tag --build-arg MY_VARIABLE=my_value .
.
Is there any way to achieve that behaviour? Setting a default value doesn't really do the trick in my case.
(I'm running Docker 1.11.1
on darwin/amd64
.)
EDIT:
One way of doing that I can think of is to run a command that fails when MY_VARIABLE
is empty, e.g.:
FROM ubuntu
ARG MY_VARIABLE
RUN test -n "$MY_VARIABLE"
ENV MY_VARIABLE $MY_VARIABLE
RUN ...
but it doesn't seem to be a very idiomatic solution to the problem at hand.
Docker Solutions
Solution 1 - Docker
I tested with RUN test -n <ARGvariablename>
what @konradstrack mentioned in the original (edit) post... that seems do the job of mandating the variable to be passed as the build time argument for the docker build
command:
FROM ubuntu
ARG MY_VARIABLE
RUN test -n "$MY_VARIABLE"
ENV MY_VARIABLE $MY_VARIABLE
Solution 2 - Docker
You can also use shell parameter expansion to achieve this.
Let's say your mandatory build argument is called MANDATORY_BUILD_ARGUMENT
, and you want it to be set and non-empty, your Dockerfile could look like this:
FROM debian:stretch-slim
MAINTAINER Evel Knievel <[email protected]>
ARG MANDATORY_BUILD_ARGUMENT
RUN \
# Check for mandatory build arguments
: "${MANDATORY_BUILD_ARGUMENT:?Build argument needs to be set and non-empty.}" \
# Install libraries
&& apt-get update \
&& apt-get install -y \
cowsay \
fortune \
# Cleanup
&& apt-get clean \
&& rm -rf \
/var/lib/apt/lists/* \
/var/tmp/* \
/tmp/* \
CMD ["/bin/bash", "-c", "/usr/games/fortune | /usr/games/cowsay"]
Of course, you would also want to use the build-argument for something, unlike I did, but still, I recommend building this Dockerfile and taking it for a test-run :)
EDIT
As mentioned in @Jeffrey Wen's answer, to make sure that this errors out on a centos:7
image (and possibly others, I admittedly haven't tested this on other images than stretch-slim
):
> Ensure that you're executing the RUN command with the bash shell.
>
> RUN ["/bin/bash", "-c", ": ${MYUID:?Build argument needs to be set and not null.}"]
Solution 3 - Docker
Another simple way:
RUN test -n "$MY_VARIABLE" || (echo "MY_VARIABLE not set" && false)
Solution 4 - Docker
You could do something like this...
FROM ubuntu:14.04
ONBUILD ARG MY_VARIABLE
ONBUILD RUN if [ -z "$MY_VARIABLE" ]; then echo "NOT SET - ERROR"; exit 1; else : ; fi
Then docker build -t my_variable_base .
Then build your images based on this...
FROM my_variable_base
...
It's not super clean, but at least it abstracts the 'bleh' stuff away to the base image.
Solution 5 - Docker
Long time ago I had a need to introduce a required (mandatory) ARG
, and for better UX include the check at the beginning:
FROM ubuntu:bionic
ARG MY_ARG
RUN [ -z "$MY_ARG" ] && echo "MY_ARG is required" && exit 1 || true
...
RUN ./use-my-arg.sh
But this busts the build cache for every single layer after the initial MY_ARG
, because MY_ARG=VALUE
is prepended to every RUN
command afterwards.
Whenever I changed MY_ARG
it would end up rebuilding the whole image, instead of rerunning the last RUN
command only.
To bring caching back, I have changed my build to a multi-staged one:
- The first stage uses
MY_ARG
and checks it's presence. - The second stage proceeds as usual and declares
ARG MY_ARG
right at the end.
FROM alpine:3.11.5
ARG MY_ARG
RUN [ -z "$MY_ARG" ] && echo "MY_ARG is required" && exit 1 || true
FROM ubuntu:bionic
...
ARG MY_ARG
RUN ./use-my-arg.sh
Since ARG MY_ARG
in the second stage is declared right before it's used, all the previous steps in that stage are unaffected, thus cache properly.
Solution 6 - Docker
I cannot comment yet because I do not have 50 reputation, but I would like to add onto @Jan Nash's solution because I had a little difficulty getting it to work with my image.
If you copy/paste @Jan Nash's solution, it will work and spit out the error message that the build argument is not specified.
What I want to add
When I tried getting it to work on a CentOS 7 image (centos:7), Docker ran the RUN
command without erroring out.
Solution
Ensure that you're executing the RUN
command with the bash shell.
RUN ["/bin/bash", "-c", ": ${MYUID:?Build argument needs to be set and not null.}"]
I hope that helps for future incoming people. Otherwise, I believe @Jan Nash's solution is just brilliant.
Solution 7 - Docker
In case anybody is looking for a the solution but with docker compose build
, I used mandatory variables.
version: "3.9"
services:
my-service:
build:
context: .
args:
- ENVVAR=${ENVVAR:?See build instructions}
After running docker compose build
:
- Before exporting ENVVAR: Invalid template: "required variable ENVVAR is missing a value: See build instructions"
- After exporting ENVVAR: build proceeds
Support for Required Environment variables Compose Environment Variables