Debian Bullseye Bug Remedy Script

@Sineos kindly posted a process for identifying whether or not your system is affected by the Debian 11 Bullseye bug:

The process works well but requires a fair amount of precision as well as copy and pasting so I came up with a bash script that automates the various actions.

The script is to be run right after the SD card is formatted with the rPi OS but before Klipper is installed. To execute the script, run the follow command from the SSH shell:

curl -sSL | bash

The original curl statement was pointing to the wrong file. Apologies and thanx to @Sineos for finding that.

I’ve tested it on the Raspberry Pi OS LITE (32-BIT) on the 4B and CM4 and I’m hoping people with other SBCs and operating systems will give it a try. At some point I’ll reconfigure one of my printers to again use a CB1 and see what happens there.

The process the script follows is:

  1. The command apt-cache policy udev is executed and the output is checked to see if the installed udev is “247.3-7+deb11u2” - if it isn’t, then the script exits. if the installed udev is “247.3-7+deb11u2” then the following steps are executed.
  2. Next the backport link (deb bullseye-backports main non-free contrib) is added to the end of the sources.list file in the system.
  3. The command sudo apt update is run. If there are “NO_PUBKEY” key errors returned then the next step executes, else execution skips to 6.
  4. The two error “NO_PUBKEY” keys are added to the system using the sudo apt-key adv --keyserver --recv-keys [key] command.
  5. sudo apt update is run again to ensure that all the required actions take place successfully.
  6. The command sudo apt install udev -t bullseye-backports is executed.
  7. The user is prompted to execute sudo reboot.

Please feel free to review the script. I am not doing anything nefarious in the script (or at least not intentionally) but I’m not a bash script guru and any comments or warnings from experts would be appreciated.


Nice work @mykepredko
Indeed, I did think about it as well. If done as a script, we should extend the checks and make it less depending on the RPi version, since users might have different Linux flavors installed.

Maybe something along the lines of:


