Cobbler (v3.3.7) Ubuntu Deployment Guide

This guide assumes that you have a Fedora 34 server or workstation system configured to run Cobbler v3.3.7 similarly to the Cobbler v3.3.7 Beginner’s guide.

Table of Contents

  1. Objective
    1. Caveats
  2. Cobbler Server Prep
    1. Dependencies
  3. Ubuntu 20.04 PXE Deployment
  4. Ubuntu 22.04 PXE Deployment
  5. Ubuntu 24.04 PXE Deployment
  6. Tips & Troubleshooting

Objective

Starting where the Beginner’s guide left off, further configure the Cobbler v3.3.7 server to deploy the latest 3 releases of Ubuntu Server LTS (Ubuntu 20.04, 22.04, 24.04) via PXE network boot, using the same system and network environment. This guide assumes that you still have selinux and firewalld configured and enabled as described in the Beginner’s guide.

Caveats

  • Until the release of Ubuntu 20.04, Canonical used the same debian-installer from Debian, including the “preseed” autoinstallation model; starting with Ubuntu 20.04, Canonical switched to using their in-house cloud-init solution.

    • Ubuntu did offer compatability/support with the debian-installer/preseed autoinstallation model in the Ubuntu 20.04 Legacy release.

    • This guide only provides procedures for deploying Ubuntu systems using cloud-init.

Cobbler Server Prep

Cobbler v3.3.7 does not come with a sample template for the cloud-init autoinstall files, so create that before getting started, as well as some Cobbler sync-triggers to correct the bootloader configurations for compatability with cloud-init.

Dependencies

Create the cloud-init autoinstall template with the following contents and save it to /var/lib/cobbler/templates/cloud-init_user-data:

Cobbler Templates and Snippets are actually Python Cheetah templates, so be careful editing the files, especially the cheetah code preceded buy #

#cloud-config
autoinstall:
  version: 1
  apt:
    # even set to no/false, geoip lookup still happens
    #geoip: no
    preserve_sources_list: true
    primary:
    - arches: [amd64, i386]
      uri: http://$http_server/cblr/links/$distro
#      uri: http://us.archive.ubuntu.com/ubuntu
    - arches: [default]
#      uri: http://$http_server/cblr/links/$distro
      uri: http://ports.ubuntu.com/ubuntu-ports
    fallback: offline-install
  identity:
$SNIPPET('cloud-init_hostname')
    password: $default_password_crypted
    realname: ubuntu
    username: ubuntu
  kernel:
    package: linux-generic
  keyboard:
    layout: us
    toggle: null
    variant: ''
  locale: en_US.UTF-8
$SNIPPET('cloud-init_network')
  ssh:
    allow-pw: true
    install-server: true
  storage:
    layout:
      name: lvm
      sizing-policy: all
  package_update: false
  package_upgrade: false
  late-commands:
## Figure out if we're automating OS installation for a system or a profile
#if $getVar('system_name','') != ''
#set $what = "system"
#else
#set $what = "profile"
#end if
    - wget -O /target/tmp/autoinstall-user-data.yaml http://$http_server/cblr/svc/op/autoinstall/$what/$name
    - chroot /target /bin/bash -s ssh-keygen -t rsa -b 2048 -m ssh2 -N "" -f /root/.ssh/id_rsa
#    - rm /target/etc/apt/apt.conf.d/99needrestart # This prompt will prevent the apt command from completing

the fallback: offline option under the apt configuration will use the live CD (.iso file) as the source to install Ubuntu from if there is an issue connecting to a remote repo mirror, and was only introduced as of Ubuntu 22.04 release.

The above autoinstall template calls for 2 Cobbler snippets; create them with the following contents, in the file system location noted above the code-block:

  • /var/lib/cobbler/snippets/cloud-init_hostname
## start of cobbler network_config generated code
#if $getVar("system_name","") != ""
    #if $hostname != ""
    hostname: $hostname
    #else
    #set $myhostname = $getVar('name','').replace("_","-")
    hostname: $myhostname
    #end if
#else
## profile based install so just provide one interface for starters
#set $myhostname = $getVar('hostname',$getVar('name','cobbler')).replace("_","-")
    hostname: $myhostname
#end if
  • /var/lib/cobbler/snippets/cloud-init_network
