Whenever automating something while working with Docker containers, you might want to execute some script inside one of your containers.

And then the fun starts.

All the documentation online will tell you that you have to run something like:

docker compose exec [container] [command]

Which is fun and all, but it isn’t anywhere close to something like:

docker compose exec [container]
$ [command]

Why?

Because the .profile and .bashrc files are not loaded when you run the command in a one-liner, but they are executed when you start bash by itself.

You might also read about adding --login or -l to the bash command. They will also tell you that you need to add -it to the command line to make it an interactive shell.

Still won’t work.

You can easily check this with these two commands:

First, check all the one-liners:

docker compose exec [container] /bin/bash -c "env"
docker compose exec [container] env
docker compose exec -ti [container] /bin/bash -c "env"
docker compose exec -ti [container] env
docker compose exec -ti [container] /bin/sh -c "env"
docker compose exec -ti [container] /bin/bash -l -c "env"
docker compose exec -ti [container] /bin/bash --login -c "env"

You will see only a few environment variables.

And then, do:

docker compose exec [container] /bin/bash
$ env

You’ll see way more environment variables.

This means that the full path, NVM and other environment parts are only set up when you start it manually.

So why is this happening and how to fix this?

If you look at the .bashrc file, you will see it starts with:

# If not running interactively, don't do anything
[ -z "$PS1" ] && return

Guess what? The -i doesn’t define the PS1 variable. It doesn’t even work if you pass it along:

docker composer exec -e PS1=true [container] /bin/bash -c "env"

You will see it in the output, but it hasn’t run the .bashrc file, probably because the environment is injected afterwards.

So how to fix this?

Simple:

docker-compose exec [container] /bin/bash -c "export PS1=true && source ~/.profile && [command]"

Obviously, replace [container] with your container name and [command] with your desired command.

This defines the PS1, then loads the .profile which, in turn, loads the .bashrc script which has the PS1 defined, so it will process it.

Finally, it will run your [command].