is_udev_version_deb11u2() {
    # Check if udev is installed
    if dpkg -l | grep -q '^ii\s*udev'; then
        # Check the version
        udev_version=$(dpkg -l | grep '^ii\s*udev' | awk '{print $3}')
        if [[ "$udev_version" == *deb11u2* ]]; then
            return 0  # true
    return 1  # false

contains_bullseye_backports() {
    # Check in /etc/apt/sources.list
    if grep -q "bullseye-backports" /etc/apt/sources.list; then
        return 0  # true

    # Check in files within /etc/apt/sources.list.d/
    for file in /etc/apt/sources.list.d/*; do
        if grep -q "bullseye-backports" "$file"; then
            return 0  # true

    return 1  # false

download_bullseye_backports_key() {
    local key_url=""
    wget -O /usr/share/keyrings/debian11-backports-archive-keyring.gpg "$key_url"

add_bullseye_backports_to_sources() {
    local repo_string="deb [signed-by=/usr/share/keyrings/debian11-backports-archive-keyring.gpg] bullseye-backports main non-free contrib"
    echo "$repo_string" | tee -a /etc/apt/sources.list.d/bullseye-backports.list > /dev/null

# Update the system first
apt update
apt upgrade -y

# Check for buggy udev version and remedy it
if is_udev_version_deb11u2; then
    if contains_bullseye_backports; then
        echo "Backports already available. Installing udev from backports."
        apt install udev -t bullseye-backports -y
        echo "Setting up bullseye-backports repository..."
        apt update
        echo "Backports installed. Installing udev from backports."
        apt install udev -t bullseye-backports -y
    echo "System seems OK. No need to install new udev. Exiting"

This is largely untested, I just typed it more or less freestyle, so it might be some pseudo-code. I would need to find a RPi and set up a test.


  • I think the buggy version deb11u2 only
  • Since the apt key is officially deprecated, I tried to do it “correct” this time. This is more involved than the apt method, that’s why I chose not to use it in the original fix. But as I said: Untested

Edit 2:
The link in your original post is pointing to another file but through tedious research and work, I was able to find the right one :wink:

Edit 3:
The way it is setup, it needs to be called with sudo

Thanx - I know it could have been done better.

Now, when I researched this, it seems that the problem is a) pretty pervasive and b) binary. Pervasive in that it’s in quite a few *nix distributions and binary as if it’s not there, it’s not a problem.

Regardless, I’d be interested in finding out what happens when people using hosts other than an rPi run the script.

I’ll go through the various methods in a few hours and report back.

Rather than return “0” for true and “1” for false, how about using the true and false Linux commands? I always find that the statement “return 0 # true” causes my head to spin and is distracting from being able to naturally follow the logic.

When I’ve looked at the script I wrote operates, it seems that apt upgrade -y executes in KIAUH.

Also, in your code, you’re not handling the NO_PUBKEY condition - is this deliberate?

I was going by Debian bug report for the test and it seems to indicate that you need to look for deb11u1:

Is there an authoritative one out there?

I’ll try your dpkg check on a newly imaged SD Card (all the ones I have right now have run my script and the value is different).

Thanx for the catch - I’ve edited the original post

Sorry, what needs to be?

In shell scripts, the logic generally is

  • return 0 for true
  • return anything not 0 for false
  • working with true and false in a boolean sense can be tricky in shell and has ugly caveats

Hmm. Seems inconclusive:
The Debian Bug Report you quoted seems to be a different issue.

The if could be modified to check for both. Would likely do no harm:

if [[ "$udev_version" == *deb11u1* ]] || [[ "$udev_version" == *deb11u2* ]]; then
  • The apt key method is deprecated. It still works but is no longer recommended
  • The function download_bullseye_backports_key will get the key and put it into /usr/share/keyrings/debian11-backports-archive-keyring.gpg
  • The function add_bullseye_backports_to_sources will add
    deb [signed-by=/usr/share/keyrings/debian11-backports-archive-keyring.gpg] bullseye-backports main non-free contrib
    which then uses this downloaded key explicitly.
  • Adding the backports will only happen if not already present. If you use, e.g. a pristine Debian or Armbian, backports are present by default. This is done with the function contains_bullseye_backports

The entire script as proposed above needs root rights to run, so it needs to be called with sudo

We could also add a check if the script is called with proper rights, e.g.


is_root_user() {
   if [ "$(id -u)" -ne 0 ]; then
        echo "This script must be run as root. Please use sudo or run as the root user."
        exit 1

# Check for root rights

# Update the system first
apt update
apt upgrade -y

# Check for buggy udev version and remedy it


I would not rely on the fact that KIAUH is run first. If the script runs it, we make sure that we have a defined starting point and the user is not surprised when running the upgrade later.

I have turned the instructions Debian Bullseye Bug causing Klipper to no longer find the printer board - #8 into a shell script:

It will:

  • Check if it is executed on Debian 11
  • Check if the installed udev contains the buggy version string
  • Check if run as root or under root rights
  • Update the system to the latest versions via apt update and apt upgrade
  • If Debian 11 AND buggy udev AND root is found, it will:
    • Check if the Debian 11 backports repository is already present → If not, download the repository signing key and install the repository
    • Install the fixed udev from the backports repository

Run it via

curl -sf -L | sudo bash

Above command will run the script as root
Only do this if you trust me or if you have verified the script beforehand.

Any tests would be appreciated. I did test it on various different OS versions, like MainsailOS, Pi OS lite etc.


To end off this thread, I’ve done a fairly extensive experiment to try and figure out what is the best approach to get to the ultimate goal which is to have an OS/firmware controller installation that can be updated without physically accessing the controller.

In terms of updating a controller, I believe the optimum (maybe only) way to do this is with a Katapult (formerly “CanBoot”) installation with a CAN network specified. I know that some people don’t like using a CAN bus interface to their toolheads but this approach will work even if there are no CAN devices attached to the controller. I suspect, but have not tested, that this approach will work with a controller with an unused UART in place of the CAN interface.

Some of the results surprised me and, thanks to @Sineos pointing out the “MainsailOS”, I have a better Raspberry PI OS option to load. I’m going to try and present as much information as possible so that other people can replicate my tests on different hardware combinations. I’d be interested in hearing people’s results with controllers like the Raspberry Pi Zero.

This is a long post, so rather than slog through it, I suggest you scroll down to the “Observations” and “Conclusions” at the end before reading through the procedure information.


In this experiment, I used the following host systems:

  • Raspberry Pi 4B connected to an Octopus controller via USB. The Octopus has a fan connected to the “FAN0” Port
  • Raspberry Pi CM4 connected to a Manta M8P controller via the board’s internal USB. The Manta has a fan connected to the “FAN0” Port.
  • BTT CB1 attached to a “RPI 4B adapter board” and connected to an Octopus via USB

For Firmware installation I used:

  • Raspberry Pi Installer

For all other installations the host network connection was used and specified using SSH. I used a Linux Ubuntu Terminal for SSH access to the hosts.

Test Procedure

Tests were run on different host/controller combination in terms of using the standard (“Lite” in the case of the Raspberry PIs) Operating Systems with these different update approaches:

  • “Vanilla” or no updates
  • “Update/Upgrade” which consisted of running sudo apt update followed by sudo apt upgrade -y
  • “Myke Script” which is running the script presented at the start of this thread
  • “Sineos Script” which was the script presented in the previous post to this thread

The following procedure was used on each of the host/controller combinations for each of the test configurations:

  1. Load Selected Operating System into the host for the test using Raspberry Pi Installer. The options are:
    1.1. 32-Bit Raspberry Pi OS Lite (for the Raspberry Pi 4B and CM4)
    1.2. 64-Bit Raspberry Pi OS Lite (for the Raspberry Pi 4B and CM4)
    1.3. CB1 Operating System provided by BTT
    1.4. 32-Bit MainsailOS Operating System (for the Raspberry Pi 4B and CM4)
    1.5. 64-Bit MainsailOS Operating System (for the Raspberry Pi 4B and CM4)
  2. Login using SSH into the host
    2.1. When MainsailOS is used, check to see that the Mainsail web page was available
  3. When testing the CB1 host, execute the sudo nmcli dev wifi connect [NetworkName] password "[Password]" from an Ethernet connection to ensure CB1 wifi is working. Reboot the CB1 and login again after executing this command
  4. Run and record the results of apt-cache policy udev This is compared to after the updates to see if any changes were made
  5. Perform the specified update procedure:
    5.1. For “Vanilla” do nothing (this includes doing the reboot specified for the other approaches)
    5.2. For “Update” execute sudo apt update followed by sudo apt upgrade -y and then reboot
    5.3. For "Myke Script’ execute curl -sSL | bash and then reboot
    5.4. For "Sineos Script’ execute curl -sf -L | sudo bash and then reboot
  6. If a reboot was performed in the previous step, then login to the host via SSH
    6.1. Execute apt-cache policy udev again to see what changes were made to the system
    6.2. If the host is running MainsailOS, check to see that the Mainsail web page is available after reboot
  7. If MainsailOS is NOT running, execute the following commands to load Klipper, Moonraker and Mainsail:
    7.1. sudo apt-get install git -y
    7.2. git clone
    7.3. ./kiauh/
    7.4. Install Klipper with Python 3.x and one instance.
    7.5. Install Moonraker
    7.6. Install Mainsail
    7.7. Exit out of KIAUH
  8. Install Katapult and make a firmware image with the commands:
    8.1. git clone
    8.2. pip3 install pyserial
    8.3. cd Katapult
    8.4. make menuconfig
    8.5. For Octopus Select STM32, F446, 12MHz Clock Reference
    8.6. For Manta Select SMT32, G0B1
    8.7. Quit from menuconfig and Save
    8.8. make
  9. Make a Klipper firmware image using the commands:
    9.1. ~/klipper
    9.2. make menuconfig
    9.3. For Octopus Select STM32, F446, 12MHz Clock Reference, USB to CAN bus bridge, CAN bus (on PD0/PD1), 500000 CAN bus speed
    9.4. For Manta Select SMT32, G0B1, USB to CAN bus bridge, CAN bus (on PD12/PD13), 500000 CAN bus speed
    9.5. Quit from menuconfig and Save
    9.6. make
  10. Put controller into DFU mode.
    10.1. For Octopus, insert the jumper on the “BOOT” pins and press reset.
    10.2. For Manta, Press and hold down the “BOOT” button followed by pressing and releasing “RESET” and then releasing “BOOT”
    10.3. Confirm the controller is in DFU mode by using the command lsusb and looking for the entry “ID 0483:df11 STMicroelectronics STM Device in DFU Mode”
  11. Flash the Katapult firmware using the command sudo dfu-util -a 0 -D ~/Katapult/out/katapult.bin --dfuse-address 0x08000000:force:mass-erase:leave -d 0483:df11
    11.1. Check that flash operation is complete using the lsusb command and seeing that the USB port is now labeled “OpenMoko”
  12. Get the MCU serial number using the command ls /dev/serial/by-id/. The returned string with be copy and pasted into the next command.
  13. Flash the Klipper firmware image into the controller using the command python3 ~/Katapult/scripts/ -d /dev/serial/by-id/[mcuSerial] where “[mcuSerial]” is the string returned from the previous step.
  14. Setup the CAN0 device:
    14.1. Create the CAN0 file using the command sudo nano /etc/network/interfaces.d/can0
    14.2. Put in the following contents of the file and then Ctr-X and Save:
allow-hotplug can0
iface can0 can static
 bitrate 500000
 up ifconfig $IFACE txqueuelen 256
 pre-up ip link set can0 type can bitrate 500000
 pre-up ip link set can0 txqueuelen 256
  1. Execute sudo reboot to restart the host
  2. Check the operation of the system by carrying out the following tasks:
    16.1. SSH into the host
    16.2. Execute ls /dev/serial/by-id and expect to see “No Such File or Directory”
    16.3. Check to see that Mainsail web page is active
    16.4. Execute -I can0 -q and you should see a "Detected UUID: " with a 12 character hex string following and then “, Application Klipper”

If everything above is good, then the initial programming is complete.

Now, to test if the controller can be updated without accessing, change the Klipper firmware so that the fan on the FAN0 port on the controller runs on firmware startup.

  1. Change the firmware to turn on the FAN0:
    1.1. Execute cd ~/klipper (if not already in the folder)
    1.2. Execute make clean
    1.3. Execute make menuconfig
    1.4. If Octopus, enter PA8 into the “GPIO pins to set at micro-controller startup”
    1.5. If Manta, enter PE0 into the “GPIO pins to set at micro-controller startup”
    1.6. Quit and Save
    1.7. Execute make
  2. Get the controller CAN bus UUID using the command ~/Katapult/scripts/ -i can0 -q and save it for copy and pasting in the next step
  3. Setup the Flashing operation with the command python3 ~/Katapult/scripts/ -r -u [uuid] where “[uuid]” is the value returned from the previous step
  4. Get the controller serial ID using the command ls /dev/serial/by-id/ and save it for copy and pasting in the next step.
  5. Program the controller with the updated klipper firmware using the command python3 ~/Katapult/scripts/ -d /dev/serial/by-id/MCU Serial from previous where “MCU Serial from previous” is the string returned in the previous step.
  6. If everything works correctly, the fan attached to the controller board will start running

After doing all this and the fan is running, I consider the operation a “PASS” .

If anything does not work as expected or return an invalid response then the process is repeated from the start and if the same error is encountered, then the result is a “FAIL”.

If the conditions of the script are not met, then the test was stopped (in the case of my script where I knew nothing would happen) or it was continued but marked as not executing as expected (@sineos’ script which indicated that the udev wasn’t updated, which is what I thought was the point of the script).


The test results were recorded in this spreadsheet: 2023.09.18 - Raspberry Pi Klipper & Katapult OS Build and Program (19.2 KB)

Netting out the results looks like:

  1. The BTT is a real PITA to work with.
    1.1. A big part of the issue is that the image cannot be updated in the Raspberry Pi Imager to specify a WiFi network - editing the configuration file as described in the documentation works about 50% of the time. I found that the nmcli dev wifi connect command works 100% of the time, but needs an Ethernet connection to enter it in.
    1.2. I never did figure out how to change the URL the CB1 appears on the network. “BTT-CB1.hitronhub.home” seems to be the only thing it will appear as which limits the number of devices on a network.
    1.3. I never figured out how to change the primary user from “biqu”. This, along with the locked in “BTT-CB1.hitronhub.home” comes across to me as a huge potential security problem (especially if you don’t change the default password from “biqu”).
    1.4. If anybody knows how to change these preset values, please let me know.
  2. The two scripts (@sineos’s and mine) did not change the system or the test results in the way that I expected.
  3. The host and controller programming process outlined here worked very reliably for all three hosts and two controller boards.


  1. I believe I have a way for doing firmware updates on CAN equipped and Katapult programmed controllers without having to access the hardware.
    1.1. I’m not sure how important this is to other people but unless I have a printer designed that the controller can be easily accessed (which I’ve never seen on any commercial printers with the exeception of the Prusa i3) I’m not going to do firmware updates unless it’s deemed really important
    1.2. Klipper’s printer.cfg file eliminates probably 90% of the need for accessing a printer’s controller, being able to update the Klipper firmware this way picks up the last 10%
  2. When I look at the results, I feel like the most reliable option is to use MainsailOS along with sudo apt update and sudo apt ugrade -y
    2.1. The scripts we came up with don’t seem to add to the effectiveness of the overall system
  3. While long, the programming process outlined here was very reliable and repeatable. I believe that there are some real opportunities here for automating the process which will make Klipper more accessible to users that are not programmers and find the host setup and controller firmware programming to be overwhelming. Automating this process would greatly reduce the amount of work setting up a new printer and make Klipper more approachable for new users
    3.1. Mainsail OS further reduces the remaining workload and, I think, the resulting process could be very slick.
  4. I am going to reach out to BTT and see if they are receptive to working on the CB1’s OS image. Right now the supply of CM4s still seems to be recovering from the Covid shortages (the rPi 4B seems to have a good supply and reasonable prices) so the CB1 is a reasonable choice - but once the CM4 pipeline fills up and prices go down, I don’t see the CB1 being a viable competitor against the “genuine” article, which is a shame because I like the hardware and think that with a bit of work could be a pretty decent host.

Sorry for all this - it was a lot of work and I think I learned some things that will be useful to others and I am confident that I have a process in which I can update Klipper firmware without the bother of turning my printers upside down (or at least on their sides with pillows).


In depth and to the point. I think more makes of things that work should shine while it is difficult, they should still shine.

And by shine, I basically mean hold virtue. I like fixtures and builds. The only thing that keeps me from building a machine to call my own is lack of “shine” or “virtue.” It is hard these days to make a well-rounded Printer and have it accessible to others (including myself).

For instance, I received some help a while back w/ adding another endstop for my current printer on this forum.

There was no way in “hell” I was goin’ to add another endstop as a cure to my woe. But, it got past me and it worked. Inevitably, simple things sometimes work. Endstops!


P.S. W/ all the hardware out there and SiP and SoCs, it is never just so to say press the orange button and things are cured. I have really begun to notice this as fact. I am still, after a good six months, trying to get my wiring correct and situated. I could probably get some good connectors, lengthen the wiring, and fasten it. Done but I just would like to say, w/out people assisting umpteen builds pile up and some are lost…


That build is from a person online at printables. Anyway, here is the link so I do not get anyone upset: Compact PET Filament Machine by mirabatek | Download free STL model |



Even as my prints somehow get better in time, somehow, I learn a bit. “No stress, less stress, and things will fall in place slowly…”