Passing docker-compose args to ENTRYPOINT script - docker

My docker-compose.yml looks like this:
...
build:
context: myimg
args:
- FOO=bar
And my dockerfile does this:
...
ARG FOO
ENTRYPOINT run.sh
However, my script can't see the FOO var...
Inside run.sh:
echo $FOO
Outputs nothing. I have tried several combinations of the shell and exec forms and passing the vars as env vars but nothing works. I need to pass them down as env vars, not params.

args: only affect building the initial image, and the initial image build isn't aware of any of the rest of the context you might define in your docker-compose.yml file.
If you're hoping to use this for runtime configuration, an environment: setting will be better. Unlike build-time arguments, you won't have to rebuild the image just to change the environment variable setting. Remove the ARG line from the Dockerfile and change the docker-compose.yml file to say
build:
context: myimg
environment:
- FOO=bar
If you want an ARG value to be visible as an environment variable later on (for instance in an ENTRYPOINT script) you need to add an ENV setting for it as well; there are a couple of examples in the Dockerfile documentation. This might look like
FROM ...
ARG FOO
ENV FOO=$FOO
COPY run.sh /
ENTRYPOINT ["/run.sh"]

Related

How can I pass ENV value in Dockerfile from .env file?

My directory structure looks like this.
|
|
--- Dockerfile
| --- .env
Content of .env file looks like this.
VERSION=1.2.0
DATE=2022-05-10
I want to access VERSION and DATE as environment variable both during build time and run time. So ENV is the one I should use. I know that.
How exactly can I do that ?
I tried using RUN command in Dockerfile like
RUN export $(cat .env)
But, it can only be accessed during runtime and not build time.
So, how can this be achieved with ENV ?
I can do it manually like
ENV VERSION 1.2.0
ENV DATE 2022-05-10
But, it is inefficient when I have many environment variables.
P.S. I cannot use docker-compose because the image is going to be used by kubernetes pods, so.
You could firstly export these variables as environmetal variables to your bash
source .env
Then use --build-arg flag to pass them to your docker build
docker image build --build-arg VERSION=$VERSION --build-arg DATE=$DATE .
Next in your dockerfile
ARG VERSION
ARG DATE
ENV version=$VERSION
ENV date=$DATE
As a result, for example you can access the variables in your build phase as VERSION and in your container as version
What I've read from others says that there is no "docker build --env-file...." option/apparatus. As such, this situation makes a good argument for shifting more of the content of the dockerfile to a shell script that the dockerfile simply copies and runs, as you can source the .env file that way.
greetings.sh
#!/bin/sh
source variables.env
echo Foo $copyFileTest
variables.env
export copyFileTest="Bar"
dockerfile
FROM alpine:latest
COPY variables.env .
RUN source variables.env && echo Foo $copyFileTest #outputs: Foo Bar
COPY greetings.sh .
RUN chmod +x /greetings.sh
RUN /greetings.sh #outputs: Foo Bar
RUN echo $copyFileTest #does not work, outputs nothing
You can specify the env_file in the docker-compose.dev.yml file as follows:
# docker-compose.dev.yml
services:
app:
...
env_file:
- .env.development
and you have to have a .env.development file containing all the environment variables you need to pass to the docker image.
e.g.:
# .env.development
REACT_APP_API_URL="https://some-api-url.com/api/graphql"

How to pass ENV arguments in my Dockerfile when using docker-compose

I have a docker-compose.yml that has a section:
myservice:
env_file:
- myvars.env
My env variable file has:
myvars.env:
SOME_VAL=123
And then in my Dockerfile I have this:
..
RUN echo "some_val ${SOME_VAL}"
ENTRYPOINT bash ${APP_BASE}/run.sh SOME_VAL=${SOME_VAL}
When I run docker-compose up, the value of some_val is empty.
Why is SOME_VAL not accessible in my dockerfile?
How do I pass the env variable SOME_VAL to my run.sh script?
You need to declare the variable with ENV in the Dockerfile before using it:
ENV variables are also available during the build, as soon as you introduce them with an ENV instruction.
Dockerfile
ENV SOME_VAL
RUN echo "some_val ${SOME_VAL}"
ENTRYPOINT bash ${APP_BASE}/run.sh SOME_VAL=${SOME_VAL}
When you docker-compose build an image, it only considers the build: sections of the docker-compose.yml file. Nothing else from any other part of the file is considered. environment: and env_file: settings aren't available, nor are volumes: nor networks:. The only way to pass settings in is through the Dockerfile ARG and the corresponding Compose args: settings, and even then, you only want to use this for things you'd "compile in" to the image.
Conveniently, shell scripts can directly access environment variables already, so you don't need to do anything at all; just use the variable.
#!/bin/sh
# run.sh
echo "SOME_VAL is $SOME_VAL"
# Dockerfile
FROM busybox
WORKDIR /app
COPY run.sh .
# RUN chmod +x run.sh
CMD ["./run.sh"]
# docker-compose.yml
version: '3.8'
services:
echoer:
build: .
env_file:
- my_vars.env
# environment:
# SOME_VAL: foo