## start of cobbler network_config generated code
#if $getVar("system_name","") != ""
  network:
    ethernets:
    #set ikeys = $interfaces.keys()
    #import re
    #for $iname in $ikeys
        #set $idata = $interfaces[$iname]
        ## Ignore BMC interface
        #if $idata["interface_type"].lower() == "bmc"
            #continue
        #end if
    #end for
    #for $iname in $ikeys
        #set $idata    = $interfaces[$iname]
        #set $mac      = $idata["mac_address"]
        #set $static   = $idata["static"]
        #set $ip       = $idata["ip_address"]
        #set $netmask  = $idata["netmask"]
        #set $type     = $idata["interface_type"]
        ## Ignore BMC interface
        #if $type == "bmc"
            #continue
        #end if
        #if $iname != "default":
            #if $mac != "":
      $iname:
        match:
          macaddress: $mac
                #if $static == True:
                    #if $ip != "":
                        #if $netmask != "":
                            #set $mask = sum([bin(int(x)).count('1') for x in $netmask.split('.')])
        dhcp4: false
        addresses:
          - $ip/$mask
                        #else
        dhcp4: false
        addresses:
          - $ip/24
                        #end if
                    #end if
                    #if $gateway != "":
        gateway4: $gateway
                    #end if
                    #if $name_servers and $name_servers[0] != "":
        nameservers:
          addresses:
                        #for $dns in $name_servers
            - $dns
                        #end for
                    #end if
                #else
        dhcp4: true
                #end if
            #else
      $iname:
                #if $static == True:
                    #if $ip != "":
                        #if $netmask != "":
                            #set $mask = sum([bin(int(x)).count('1') for x in $netmask.split('.')])
        dhcp4: false
        addresses:
          - $ip/$mask
                        #else
        dhcp4: false
        addresses:
          - $ip/24
                        #end if
                    #end if
                    #if $gateway != "":
        gateway4: $gateway
                    #end if
                    #if $name_servers and $name_servers[0] != "":
        nameservers:
          addresses:
                        #for $dns in $name_servers
            - $dns
                        #end for
                    #end if
                #else
        dhcp4: true
                #end if
            #end if
        #else
            #if $mac != "":
      $iname:
        match:
          macaddress: $mac
                #if $static == True:
                    #if $ip != "":
                        #if $netmask != "":
                            #set $mask = sum([bin(int(x)).count('1') for x in $netmask.split('.')])
        dhcp4: false
        addresses:
          - $ip/$mask
                        #else
        dhcp4: false
        addresses:
          - $ip/24
                        #end if
                    #end if
                    #if $gateway != "":
        gateway4: $gateway
                    #end if
                    #if $name_servers and $name_servers[0] != "":
        nameservers:
          addresses:
                        #for $dns in $name_servers
            - $dns
                        #end for
                    #end if
                #else
        dhcp4: true
                #end if
            #else
      $iname:
                #if $static == True:
                    #if $ip != "":
                        #if $netmask != "":
                            #set $mask = sum([bin(int(x)).count('1') for x in $netmask.split('.')])
        dhcp4: false
        addresses:
          - $ip/$mask
                        #else
        dhcp4: false
        addresses:
          - $ip/24
                        #end if
                    #end if
                    #if $gateway != "":
        gateway4: $gateway
                    #end if
                    #if $name_servers and $name_servers[0] != "":
        nameservers:
          addresses:
                        #for $dns in $name_servers
            - $dns
                        #end for
                    #end if
                #else
        dhcp4: true
                #end if
            #end if
        #end if
    #end for
    version: 2
#else
## profile based install so use DHCP
  network:
    ethernets: 
      eth0:
        dhcp4: true
    version: 2
#end if

This section should resemble the netplan network configuration standard to Ubuntu once generated.

Create 2 Cobbler sync-triggers to correct the GRUB and PXELINUX boot configurations to support Ubuntu cloud-init installations, which will run each time the cobbler sync action is performed:

  • /var/lib/cobbler/triggers/sync/post/fix-ubuntu-profiles-GRUB_PXE.sh
