Debian 12 Bookworm Upgrade Report

I’ve updated the Reiterate server to the latest Debian release (bookworm). Here’s a report of everything I had to do.

A giant worm made out of books

Overall, Debian 12 is a fantastic release, the latest in a string of excellent updates. I’ve been running this server on Debian since Day One, and I’ve never regretted it.

As I’ve written before, I’m now using Ansible to configure and document my server setup. One of the reasons for that was so that I could upgrade my server cleanly. Typically, if you have a desktop running Debian, you’ll upgrade it using apt-get dist-upgrade which does the upgrade “in place,” that is, you keep everything else in your system that isn’t part of the OS. That’s convenient and it’s the option most people choose, but I like starting from scratch, for three reasons.

  1. It avoids the accumulation of cruft. When you keep upgrading in place over and over, it’s like painting your house a different color every year, just slapping a new coat on top. It works for a while but eventually you need to strip everything down to the bare wood and start fresh.

  2. It keeps me in touch with every part of my server. If I simply upgraded in place every could years, I’d forget some of the details and wind up in a situation where I had certain files in the system and I didn’t know why. By rebuilding from scratch I force myself to examine each and every part of the system.

  3. It documents my system setup. My Ansible playbook serves as a document of everything my system needs to run. I know it is a complete and sufficient recipe because I can use it to rebuild the server from scratch.

However, upgrading is not as simple as replacing “debian11” with “debian12” in my playbook. There’s always a few details that need to be fixed up, some things that don’t quite work the same way and need to be adjusted. Here’s a report of everything I needed to clean up after my Debian 12 upgrade.

Python interpreters are now externally-managed

In Debian 12, you can no longer use pip to install packages system-wide. You have to create a virtual environment, and install the packages there. This makes sense from a system management viewpoint, since you can easily break your system by installing packages into the global library space.

I was using pip to install isso, which manages my blog comments. I already had an isso user account, which I was using to keep things nice and neat, putting the comments database and config files in that user directory. I didn’t really need to do that but I felt it was “best practice.” Now that I needed to install the python modules outside of the system directories, having the account already set up seemed perspicacious.

First, I needed to get the proper packages installed to do the virtual environment installed. On the old Debian 11 install I was using python-dev but that failed with:

Package python-dev is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source
However the following packages replace it:

That’s a very odd name for a package. I guess starting with Debian 12, python2 is officially deprecated and everything is forced to be python3. This package just makes a link so that any script referring to /usr/bin/python gets python3.

The next error was

The virtual environment was not created successfully because ensurepip is not
available.  On Debian/Ubuntu systems, you need to install the python3-venv
package using the following command.

    apt install python3.11-venv

Which is the new package I needed to actually do the virtual environment install.

Next, I had issues with Ansible. Ansible is finicky about privilege escalation. I wanted to install this in my isso directory as the isso user, but Ansible doesn’t like becoming users other than root. I tried every combination of become_user, become, ansible_become_user, but I was unable to get Ansible to do this directly. I ended up doing the venv install as root, and then chown-ing everything over to isso. It’s kind of hacky but it works and I was too frustrated to try and find a cleaner solution.


The main website is Rails-based, which requires Ruby of course. I happened to be on ruby version 3.1.3, which is no longer supported anywhere, so I decided to upgrade to the current stable version 3.2.2. I use rvm to manage my ruby installs, which… it works, but it seems like there hasn’t been a lot of development work on it lately? I checked around and it doesn’t seem like there’s any good, well-supported ruby version managers out there right now.

Rvm doesn’t have any binaries pre-compiled (they’re behind on this, there’s some support for Debian 11. I offered to help compile some binaries but never got a response back). I had to compile ruby from source, and that meant I had to install some additional packages: libyaml, libreadline, and libffi. After all that, I successfully got the latest ruby installed.


Puma is the application server that sits between nginx and my Rails app. Trying to start up puma gave me an error: cannot open shared object file: No such file or directory - /home/deploy/production/vendor/bundle/ruby/3.2.0/gems/puma-6.0.2/lib/puma/

It took me a while to figure out, puma-6.0 has a dependency on libssl-1.1, which is no longer in Debian 12. The current libssl is 3.0, so I needed up upgrade to puma-6.3 which has the proper libssl dependency.


Goaccess is a small utility I use to parse my web logs and give me statistics. I was calling it with an ignore-referer flag, but the version of goaccess in bookworm changed that to ignore-referrer. I had to update my wrapper script which was erroring becuase of that.

That original misspelling should be a warning to every standard developer that details matter.


I use [fail2ban] to block script kiddies. The new version in Debian 12 was now logging warnings:

WARNING 'allowipv6' not defined in 'Definition'. Using default one: 'auto'

I fixed that by adding a section in /etc/fail2ban/fail2ban.d/local/conf

allowipv6 = auto

PAM sshd user_readenv

There’s a bug in the sshd PAM config that was spamming warnings in my log files that look like:

pam_env(sshd:session): deprecated reading of user environment enabled

Reading through the bug report, it seems harmless to fix it by removing the user_readenv flag in /etc/pam.d/sshd so I did that.

sudo flag file

Sudo is now adding a file .sudo_as_admin_successful in my home dir. Dotfile litter is one of my pet peeves, so I disabled this by adding a block into my sudoers.d config file:

# Disable ~/.sudo_as_admin_successful file
Defaults !admin_flag

And that was everything! Well, everything Debian-related. The server has been up and running on Debian 12 for several days now and I’m not seeing any new errors or warnings. Thanks again to everyone on the Debian team for putting out quality releases!

Comments and Webmentions

You can respond to this post using Webmentions. If you published a response to this elsewhere,

This article is licensed under CC BY 4.0