Why can't I load environment variables in dockerfile? [duplicate]

I'm building a container for a ruby app. My app's configuration is contained within environment variables (loaded inside the app with dotenv).
One of those configuration variables is the public ip of the app, which is used internally to make links.
I need to add a dnsmasq entry pointing this ip to 127.0.0.1 inside the container, so it can fetch the app's links as if it were not containerized.
I'm therefore trying to set an ENV in my Dockerfile which would pass an environment variable to the container.
I tried a few things.
ENV REQUEST_DOMAIN $REQUEST_DOMAIN
ENV REQUEST_DOMAIN `REQUEST_DOMAIN`
Everything passes the "REQUEST_DOMAIN" string instead of the value of the environment variable though.
Is there a way to pass environment variables values from the host machine to the container?
You should use the ARG directive in your Dockerfile which is meant for this purpose.
The ARG instruction defines a variable that users can pass at build-time to the builder with the docker build command using the --build-arg <varname>=<value> flag.
So your Dockerfile will have this line:
ARG request_domain
or if you'd prefer a default value:
ARG request_domain=127.0.0.1
Now you can reference this variable inside your Dockerfile:
ENV request_domain=$request_domain
then you will build your container like so:
$ docker build --build-arg request_domain=mydomain Dockerfile
Note 1: Your image will not build if you have referenced an ARG in your Dockerfile but excluded it in --build-arg.
Note 2: If a user specifies a build argument that was not defined in the Dockerfile, the build outputs a warning:
[Warning] One or more build-args [foo] were not consumed.
So you can do:
cat Dockerfile | envsubst | docker build -t my-target -
Then have a Dockerfile with something like:
ENV MY_ENV_VAR $MY_ENV_VAR
I guess there might be a problem with some special characters, but this works for most cases at least.
This is for those looking to pass env variable from docker-compose using .env file to dockerfile during build and then pass those args as env variable to container.
Typical docker-compose file
services:
web:
build:
context: ./api
dockerfile: Dockerfile
args:
- SECRET_KEY=$SECRET_KEY
- DATABASE_URL=$DATABASE_URL
- AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID
Pass the env variable present in .env file to args in build command.
Typical .env file
SECRET_KEY=blahblah
DATABASE_URL=dburl
Now when you run docker-compose up -d command, docker-compose file takes values from .env file then pass it to docker-compose file. Now Dockerfile of web containes all those varibales through args during build. Now typical dockerfile of web,
FROM python:3.6-alpine
ARG SECRET_KEY
ARG DATABASE_URL
ARG AWS_ACCESS_KEY_ID
ARG AWS_SECRET_ACCESS_KEY
ARG AWS_BUCKET
ARG AWS_REGION
ARG CLOUDFRONT_DOMAIN
ENV CELERY_BROKER_URL redis://redis:6379/0
ENV CELERY_RESULT_BACKEND redis://redis:6379/0
ENV C_FORCE_ROOT true
ENV SECRET_KEY ${SECRET_KEY?secretkeynotset}
ENV DATABASE_URL ${DATABASE_URL?envdberror}
Now we recieved those secret_key and db url as arg in dokcerfile. Now let's use those in ENV as ENV SECRET_KEY ${SECRET_KEY?secretkeynotset}. Now even docker container has those variables in it's environment.
Remember not to use ARG $SECRET_KEY(which I did). It should be ARG SECRET_KEY
An alternative using envsubst without losing the ability to use commands like COPY or ADD, and without using intermediate files would be to use Bash's Process Substitution:
docker build -f <(envsubst < Dockerfile) -t my-target .
Load environment variables from a file you create at runtime.
export MYVAR="my_var_outside"
cat > build/env.sh <<EOF
MYVAR=${MYVAR}
EOF
... then in the Dockerfile
ADD build /build
RUN /build/test.sh
where test.sh loads MYVAR from env.sh
#!/bin/bash
. /build/env.sh
echo $MYVAR > /tmp/testfile
If you just want to find and replace all environment variables ($ExampleEnvVar) in a Dockerfile then build it this would work:
envsubst < /path/to/Dockerfile | docker build -t myDockerImage . -f -
When using build-arg...
docker build --build-arg CODE_VERSION=1.2 Dockerfile
...consider that the variable is not availabe after FROM:
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
An ARG declared before a FROM is outside of a build stage, so it can’t be used in any instruction after a FROM.
Generally ARGs should be placed after FROM if not required during FROM:
FROM base:xy
ARG ABC=123
To use the default value of an ARG declared before the first FROM use an ARG instruction without a value inside of a build stage:
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version
https://docs.docker.com/engine/reference/builder/#understand-how-arg-and-from-interact
add -e key for passing environment variables to container.
example:
$ MYSQLHOSTIP=$(sudo docker inspect -format="{{ .NetworkSettings.IPAddress }}" $MYSQL_CONRAINER_ID)
$ sudo docker run -e DBIP=$MYSQLHOSTIP -i -t myimage /bin/bash
root#87f235949a13:/# echo $DBIP
172.17.0.2

