More on running containers
Different ways to run a container
As a reminder, we continue working in our ~/tmp
directory inside an interactive job with the Apptainer module loaded:
cd ~/tmp
module load apptainer
salloc --cpus-per-task=1 --time=3:00:0 --mem-per-cpu=3600 # only from the login node
If you have not done so already, let’s pull the latest Ubuntu container from Docker:
apptainer pull ubuntu.sif docker://ubuntu
We already saw some of these commands:
apptainer shell ubuntu.sif
launches the container and opens an interactive shell inside itapptainer exec ubuntu.sif <command>
launches the container and runs a command inside itapptainer run ubuntu.sif
launches the container and executes the default runscript
Apptainer matches users between the container and the host. For example, if you run a container that needs to be root, you also need to be root outside the container.
1. Running a single command
apptainer exec ubuntu.sif ls /
apptainer exec ubuntu.sif ls /; whoami
apptainer exec ubuntu.sif bash -c "ls /; whoami" # probably a safer way
apptainer exec ubuntu.sif cat /etc/os-release
2. Running a default script
We’ve already done this! If there is no default script, Apptainer will give you the shell to type in your commands.
3. Starting a shell
We’ve already done this!
$ apptainer shell ubuntu.sif
Apptainer> whoami # same username as in the host system
Apptainer> groups # tries to match groups as on the host system
At startup Apptainer simply copied the relevant user and group lines from the host system to files /etc/passwd
and /etc/group
inside the container. Why do this? The container must ensure that you cannot modify anything on the host system that you should not have permission to, i.e. you are restricted to the same user permissions within the container as you are on the host system.
Mounting external directories and copying environment
By default, Apptainer containers are read-only, so you cannot write into its directories. However, from inside the container you can organize read-write access to your directories on the host filesystem. The command
apptainer shell -B /home,/project,/scratch ubuntu.sif
will bind-mount /home,/project,/scratch
inside the container so that these directories can be accessed for both read and write, subject to your account’s permissions, and then will run a shell. Inside the container:
pwd # most likely your working directory
echo $USER
ls /home
ls /scratch
ls /project
If you prefer, you can pass the same bind information via an environment variable APPTAINER_BIND
. In fact, by default your APPTAINER_BIND
is set to /project,/scratch
, so these two directories (along with /home/$USER
) will be mounted every time. If you want to stop mounting /project
and /scratch
, unset the variable:
unset APPTAINER_BIND
apptainer shell ubuntu.sif
You can mount host directories to specific paths inside the container, e.g.
apptainer shell -B /project/def-sponsor00/$USER:/myproject,/home/$USER/scratch:/myscratch ubuntu.sif
Apptainer> ls /myproject
Apptainer> ls /myscratch
Note that by default Apptainer typically mounts some of the host’s directories (think /home/$USER
). The flag -C
will hide the host’s filesystems and environment variables, but then you need to explicitly bind-mount the needed paths (to store results), e.g.
apptainer shell -C -B /scratch ubuntu.sif # from the host see only /scratch
Apptainer> ls /home/$USER # still there, but does not contain host's files and directories
You can disable specific mounts, e.g. the following will start the container without mounting your home directory, but it’ll mount the current directory:
apptainer shell --no-mount home ubuntu.sif
Alternatively, you can disable mounting /home
with the --no-home
flag, which is equivalent to --no-mount home
. And you can disable multiple mounts with something like --no-mount tmp,sys,dev
.
In general, without -C
, Apptainer inherits all environment variables and default bind-mounted filesystems. You can add the -e
flag to remove only the host’s environment variables from your container but keep the default bind-mounted filesystems, to start in a cleaner environment:
apptainer shell ubuntu.sif
Apptainer> echo $USER $PYTHONPATH # defined from the host
Apptainer> ls /home/user01 # shows my $HOME content on the host
apptainer shell -C ubuntu.sif
Apptainer> echo $USER $PYTHONPATH # not defined
Apptainer> ls /home/user01 # nothing
apptainer shell -e ubuntu.sif
Apptainer> echo $USER $PYTHONPATH # not defined
Apptainer> ls /home/user01 # shows my $HOME content on the host
On the other hand, you can pass variables to your container by prefixing their names:
$ APPTAINERENV_HI="hello" APPTAINERENV_NAME="alex" apptainer shell ubuntu.sif
Apptainer> echo $HI
hello
Apptainer> echo $NAME
alex
Finally, we already mentioned APPTAINER_BIND
: you don’t have to pass the same bind (-B
) flags every time – instead you can put them into a variable (that can be stored in your ~/.bashrc
file):
export APPTAINER_BIND="/home,/project/def-sponsor00/${USER}:/project,/scratch/${USER}:/scratch"
apptainer shell ubuntu.sif
You can have more granular control (e.g. specifying read only) with the --mount
flag – for details see the official Bind Paths and Mounts documentation.
- Your current directory and home directory are usually available by default in a container.
- You have the same username and permissions in a container as on the host system.
- Use
-B
to mount host’s directories inside the container. - Use
-C
to hide both host’s filesystems and environment variables, perhaps while mounting only few specific directories. - Use
-e
to hide only the host’s environment variables.