#!/bin/bash 
for PROFILE in $(cobbler profile list); do
    DIST=$(cobbler profile report --name $PROFILE | grep ^Distribution | awk {' print $3 '});
    VER=$(cobbler distro report --name $DIST | grep "OS Version" | awk {' print $4 '});
    [[ ($VER == "focal" || $VER == "jammy" || $VER == "noble") ]] || continue;
    sed -i "s,auto-install/enable=true priority=critical netcfg/choose_interface=auto url=http://10.0.0.10/cblr/svc/op/autoinstall/profile/${PROFILE} hostname=.* domain=local.lan suite=${VER},," /var/lib/tftpboot/grub/x86_64_menu_items.cfg;
    sed -i "s,auto-install/enable=true priority=critical netcfg/choose_interface=auto url=http://10.0.0.10/cblr/svc/op/autoinstall/profile/${PROFILE} hostname=.* domain=local.lan suite=${VER},," /var/lib/tftpboot/pxelinux.cfg/default;
    DIST="" && VER="";
done
  • /var/lib/cobbler/triggers/sync/post/fix-ubuntu-systems-GRUB_PXE.sh
#!/bin/bash 
for SYSTEM in $(cobbler system list); do
    PROFILE=$(cobbler system report --name $SYSTEM | grep ^Profile | awk {' print $3 '})
    DIST=$(cobbler profile report --name $PROFILE | grep ^Distribution | awk {' print $3 '});
    VER=$(cobbler distro report --name $DIST | grep "OS Version" | awk {' print $4 '});
    [[ ($VER == "focal" || $VER == "jammy" || $VER == "noble") ]] || continue;
    MAC=$(cobbler system report --name $SYSTEM | grep "MAC Addr" | awk {' print $4 '})
    sed -i "s,auto-install/enable=true priority=critical netcfg/choose_interface=auto url=http://10.0.0.10/cblr/svc/op/autoinstall/system/${SYSTEM} hostname=.* domain=local.lan suite=${VER},," /var/lib/tftpboot/grub/system/$MAC;
    MAC=$(echo $MAC | sed 's,:,-,g');
    sed -i "s,auto-install/enable=true priority=critical netcfg/choose_interface=auto url=http://10.0.0.10/cblr/svc/op/autoinstall/profile/${PROFILE} hostname=.* domain=local.lan suite=${VER},," /var/lib/tftpboot/pxelinux.cfg/01-$MAC;
	PROFILE="" && DIST="" && VER="" && MAC="";
done

Mark the above 2 Cobbler sync-triggers as executable

chmod u+x /var/lib/cobbler/triggers/sync/post/fix-ubuntu-profiles-GRUB_PXE.sh
chmod u+x /var/lib/cobbler/triggers/sync/post/fix-ubuntu-systems-GRUB_PXE.sh

Download the latest 3 releases of Ubuntu Server LTS

cd ~/Downloads && wget https://releases.ubuntu.com/24.04/ubuntu-24.04.1-live-server-amd64.iso
cd ~/Downloads && wget https://releases.ubuntu.com/22.04/ubuntu-22.04.5-live-server-amd64.iso
cd ~/Downloads && wget https://releases.ubuntu.com/20.04/ubuntu-20.04.6-live-server-amd64.iso

Ubuntu 20.04 PXE Deployment

Mount the Ubuntu 20.04 live server installer and import to Cobbler, then copy the installation media to the public HTTP share

[ -d /mnt/Ubuntu] || mkdir /mnt/Ubuntu
mount -t iso9660 -o loop,ro ~/Downloads/ubuntu-20.04.6-live-server-amd64.iso /mnt/Ubuntu
cobbler import --name Ubuntu20 --path /mnt/Ubuntu

mkdir -p /var/www/cobbler/pub/cloud-init/Ubuntu20
cp ~/Downloads/ubuntu-20.04.6-live-server-amd64.iso /var/www/cobbler/pub/cloud-init/Ubuntu20/.

the PXE Client will not install Ubuntu from the local mirror @ /var/www/cobbler/distro_mirror, and since the fallback: ofline-install option was not available for Ubuntu 20.04 cloud-init, this distro REQUIRES AN INTERNET CONNECTION to install, as detailed in this guide.

Edit the kernel options for the new Cobbler Distro to install Ubuntu automatically by default:

cobbler distro edit --name Ubuntu20-casper-x86_64 --kernel-options 'root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu20/ubuntu-20.04.6-live-server-amd64.iso autoinstall cloud-config-url=http://10.0.0.10/cblr/svc/op/autoinstall/profile/Ubuntu20-casper-x86_64'

alternatively, use the following kernel-options to install Ubuntu MANUALLY via PXE network boot

