Cobbler in a Docker Container

For one of our projects we found ourselves in need of a PXE server. In order to make proper use of a PXE server you will quickly find you need to change settings in your DHCP server. So we figured we’d probably needed one of those as well. We tried to roll our own but soon enough we came across a package that does all this combined. It’s called Cobbler.

Also being somewhat biased, we would prefer to run Cobbler in a container. That comes with some challenges in itself. If you search for Cobbler on the Docker Hub, there are 10 hits. 4 of them are not automated builds, which means you can’t really tell what’s in them. None of the others seem to be using systemd, which makes it kind of hard for Cobbler to manage the services, which in turn makes it rather impossible to change the config of a running Cobbler container.

So the obvious thing to do is to roll our own. Which we did.

Building the Docker image

Docker is primarily meant to run a single process, but Cobbler consists of mulitple services that you cannot easily separate. There’s cobblerd itself, httpd for the webfrontend, tftpd for serving the PXE images, dhcpd serving ip addresses, and possibly dns for resolving hostnames. Cobbler wants control over all of these services, i.e. after you make changes to the configuration it needs to restart the services so they can pick up the changes. This is why it makes sense to put all these things in a single container, although it makes for a bit of a bloated one. Restarting services requires an init system such as systemd, and luckily the official Centos image is able to run with systemd. Details on how to use it can be found on the CentOS Docker Hub page. So the beginning of the Dockerfile will look something like this:

Next we install the EPEL repositories that contain the Cobbler packages we need. Once we add EPEL we can install Cobbler and the rest of the necessary packages.

We need to enable the services in systemd.

Then we change the tftp xinetd config file to enable the tftp service.

And we make sure the rsync file exists, or else Cobbler will fail to start.

We expose the ports we plan to use:

And we end by invoking init

Once we have declared the Dockerfile, we build it with docker build -t cobbler . and we should be good to go. But not quite.

Running the Container

We can’t just run it, we need to specify --privileged because we’re using systemd which needs elevated capabilities, like CAP_SET_FILE.

We also want to use the network stack of the host, so Cobbler will listen on and offer ip addresses on a subnet that the host is connected to. If it would use it’s own network stack Cobbler would only be able to issue addresses in the private docker subnet, which would not really make any sense because every container already gets an address from the docker engine.

Another thing we want is to save the Cobbler configuration and the imported OS distributions in between restarts of the container. For this we need to mount a bunch of volumes. Lastly we also need to mount an iso inside the container to be able to import it’s contents into Cobbler. So the resulting docker run command is gonna look like this:

Before we can run though, we need to configure some files. Get these files by starting the Cobbler container without the volumes mounted, and use docker cp to copy over at least the settings and dhcp_template files from /etc/cobbler inside the container to the host. Put them in a directory etc/cobbler under the current dir, which would be where the Dockerfile is.

Next, edit the settings file, change the value for manage_dhcp to 1 and enter the ip address where your host will be listening on in both the next_server and server variables.

In the dhcp_template file, configure the subnet details. Be sure to use a subnet that your host can actually connect to, or Cobbler will fail to start later. Debugging this is kind of hard and cost me a lot of time. There are no logs written anywhere and you can only find out what’s wrong by checking systemctl status <servicename> or journalctl -xe. Once you have the addresses sorted though you should be able to use the run command listed above.

Importing a distribution

You see we used a CentOS iso for importing into Cobbler, we mounted it on dist/centos on our host and attached that as a volume to /mnt inside the container. I found the cobbler import command fails if you use it with any other path than /mnt. The complete mount command is:

and for importing the distribution:

Checking the web interface

After you have started the container, you should be able to go to the web interface under https://localhost/cobbler_web.


Login using cobbler as the username and cobbler as the password. If the web interface doesn’t respond, exec into the container and check the systemctl status for dhcpd , cobblerd and httpd and also journalctl -xe.

Seeing Cobbler in action

For my test setup i used a Virtualbox VM to connect to the Cobbler server. I created a host-only network and attached an empty client to that. I configured the ip address of the vboxnet interface in the settings and dhcp_template files.

Last, not unimportant step is to create a system in Cobbler that actually uses the distribution you imported. Configuration is straightforward, but be sure to match the MAC-address assigned to the VM by VirtualBox.
Once the vm boots, press F12 and select the LAN boot device. You should see the VM booting into the CentOS installer.

Final thoughts

All the code from this post can be found on our Github project page. I also tried to get Docker Hub to do an automated build but due to an issue with Docker Hub (see here and here), installing httpd in a CentOS machine doesn’t work on the Docker Hub. It appears to be related to using aufs as the storage backend. For now, just build it on your local machine.

The following two tabs change content below.

Thijs Schnitger

Senior Engineer at Container Solutions
Thijs is a Systems Engineer specialized in all things Linux. At Container Solutions he mainly focuses on programmable infrastructure, when he’s not busy lending a hand. His years of experience with development, delivery and operations make him an allround application debugger and problem solver.

Latest posts by Thijs Schnitger (see all)


  1. This works quite well. Thank you.

    A few notes:
    – Please add a reference to this blog in the README. Anyone looking at the github project might have trouble figuring things out.
    – Also note that it is important to go through the settings and adjust them, especially the ip address.
    – cobbler sync won’t work because sync_post_restart_services calls “service dhcpd restart” and /sbin/service is not there. This would work if not in a container, because /sbin/service is there and automatically gets forwarded to systemctl. To me, this is a bug in cobbler, but it can be remedied by installing initscripts, although I don’t know what else will break as a consequence. Maybe it is easier to let cobbler sync crash on the calls to restart dhcpd and named, )or simply disable that in settings) and simply restart the container at that point.

    • Glad you like it! I updated the README a bit to explain things.
      On the sync issue, I think I just let it crash and restarted the dhcp and named services manually when I needed. If I need to use it again, I’ll definitely look at adding initscripts.

Leave a Reply

Your email address will not be published. Required fields are marked *