17

I'm trying to run a javascript app on localhost:8000 using docker. Part of what I would like to do is swap out some config files based on the docker run command, I'd like to pass an environment variable into the container so that the bash script can use that as a parameter.

What my dockerfile is looking like is this:

FROM nginx
COPY . /usr/share/nginx/html
CMD ["bash","/usr/share/nginx/html/runfile.sh"]

And the bash script looks like this:

#!/bin/bash
if [ "$SECURITY_VERSION" = "OPENAM" ]; then
    sed -i -e 's/localhost/openam/g' authConfig.js
fi

docker run -p 8000:80 missioncontrol:latest -e SECURITY_VERSION="TEST"

Docker gives me an exception saying -e exec command not found.

However if I change the dockerfile to use ENTRYPOINT instead of CMD, the -e flag works but the webserver does not start up.

Is there something I'm missing here? Is the ENTRYPOINT being overriden or something?

EDIT:

So I've updated my dockerfile to use ENTRYPOINT ["bash","/usr/share/nginx/html/runfile.sh", ";", " nginx -g daemon off;"]

But the docker container still shuts down. Is there something I'm missing?

4
  • 1
    You probably don't even want the -e really. You also probably want RUN rather than CMD, so RUN /usr/share/nginx/html/runfile.sh? Commented Nov 15, 2016 at 11:01
  • But I would like to pass a value from the docker run command, is there a better way than -e? I had considered docker run -p 8000:80 missioncontrol:latest /bin/bash -c "/usr/share/nginx/html/runfile.sh OPENAM" But the web server didn't start then Commented Nov 15, 2016 at 11:05
  • I was talking about the sed command. :P Commented Nov 15, 2016 at 11:08
  • Oh yeah, yeah maybe I don't need -e there Thanks Commented Nov 15, 2016 at 11:13

6 Answers 6

28

NGINX 1.19 has a folder /docker-entrypoint.d on the root where place startup scripts executed by thedocker-entrypoint.sh script. You can also read the execution on the log.

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration

/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/

/docker-entrypoint.sh: Launching

[..........]

/docker-entrypoint.sh: Configuration complete; ready for start up

Sign up to request clarification or add additional context in comments.

3 Comments

It helped me because I realized that docker-entrypoint.sh which you can find on GitHub github.com/nginxinc/docker-nginx/blob/master/stable/alpine/… is there only since NGINX 1.19. I used an older version before.
Thank you! This was exactly what I was searchong for.
Thanks mate. You saved my day after I lost two. Was struggling with custom entrypoint, which would basically overwrite the nginx one.
5

NginX image has docker-entrypoint.d included and on container start will look for any scripts located in there. You can add your custom scripts during docker build. I also found that if you are using alpine image, bash is not installed, so you can add it yourself by running:

RUN apk update
RUN apk upgrade
RUN apk add bash

sample DockerFile:

FROM nginx:alpine

EXPOSE 443
EXPOSE 80

RUN apk update
RUN apk upgrade
RUN apk add bash

COPY ["my-script.sh", "/docker-entrypoint.d/my-script.sh"]
RUN chown nginx:nginx /docker-entrypoint.d/my-script.sh

USER nginx

In order to limit scope execution of your custom script script, it's highly recommended to run your container as a non-privileged user.

Comments

4

For my future self and everybody else, this is how you can set up variable substitution at startup (for nginx, may also work for other images):

I've also wrote a more in depth blog post about it: https://danielhabenicht.github.io/docker/angular/2019/02/06/angular-nginx-runtime-variables.html

Dockerfile:

FROM nginx

ENV TEST="Hello variable"

WORKDIR /etc/nginx
COPY ./substituteEnv.sh ./substituteEnv.sh

# Execute the subsitution script and pass the path of the file to replace
ENTRYPOINT ["./substituteEnv.sh", "/usr/share/nginx/html/index.html"]
CMD ["nginx", "-g", "daemon off;"]

subsitute.sh: (same as @Daniel West's answer)

#!/bin/bash

if [[ -z $1 ]]; then
    echo 'ERROR: No target file given.'
    exit 1
fi

#Substitute all environment variables defined in the file given as argument
envsubst '\$TEST \$UPSTREAM_CONTAINER \$UPSTREAM_PORT' < $1 > $1

# Execute all other paramters
exec "${@:2}"

Now you can run docker run -e TEST="set at command line" -it <image_name>

The catch was the WORKDIR, without it the nginx command wouldn't be executed. If you want to apply this to other containers be sure to set the WORKDIR accordingly.

If you want to do the substitution recursivly in multiple files this is the bash script you are looking for:

# Substitutes all given environment variables
variables=( TEST )
if [[ -z $1 ]]; then
    echo 'ERROR: No target file or directory given.'
    exit 1
  fi

for i in "${variables[@]}"
do
  if [[ -z ${!i} ]]; then
    echo 'ERROR: Variable "'$i'" not defined.'
    exit 1
  fi
  echo $i ${!i} $1
  # Variables to be replaced should have the format: ${TEST}
  grep -rl $i $1 | xargs sed -i "s/\${$i}/${!i}/Ig"
done

exec "${@:2}"

Comments

3

I know this is late but I found this thread while searching for a solution so thought I'd share.

I had the same issue. Your ENTRYPOINT script should also include exec "$@"

#!/bin/sh
set -e
envsubst '\$CORS_HOST \$UPSTREAM_CONTAINER \$UPSTREAM_PORT' < /srv/api/default.conf > /etc/nginx/conf.d/default.conf
exec "$@"

That will mean the startup CMD from the nginx:alpine container will run. The above script will inject the specified environment variables into a config file. By doing this in runtime yo can override the environment variables.

Comments

2

Update the CMD line as below in the your dockerfile. Please note that if runfile.sh does not succeed (exit 0; inside it) then the next nginx command will not be executed.

FROM nginx
COPY . /usr/share/nginx/html
CMD /usr/share/nginx/html/runfile.sh && nginx -g 'daemon off;'

nginx docker file is using a CMD commnd to start the server on the base image you use. When you use the CMD command in your dockerfile you overwrite the one in their image. As it is mentioned in the dockerfile documentation:

There can only be one CMD instruction in a Dockerfile. If you list more than one CMD then only the last CMD will take effect.

Comments

1

nginx container already defines ENTRYPOINT. If you define also CMD it will combine them both like 'ENTRYPOINT CMD' in such way that CMD becomes argument of ENTRYPOINT. That is why you need to redefine ENTRYPOINT to get it working.

Usually ENTRYPOINT is defined in such way, that if you also pass CMD, it will be executed by ENTRYPOINT script. However this might not be case with every container.

4 Comments

Yes this was my suspicion, I would probably need to look at the nginx documentation to see if I can find the ENTRYPOINT they use
So I've updated my dockerfile to use ENTRYPOINT ["bash","/usr/share/nginx/html/runfile.sh", ";", " nginx -g daemon off;"] But the docker container still shuts down. Is there something I'm missing?
I'm using FROM nginx:1.15.1-alpine. I need to run a sh script when on docker run... using some env variables defined in the compose file but i cannot find the right syntax. I tried overriding the entrypoint but didn't work. If I run the same script after the container started w/ the default CMD, it works. PS: I had to remove #!/bin/sh form the script file to be able to execute it on the running container
@Crixo removing "#!/bin/sh" helped me as well, thanks!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.