3 minute read

Instead of running a full VM just to run a bunch of Docker containers, I wanted to utilize the LXC feature of Proxmox. It allows for running a full Debian system in a container that, instead of emulating the hardware of a complete virtual machine, shares hardware and kernel with the Proxmox host.

It also allows to operate containers in an unprivileged mode, increasing security by reducing the permission level. But in order for this to work there are some special preparations needed.

Note

There is one thing that won’t work in this scenario due to the lack of permissions: setting a domain name or an FQDN in a docker container. If either the domainname setting is used in the docker-compose.yml, or if the hostname setting contains an FQDN instead of a regular hostname (no periods!), the start of the container will fail with a permission error.

However, there is nothing speaking against using a hostname with an app that runs inside the container, it just can not be set as a container parameter.

Docker inside Proxmox LXC

Set up an unprivileged container in Proxmox using the latest Debian template (at the time of writing this is Debian 12 “Bookworm”).

Inside the container

The following describes the basic setup inside the container, the commands are run as root.

Configure password-less SSH login

All that’s needed to allow a key-based login is adding the respective public key to the root user’s authorized_keys file:

echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB0t4snxPv/aibNCtIujhkwVBRclKFtXKDwCG5WBANuj mcfisch@local' > /root/.ssh/authorized_keys

Now the local system’s key-store (Debian/Ubuntu keyring, Windows SSH-agent, etc.) can store the private part of the key, asking for its password once, and from there password-less SSH logins should make the following even easier, especially if one doesn’t use the Proxmox Gui to connect to the container.

Update packages

Next we update the repo list and install all package updates.

apt update && apt upgrade -y

A reboot shouldn’t be necessary as the container uses the host’s kernel, but it can help with getting the services started as supposed. It also helps with verifying that all settings are properly applied on boot.

Install additional packages

Some tools might be needed for later steps, or just to satisfy personal preferences. I use curl and vim a lot, so these are on my list. fuse-overlayfs is necessary as in an unprivileged LXC container there are some permission issues prohibiting the default docker storage driver overlayfs from working correctly. vfs could be an alternative, but it lacks the space-saving features of overlayfs and can cause a massive headache by using disk space uncontrollably. That’s where fuse-overlayfs comes in, the FUSE implementation of overlayfs.

apt install -y curl vim fuse-overlayfs

Once apt has finished, open the container’s options in Proxmox and enable FUSE under the Features setting. Restart the container.

Install Docker

Now the container is ready to get Docker. I prefer the convenience script that Docker provides. And since curl is already on the system I use it to fetch the script. For wget adjust the command accordingly (curl’s -L stands for “follow redirects”).

curl -fsSL https://get.docker.com | sh

Once the script is done the system should have the docker container service running.

Now configure the new storage driver and restart the service.

cat <<EOF >/etc/docker/daemon.json 
{
  "storage-driver": "fuse-overlayfs"
}
EOF
systemctl restart docker.service

Create regular user

In order to avoid working with the root user all the time, as even in a rootless container this is still the superuser that has all available permissions. So I create a regular user that is also allowed to maintain docker resources.

useradd -m -d /home/mcfisch -U -s /bin/bash mcfisch
usermod -aG docker mcfisch
echo 'ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIB0t4snxPv/aibNCtIujhkwVBRclKFtXKDwCG5WBANuj mcfisch@local' > /home/mcfisch/.ssh/authorized_keys
chown -R mcfisch:mcfisch /home/mcfisch/.ssh/
chown -R mcfisch:docker /docker/
chmod 700 /home/mcfisch/.ssh/
chmod 600 /home/mcfisch/.ssh/authorized_keys

The ssh key allows me to directly connect to the user without setting a password, so a password-based login will always fail.

Set up a container

Now we can begin setting up docker containers. I usually prefer docker-compose files combined with environmental variables in either .env files or stored in my shell profile.

The process usually resembles something like this:

mkdir -p /docker/grafana && cd /docker/grafana
curl -fsSL https://github.com/docker/awesome-compose/raw/master/prometheus-grafana/compose.yaml -o /docker/grafana/docker-compose.yml
docker compose -f /docker/grafana/docker-compose.yml pull
docker compose -f /docker/grafana/docker-compose.yml up -d

If all goes well this will start an instance of each Prometheus and Grafana, using the sample docker-compose file from the Docker github repo.

Happy Containerizing!

Comments