Preface
Pi-hole is, as advertised, a network-level adblocker. It's basically an easily hostable DNS server that looks for DNS lookups and matches them to lists and rejects or allows them based on these lists - kind of like what an adblocker does, and the reason why most of its users have it deployed on their network. It's a mature tool, and a typical usecase for Raspberry Pis and other single-board computers.
Typically, most people will install it via Raspbian (oh, I guess it's called "Raspberry Pi OS" now) - but I already have experience with so-called "container optimised OSes" like Fedora CoreOS. These operating systems are great for several reasons:
- Relaxed management: I don't want to mess around with a bunch of things and have a lot of moving parts, really. These operating systems come with everything you need to run containers, and not much else. MicroOS's openSUSE Wiki page even says "openSUSE MicroOS is an operating system you don't have to worry about."
- Declaratively configured: Systems like CoreOS and MicroOS use a configuration system called "Ignition", which at its core is a simple config that allows you to set up users, network, and the rest is just creating files on the filesystem. It's rather minimalistic, but it works great. I'm a fan of declarative configuration, which makes me interested in these kinds of operating systems.
- Automatic updates: automatic updates without the hassle of unattended-upgrades etc.
Basically, in the long term, maintaining this system is easier than maintaining a regular Raspbian-based Pi-hole, as there are less manual tasks, and you can easily remake your setup on other systems.
What do you need?
Raspberry Pi
Well, you could use any single-board computer, but I chose a Raspberry Pi, more specifically my Raspberry Pi 3B+. I got it around 2018 and haven't found much use for it (though it used to be my "homeserver"), so it's perfect for this.
A copy of MicroOS
To get a copy of MicroOS, navigate to get.opensuse.org and select "MicroOS". While you're there, go to the "Download" tab and select "Alternative Downloads". Scroll down to the "Hardware" section and download the "RaspberryPi Container Host".
The difference between the regular "RaspberryPi" and "RaspberryPi Container Host" images is that the "Container Host" image comes with the podman container runtime already installed and pre-configured. I'm using a container for Pi-hole, so make sure you select the correct image.
Next up is to flash it onto a microSD card. You could use the dd (or Disk Destroyer depending on who you ask) utility on pretty much any *nix system, or you could just use the Raspberry Pi Imager. I chose to use the Raspberry Pi Imager, because it verifies whether the flashed microSD card is correct according to the image or not (plus it's less hassle). Going over how to use it is out of scope here, since it's pretty straight-forward.
Configuring MicroOS
How does this work?
MicroOS is a bit unique in that it has two different "configuration" methods, which can be used at the same time:
- Combustion, which is their own solution, which is basically just a shell script
- Ignition, which is the exact same as Fedora CoreOS uses (even uses the same "variant" section in the YAML file,
fcos)
I will use both in this post, as they each do their own thing very well compared to the other. Ignition is more fit for creating files, users, or enabling systemd units, while Combustion is powerful due to its scripting nature.
Create configuration files
Now, you could do this manually using tools like Butane and just reading a lot of documentation. However, after searching around the internet, I discovered that openSUSE (the organisation behind MicroOS) made a tool that allows you to graphically produce both Combustion scripts and Ignition configs at the same time by just filling out a form. I decided to use this as it eases the amount of work by a lot. The tool is called Fuel Ignition, and you can start it by pressing "Let's do it".

Here's how I configured things:
Users
I created a user called trainer. You'll see why. In order to give it sudo access, I had to create a script (inside of Advanced > Custom Bash Script) that looked like this, which allows me to run sudo passwordless:
# Custom: add trainer user to sudoers file
echo "trainer ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/trainer
Interestingly, you can easily allow the user to use sudo via the Ignition configuration file by doing something like this in Butane:
variant: fcos
version: 1.7.0
passwd:
users:
- name: trainer
groups:
- wheel
This doesn't seem to be an option in Fuel Ignition, so I had to do it manually.
I made sure to set a strong password and added my SSH public keys. I recommend using SSH keys instead of passwords due to security and convenience.
Hostname
I set the hostname to rotom. My personal devices are named after Pokémon things - my main computer is named Zygarde, my Legion Go S is named Pokedex etc. If you've played Pokémon Sun/Moon/Ultra Sun/Ultra Moon, you'll know that Rotom serves as the map in the game (as well as a myriad of other things), so this hostname seemed fitting.
Timezone
I live in Norway, which means I had to set the timezone to Europe/Oslo.
Network
My setup uses Wi-Fi, as I've yet to receive an Ethernet connection to my room, and thankfully Fuel Ignition makes configuring Wi-Fi declaratively easy.
In the Network section, enable IPv4 Network and set a fixed IPv4 address. Configure this however you wish. IPv6 is optional. I just enabled DHCP for IPv6 as I don't really feel like using it right now (but I should in the future). Once you've configured a static IPv4 (and/or IPv6 is configured), you can enable Wi-Fi and set up the SSID, what kind of key management your SSID is using and of course the PSK (also known as pre-shared key, Wi-Fi password etc.).
Set up the Pi-hole container
This is probably the most interesting part here. The way I deployed Pi-hole is through a Podman quadlet. Basically, this is a systemd unit that runs a container.
To set up the quadlet, go to the "Advanced -> Add Files To System" section of Fuel Ignition and set the file path to /etc/containers/systemd/pihole.container. The default file permissions are fine.
Set the file content to this:
[Unit]
Description=Pi-hole
After=network-online.target
Wants=network-online.target
[Service]
Restart=always
[Container]
ContainerName=pihole
Image=docker.io/pihole/pihole:latest
AutoUpdate=registry
Network=host
PublishPort=53:53/tcp
PublishPort=53:53/udp
PublishPort=80:80/tcp
PublishPort=443:443/tcp
Environment=TZ="Europe/Oslo"
Environment=FTLCONF_webserver_api_password="ch4ng3m3!!!" # Do as it says.
Environment=FTLCONF_dns_listeningMode="ALL"
Environment=FTLCONF_dns_upstreams="9.9.9.9;149.112.112.112"
Volume=pihole-etc:/etc/pihole:z
AddCapability=NET_ADMIN
AddCapability=SYS_TIME
AddCapability=SYS_NICE
[Install]
WantedBy=default.target
Make sure to set the
:zsuffix on the volume mount. Not doing so will cause permission errors due to how Podman volume permissions work.
Something I found useful while trying to debug my initially broken quadlet was the command sudo /usr/libexec/podman/quadlet -dryrun. This utility will look for your quadlet files and check for any configuration errors. If you don't see your quadlet in systemctl list-units, give this a shot.
If you ever update the quadlet on a running system, make sure to run systemd daemon-reload as root to reload the container unit file, then run systemctl restart pihole to restart and remake the container.
The AutoUpdate=registry option makes Podman automatically look for image updates and apply them every time podman-auto-update.timer is run. This could have security implications (especially because we're using the latest tag), so if this is a concern for you, you can remove the line.
Set up a USB drive containing our configs
The recommended way to load these configuration files is to create an ext4 partition on a USB drive (could be any storage medium, it seems to just look for devices with the label ignition). Create something that looks like this tree (no need to fill out the files yet - we'll get to it!)
alex@zygarde [18:23:04] [/run/media/alex/ignition]
-> % tree
.
├── combustion
│ └── script
└── ignition
└── config.ign
3 directories, 2 files
The MicroOS documentation doesn't make this obvious (in my opinion), but naming the partition on your drive
combustionhas a side-effect where it will only look for the combustion script, and not the Ignition configuration file. Make sure you name your partitionignition, to ensure that it will apply both the Combustion script and the Ignition configuration file.
When you're happy with the outputted configuration files in Fuel Ignition, copy and paste the outputs to their respective places. That is, the combustion script should go to combustion/script on your newly created partition, and the Ignition output into ignition/config.ign.
Run it
Once you've got your separate storage medium containing an ignition partition with the files combustion/script and ignition/config.ign and a microSD card with a fresh MicroOS RaspberryPi Container Host image, insert the microSD card into your Pi, then insert the storage medium into one of the USB ports. After that, turn the Pi on and connect it to a monitor or serial connection and watch the output. The initial boot will take a while, but after that the startup times should be reasonable (obviously, this all depends on the speed of your microSD card).
Once the initial setup is complete, wait a while and try reaching your device on whatever IP you configured at port 80 and you should hopefully see the Pi-hole admin login page.
Notes
I originally intended to use Fedora IoT for this, but I decided on MicroOS for several reasons:
- MicroOS allows you to load configuration files from a storage medium. Fedora IoT, from my knowledge, requires you to load it from an HTTP server, which is a hassle to set up with my setup (my computer wouldn't share the Ethernet port even). There seems to be an "either or" situation here, because on my VPS I'd like to use CoreOS as it allows you to load Ignition files from the web, whereas MicroOS only allows you to load configs from storage media. You can't have both!
- MicroOS has slightly better documentation from Fedora IoT it seems. Most of my knowledge is from Fedora CoreOS, but Fedora IoT is somewhat similar to CoreOS. Despite this, I couldn't find information on how to configure quadlets on Fedora IoT through Ignition. There was only documentation on how to run the podman cli, which isn't what I wanted to do.
They're both pretty similar and achieve roughly the same goals, it's just that MicroOS was a better fit for my setup.
I used and read a lot of resources in order to do this. Here's a list of some of them, in no particular order:
- Fuel Ignition
- Portal:MicroOS/Combustion
- Installing MicroOS on a Raspberry Pi
- The Low-Complexity Stack: Self-Hosting with OpenSUSE MicroOS and Podman
- Guide: Getting started with Podman Quadlets
- Building a openSUSE MicroOS RPi Network Monitor
- Make systemd better for Podman with Quadlet
- Docker - Pi-hole documentation
No AI tools have been involved in any part of this project.