• An upgradeable Pi-Hole image

  • This project was inspired by reading the comments section of an article about Pi-Hole in which quite a few people were saying that an upgrade to Pi-Hole had broken their install. My immediate thought was that I know how to solve that by using an A/B updating scheme with RAUC as I have done on customer projects.

    Pi-Hole is normally installed by running a script which downloads all the components and configures the system. This would obviously not work when we need a pre-built update bundle to install an updated image. As a long time Yocto user, Yocto was my default choice of build system and as I had a Raspberry Pi4 available that became the chosen platform.

    A variety of Yocto layers were used to provide all the code required to build a minimal image for the RPi4 which had the single purpose of running Pi-Hole.

    • poky
    • meta-open-embedded
      • These provide the core toolchains and target linux filesystem. The current LTS version scarthgap was used.
    • meta-raspberrypi
      • This provides the RPi code such as the kernel and the board specific support
    • meta-rauc
      • This provides the core RAUC functionality
    • meta-rauc-community
      • provides customisation for RAUC for common embedded platforms including the RPi
    • meta-hunter-embedded
      • defines the pi-hole distribution contents.
      • Adds the new recipes to build and package Pi-Hole. This is all based on original Pi-Hole install script
      • Modifies recipes from the previous layers to meet specific requirements of the image. An example is removing unused drivers from kernel to minimise filesystem size.

    My preferred way to manage the Yocto configuration and build is to use KAS. The configuration is handled by yaml files. The ones used are available at https://github.com/HunterEmbedded/kas-pi-hole. The README.md file has the full instructions to build and programme/update an SD card.

    The yaml files are divided as follows

    • kas/pihole.yaml – pulls in the board independent layers.
    • kas/rpi4.yaml – pulls in the board specific layers for the RPi4 and then includes the generic pihole.yaml configuration.

    The actual Yocto build takes place in a container to make builds reliably reproducible. In the past I have been bitten by a Ubuntu update breaking a build.  So, it’s a container for me every time now running in a virtual environment. KAS provides kas-container.

    The WKS file defines the layout of the disk image that is written to the SD card. It consists of 4 partitions:

    Partition 1 is a FAT partition that contains the u-boot and the u-boot environment. It must be a FAT partition so that u-boot can read the environment. It is mounted by linux as /u-boot so that fw_printenv/saveenv can access the environment.

    Partition 2 is an EXT4 that contains the linux rootfs built by Yocto. It is referred to by RAUC as RootFS A. It contains the kernel so that a RAUC update of the rootfs can update the kernel. As this image is not designed to have packages updated or added the partition is not defined to be of a fixed size, just big enough.

    Partition 3 is an EXT4 that will be RootFS B. In the initial SD card image this is just a redundant copy of RootFS A.

    Partition 4 is an EXT4 that is mounted as /data. It contains the persistent configuration files for Pi-Hole that are symlinked from the RootFS. It is also large enough to write an update bundle to with scp as described in the README.md.