cobbler distro edit --name Ubuntu20-casper-x86_64 --kernel-options 'root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu20/ubuntu-20.04.6-live-server-amd64.iso cloud-config-url=/dev/null'

More info on these configs in the Tips & Troubleshooting section below.

create a new autoinstall (cloud-init) template from the cloud-init_user_data file created above, and configure it as the autoinstallation template for the Ubuntu 20.04 Cobbler PROFILE

sed ':a;N;$!ba;s,      uri: http://\$http_server/cblr/links/\$distro\n#      uri: http://us.archive.ubuntu.com/ubuntu,#      uri: http://\$http_server/cblr/links/\$distro\n      uri: http://us.archive.ubuntu.com/ubuntu,' /var/lib/cobbler/templates/cloud-init_user-data | tee /var/lib/cobbler/templates/Ubuntu20_cloud-init_user-data
cobbler profile edit --name Ubuntu20-casper-x86_64 --autoinstall Ubuntu20_cloud-init_user-data

Create a new Cobbler System to install Ubuntu 20.04 automatically based on the system’s (PXE client’s) MAC address, replacing the “aa:bb:cc:dd:ee:ff” with the MAC address of your PXE client, and being sure not to use a duplicate MAC or IP addresse of any other Cobbler System as well as setting the kernel-options to match the Cobbler System’s name:

NAME="Ubuntu20-auto" && cobbler system add --name $NAME --profile Ubuntu20-casper-x86_64 --kernel-options "root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu20/ubuntu-20.04.6-live-server-amd64.iso autoinstall cloud-config-url=http://10.0.0.10/cblr/svc/op/autoinstall/system/$NAME" --mac-address "aa:bb:cc:dd:ee:ff" --static true --ip-address "10.0.0.20" --netmask "255.255.255.0" --gateway "10.0.0.1" --name-servers "10.0.0.1 1.1.1.1 10.0.0.10" --hostname "Ubuntu20" --netboot-enabled true && NAME=""

Alternatively, configure the Cobbler System kernel-options to run as a MANUAL installation:

cobbler system add --name Ubuntu20 --profile Ubuntu20-casper-x86_64 --kernel-options 'root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu20/ubuntu-20.04.6-live-server-amd64.iso autoinstall cloud-config-url=/dev/null' --mac-address "aa:bb:cc:dd:ee:ff"

Restart and sync up Cobbler

systemctl restart cobblerd && sleep 10
cobbler sync

The PXE Client VM can now be powered on, and should automatically boot to PXE and install Ubuntu 20.04 to the VM HDD using the “Ubuntu20_cloud-init_user-data” preseed template created above.

Ubuntu 22.04 PXE Deployment

Take similar steps as above to import and automatically deploy Ubuntu 22.04 LTS Server over PXE, using the original cloud-init_user-data autoinstall template created above.

[ -d /mnt/Ubuntu ] || mkdir /mnt/Ubuntu
mount -t iso9660 -o loop,ro /home/fedora/Downloads/ubuntu-22.04.5-live-server-amd64.iso /mnt/Ubuntu
cobbler import --name Ubuntu22 --path /mnt/Ubuntu

mkdir -p /var/www/cobbler/pub/cloud-init/Ubuntu22
cp ~/Downloads/ubuntu-22.04.5-live-server-amd64.iso /var/www/cobbler/pub/cloud-init/Ubuntu22/.

cobbler distro edit --name Ubuntu22-casper-x86_64 --kernel-options 'root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu22/ubuntu-22.04.5-live-server-amd64.iso autoinstall cloud-config-url=http://10.0.0.10/cblr/svc/op/autoinstall/profile/Ubuntu22-casper-x86_64'

cobbler profile edit --name Ubuntu22-casper-x86_64 --autoinstall cloud-init_user-data

NAME="Ubuntu22-auto" && cobbler system add --name $NAME --profile Ubuntu22-casper-x86_64 --kernel-options "root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu22/ubuntu-22.04.5-live-server-amd64.iso autoinstall cloud-config-url=http://10.0.0.10/cblr/svc/op/autoinstall/system/$NAME" --mac-address "aa:bb:cc:dd:ee:ff" --static true --ip-address "10.0.0.22" --netmask "255.255.255.0" --gateway "10.0.0.1" --name-servers "10.0.0.1 1.1.1.1 10.0.0.10" --hostname "Ubuntu22" --netboot-enabled true && NAME=""

