1. Overview

LXC (Linux Containers) provides a low-level interface for using kernel containerization features in the user space. Basically, LXC creates an isolated environment for applications using namespaces and control groups. It also provides command-line tools, templates, libraries, and language bindings.

Occasionally, we may need to share a directory between an LXC container and the host on which the LXC container is running.

In this tutorial, we’ll discuss how to share a directory between an LXC container and a host.

2. Preparing the Setup

In this section, we’ll prepare the setup. The Linux distro we’re working on is Ubuntu 22.04.

We need to install the lxc package to use the commands in the LXC toolset such as lxc-create and lxc-ls. Additionally, we need to install the lxc-templates package to use the template scripts.

2.1. Creating an LXC Container

We use the lxc-create command to create an LXC container:

$ sudo lxc-create -n alpine-container -t download -- -d alpine -r 3.20 -a amd64
Downloading the image index
Downloading the rootfs
Downloading the metadata
The image cache is now ready
Unpacking the rootfs

---
You just created an Alpinelinux 3.20 x86_64 (20240610_13:00) container.

As it’s apparent from the output, lxc-create downloads the image and initializes the root file system (rootfs) of the container. We use the download template by passing -t download to lxc-create. It loads prebuilt LXC images from the LXC repository.

The Linux distro in the container is Alpine 3.20. The container’s name is alpine-container, which we specify using the -n option of lxc-create.

The commands in the LXC toolset require root privileges, so we use the lxc-create command together with sudo.

We can list the containers existing in the system using the lxc-ls command:

$ sudo lxc-ls
alpine-container

The container we’ve just created, alpine-container, is within the available containers, as expected. The directories and files belonging to the container are within the /var/lib/lxc directory:

$ sudo ls -l /var/lib/lxc/alpine-container
total 8
-rw-r-----  1 root root  740 Jun 30 14:33 config
drwxr-xr-x 19 root root 4096 Jun 30 09:00 rootfs

Having created the container, it’s time to start the container.

2.2. Starting the LXC Container

Let’s now start the container using the lxc-start command:

$ sudo lxc-start -n alpine-container

We specify the container’s name using the -n option of the lxc-start command. The name is alpine-container.

We can check whether the container is running by executing commands inside the container using the lxc-attach command:

$ sudo lxc-attach -n alpine-container hostname
alpine-container

The -n option of lxc-attach specifies the name of the container we want to connect to. hostname at the end is the command we want to run in the container. The name of the running container is alpine-container, as expected.

If we don’t pass any commands to lxc-attach, then a terminal running within the container appears:

$ sudo lxc-attach -n alpine-container
/ #

We can run commands within the terminal:

$ sudo lxc-attach -n alpine-container
/ # hostname
alpine-container

Therefore, our container is up and running. We’re ready to share a directory between the container and the host.

3. Sharing a Directory

We can share a directory between the host and the container using the configuration file of the container, config, in the host:

$ sudo ls /var/lib/lxc/alpine-container/config
/var/lib/lxc/alpine-container/config

This configuration file has options in the form of key and value pairs in each line:

$ sudo cat /var/lib/lxc/alpine-container/config
# Template used to create this container: /usr/share/lxc/templates/lxc-download
# Parameters passed to the template: -a amd64 -d alpine -r 3.20
# For additional config options, please look at lxc.container.conf(5)

# Uncomment the following line to support nesting containers:
# lxc.include = /usr/share/lxc/config/nesting.conf
# (Be aware this has security implications)


# Distribution configuration
lxc.include = /usr/share/lxc/config/common.conf
lxc.arch = linux64

# Container specific configuration
lxc.rootfs.path = dir:/var/lib/lxc/alpine-container/rootfs
lxc.uts.name = alpine-container

# Network configuration lxc.net.0.type = veth lxc.net.0.link = lxcbr0 lxc.net.0.flags = up
lxc.net.0.hwaddr = 00:16:3e:1b:16:54

For example, the options starting with lxc.net specify how the network is virtualized in the container. Obviously, the lines that start with # are comments.

We’ll share a directory within the host and mount it in the container. We need to use the lxc.mount.entry option in the configuration file for this purpose. Let’s add the following setting to the configuration file:

lxc.mount.entry = /home/baeldung/work/shared_directory tmp/share none bind,create=dir

The lxc.mount.entry option mounts directories in the same format we use in /etc/fstab. The first field, /home/baeldung/work/shared_directory, is the directory in the host that we want to share with the container.

The second field, tmp/share, is the mount point in the container. We need to specify the mount point in the container relative to the container’s root directory, hence it’s tmp/share, not /tmp/share.

The third parameter specifies the type of the file system. Its value is none in our case since we don’t use a special device. Therefore, none is just a placeholder to satisfy the option’s syntax.

The fourth parameter, bind,create=dir, contains the mount options. bind means that this is a bind mount. Additionally, we use create=dir to create the mount point during mounting. Therefore, the /tmp/share directory in the container will be created automatically.

Besides these four parameters, there are two more fields, dump and pass fields, that we can specify as well. However, we don’t need to use these fields as their default values are acceptable for our needs. The default values of these two fields are 0.

4. Testing the Setup

We’ll test our setup in this section. But, let’s first stop the running container using the lxc-stop command:

$ sudo lxc-stop -n alpine-container

The usage of the lxc-stop command is similar to lxc-start, i.e., we specify the name of the container we want to stop using the -n option of the lxc-stop command.

Next, let’s create the directory we want to share using mkdir and create a file within this directory:

$ mkdir -p /home/baeldung/work/shared_directory
$ cd /home/baeldung/work/shared_directory
$ echo "Hello from host" > greeting

The name of the file we created is greeting. It contains the “Hello from host” string.

Next, we start the container again using lxc-start:

$ sudo lxc-start -n alpine-container

Let’s now attach to the running container using lxc-attach and check whether there’s a file in the /tmp/share directory:

$ sudo lxc-attach -n alpine-container
/ # cat /tmp/share/greeting
Hello from host

The file exists and contains the “Hello from host” string, as expected. Let’s modify the file within the container:

/ # echo "Hello from container" >> /tmp/share/greeting
/ # cat /tmp/share/greeting
Hello from host
Hello from container

We appended the “Hello from container” string to the file using the echo “Hello from container” >> /tmp/share/greeting command.

Let’s now check the content of the file within the host:

$ cat /home/baeldung/work/shared_directory/greeting
Hello from host
Hello from container

We can see the string appended from the container. Therefore, we’re successful in sharing a directory between an LXC container and a host.

Although we can update the files in the shared directory from the container in our setup, we may not always want the container to be able to modify the files in the shared directory for security reasons. In that case, we can mount the directory as read-only using the ro mount option.

5. Conclusion

In this article, we discussed sharing a directory between an LXC container and a host. First, we prepared the setup. The distro in the host was Ubuntu 22.04, whereas it was Alpine 3.20 in the container.

Then, we shared a directory in the host using the lxc.mount.entry option in the container’s configuration file. This option specifies the shared directory in the host and the mount point in the container, in addition to other fields like mount options.

Finally, we tested the setup and saw that we could update the files in the shared directory both from the host and the container.