windows docker-compose args not working

I'm trying to use args in a docker compose file.
The docker-compose file:
version: '3'
services:
service1:
image: test
restart: always
build:
context: C:/ProgramData/
dockerfile: Dockerfile
args:
entry: 1
volumes:
- C:/ProgramData/test
The Dockerfile:
FROM microsoft/dotnet-framework:3.5
ARG entry
WORKDIR C:\\test
ADD ["/bin/x86/Release/","C:/test/"]
ENTRYPOINT ["C:\\test\\file.exe", ${entry}]
I don't know how exactly works the syntax in the docker file. How I have to put the arg in the ENTRYPOINT?
You cannot use ARG in ENTRYPOINT (at least not directly). See How to pass ARG value to ENTRYPOINT?:
Both ARG and ENV are not expanded in ENTRYPOINT or CMD. (https://docs.docker.com/engine/reference/builder/#environment-replacement) However, because ENVs are passed in as part of the environment, they are available at run time, so the shell can expand them. (This means you can't use the array form of ENTRYPOINT or CMD.)
I solved this issue by changing the Dockerfile as follows:
FROM microsoft/dotnet-framework:3.5
ARG ENTRY
ENV my_env=$ENTRY
#RUN echo %ENTRY%
#RUN echo %my_env%
WORKDIR C:\\test
ADD ["/bin/x86/Release/","C:/test/"]
ENTRYPOINT C:/test/file.exe %my_env%

Pass host environment variables to dockerfile

How can I pass a host environment variable (like user and hostname) to a dockerfile?
For example, if my username is taha:
echo $USER
taha
How do I write my Docker file to get the same output?
FROM centos:centos7
ARG myuser=$USER
CMD echo $myuser
I was experiencing the same issue. My solution was to provide the variable inside of a docker-compose.yml because yml supports the use of environment variables.
In my opinion this is the most efficient way for me because I didn't like typing it over and over again in the command line using something like docker run -e myuser=$USER . . .
Declaring ENV myuser=$USER will NOT work, in the container, $myuser will be set to null.
So your docker-compose.yml could look something like this:
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
environment:
- "myuser=${USER}"
and can be run with the short command docker-compose up
To check that the variable has been applied, run docker exec -it container-name printenv to list all variables in the container.
When you start your docker container you can pass environment variables using the -e option like so:
docker run -it <image> -e USER=$USER /bin/bash
I had a similar use-case where I wanted to be able to specify a version for the image. This has a slight extra requirement that you must specify the ARG before the FROM.
First we specify IMAGE_VERSION in the Dockerfile, as per the question I'm also including a USER arg that we can pass in too:
# give it a default of latest
# declare the ARG before FROM
ARG IMAGE_VERSION=latest
FROM centos:${IMAGE_VERSION}
ARG myuser
CMD echo $myuser
Then, as from the Docker compose docs on args:
Add build arguments, which are environment variables accessible only during the build process.
You can add these in your docker-compose.yml for example:
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
"myuser=${USER}"
IMAGE_VERSION
Then as long as you have IMAGE_VERSION set in your environment it will be passed through.
you need to use the ENV setting in your dockerfile
ENV myuser=$USER
https://docs.docker.com/engine/reference/builder/#env

Resources