systemctl restart cobblerd && sleep 10
cobbler sync

Again, the PXE Client is not installing via the local repo @ /var/www/cobbler/distro_mirror, but now that the fallback: offline-install option is available with Ubuntu 22.04 cloud-init, the installer will NOT require and internet connection to install, instead installing using the live server installer (.iso file) available on Cobbler’s public HTTP share.

Ubuntu 24.04 PXE Deployment

First, modify /var/lib/cobbler/distro_signatures.json to suppport Ubuntu 24.04 (which is not included with Cobbler at the time of writing) by pasting the following code block immediately following the Ubuntu 22 jammy signature definition:

      "noble": {
        "signatures": [
          "dists",
          ".disk"
        ],
        "version_file": "Release|info",
        "version_file_regex": "Suite: noble|Ubuntu 24.04",
        "kernel_arch": "linux-headers-(.*)\\.deb",
        "kernel_arch_regex": null,
        "supported_arches": [
          "i386",
          "amd64"
        ],
        "supported_repo_breeds": [
          "apt"
        ],
        "kernel_file": "(linux|vmlinuz(.*))",
        "initrd_file": "initrd($|.gz$|.lz$)",
        "isolinux_ok": false,
        "default_autoinstall": "",
        "kernel_options": "",
        "kernel_options_post": "",
        "template_files": "",
        "boot_files": [],
        "boot_loaders": {}
      },

With this modification made, be careful not to use the cobbler signature update command on the Cobbler server, which will remove any user-defined signatures.

Sync up Cobbler in order to apply the changes

systemctl restart cobblerd && sleep 10
cobbler sync

Take similar steps as above to import and automatically deploy Ubuntu 24.04 LTS Server over PXE

[ -d /mnt/Ubuntu ] || mkdir /mnt/Ubuntu
mount -t iso9660 -o loop,ro /home/fedora/Downloads/ubuntu-24.04.1-live-server-amd64.iso /mnt/Ubuntu
cobbler import --name Ubuntu24 --path /mnt/Ubuntu

mkdir -p /var/www/cobbler/pub/cloud-init/Ubuntu24
cp ~/Downloads/ubuntu-24.04.1-live-server-amd64.iso /var/www/cobbler/pub/cloud-init/Ubuntu24/.

cobbler distro edit --name Ubuntu24-casper-x86_64 --kernel-options 'root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu24/ubuntu-24.04.1-live-server-amd64.iso autoinstall cloud-config-url=http://10.0.0.10/cblr/svc/op/autoinstall/profile/Ubuntu24-casper-x86_64'

cobbler profile edit --name Ubuntu24-casper-x86_64 --autoinstall cloud-init_user-data

NAME="Ubuntu24-auto" && cobbler system add --name $NAME --profile Ubuntu24-casper-x86_64 --kernel-options "root=/dev/ram0 ramdisk_size=1500000 ip=dhcp url=http://10.0.0.10/cblr/pub/cloud-init/Ubuntu24/ubuntu-24.04.1-live-server-amd64.iso autoinstall cloud-config-url=http://10.0.0.10/cblr/svc/op/autoinstall/system/$NAME" --mac-address "aa:bb:cc:dd:ee:ff" --static true --ip-address "10.0.0.24" --netmask "255.255.255.0" --gateway "10.0.0.1" --name-servers "10.0.0.1 1.1.1.1 10.0.0.10" --hostname "Ubuntu24" --netboot-enabled true && NAME=""

systemctl restart cobblerd && sleep 10
cobbler sync

Again, if using the fallback: offline-install option in the cloud-init template for this System, the PXE Client will use the .iso file hosted by Cobbler as the source for installation.

Tips & Troubleshooting

  1. The Cobbler 3.3.7 Beginner’s Guide Tips & Troubleshooting section contains some basic recommendations and limitations of Cobbler which will not be repeated here.

  2. More info on cloud-init

  3. The kernel-options used above were based off of Ubuntu’s official docs

  4. Additional references:




Enjoy Reading This Article?

Here are some more articles you might like to read next:

  • Cobbler (v3.3.7) XCP-ng Deployment Guide
  • Cobbler (v3.3.7) OpenSUSE Deployment Guide
  • Cobbler (v3.3.7) Debian Deployment Guide
  • Cobbler 3.3.7 Beginner's Guide