The title pretty much says it all (but maybe there’s something more sinister in play.) I’m aware of /etc/profile.d/*.sh
scripts that run when you login but that doesn’t seem to work as expected when you simply start a shell. Here’s an example with docker:
Create a small script :
$ echo "echo hello world" > startup.sh
Run the alpine shell with the script mounted like so:
$ docker run -it --rm -v `pwd`/startup.sh:/etc/profile.d/statup.sh alpine sh
All you’ll get is a shell prompt:
/ #
It does print "hello world" when you su -
from here though:
/ # su -
hello world
2bf679a5677d:~#
But a simple sh
to start a shell will not do:
/ # sh
/ #
So, what’s the issue here? Is it an obscure Alpine Linux thing? Is it an obscure Alpine Linux Docker image thing?
2
Answers
Dockerfile
RUN
commands and the main containerCMD
don’t run any shell dotfiles, ever, on any shell, on any base Linux distribution. This is true even if your base image includes GNU bash or you’ve manually reset theSHELL
to run bash instead of standardsh
.The GNU Bash Reference Manual probably has the best description of when dotfiles get read. There are three cases: a shell can be a non-interactive shell; it can be an interactive shell but not a login shell; or it can be a login shell specifically. In a Docker container, the shells you encounter usually aren’t interactive shells, and if they are, they aren’t login shells.
The important corollary to this is that writing shell dotfiles in Dockerfiles at all is usually incorrect. Most paths to running commands in containers won’t read them. If you need to set an environment variable, use an
ENV
directive instead.Some examples:
This exec form
CMD
doesn’t run a shell at all. There is nosh
process, and nothing will ever read any shell dotfiles.This shell form
CMD
is automatically wrapped in/bin/sh -c '...'
, but the shell that produces is a non-interactive shell, and doesn’t read dotfiles.Again, this doesn’t run a shell.
This explicitly supplies a shell as part of the command string, but it’s not an interactive shell.
The main container command is a shell, and you haven’t given it a command, and you have provided a stdin; so in this case it’s an interactive shell. If it were bash it would read
.bashrc
but not any other dotfiles.The interactive shell is only a login shell if you explicitly request it. In pretty much only this invocation, the full set of shell dotfiles will get read.
/etc/profile.d
on its own isn’t a standard shell feature. It’s a convenience provided by some distributions, but not something specifically mentioned in the bash manual. Compare for exampleNotice that, in the first three cases, the
/etc/profile
files are different, but all of them contain logic to read an/etc/profile.d
directory. Your invocation isn’t running a login shell so that file isn’t getting read.In the normal case, the thing your container is running won’t be a shell. Prebuilt images like
docker run postgres
ordocker run nginx
are typical examples: there is some specific piece of software packaged in the image, and running the container runs exactly that single piece of software. Starting an Nginx HTTP server doesn’t require a shell and doesn’t read shell dotfiles.As
man ash
tells you in the Invocation section, the environment variableENV
can be used to specify a file to source during shell startup. This applies even to noninteractive, non-login shells.In your Dockerfile:
…and then your shell will execute the contents of
/home/youruser/.rc
during startup.This is not specifically an
ash
behavior or an Alpine behavior; it’s true of all POSIX sh implementations. (bash usesBASH_ENV
instead except when running in sh compatibility mode, at which point it too honorsENV
).