The Day Python 3.10 "broke" my aaPanel on Podman (and how i fixed it)
Back to blog

The Day Python 3.10 "broke" my aaPanel on Podman (and how i fixed it)

6/7/2026 · 3 min · Infrastructure

The Day Python 3.10 "Broke" my aaPanel on Podman (and how I fixed it)#

If you work with infrastructure, you know that an "unhealthy" container status is the start of a headache. Recently, I came across a classic scenario for anyone who likes to keep things updated but forgets that the isolated environment of a container might not be ready for the future.

In this post, I will detail how I diagnosed and solved a critical syntax error in aaPanel running via Podman on Rocky Linux, which culminated in the decision to migrate the panel to the physical host.

The "zombie" container#

It all started when I noticed my aaPanel container was running, but the panel simply wouldn't load. Running a podman ps, the status was discouraging: Up 33 hours (unhealthy).

The first step was to investigate the health check:

podman inspect --format='{{json .State.Health}}' aapanel

The output showed a FailingStreak of almost 20,000 attempts and an ExitCode: 1. The internal service was dead.

The Python generational conflict#

I tried to manually restart the panel inside the container with the bt restart command. That's when the culprit appeared. The error log spat out a traceback that any Python developer recognizes:

TypeError: unsupported operand type(s) for |: 'type' and '_GenericAlias'

What happened? The aaPanel code (version 11.5.0) was updated to use modern Python 3.10+ Type Hints, specifically the union operator | (e.g., dict | List). However, the base image I was using (aapanel:lib) was still running Python 3.7.9.

Python 3.7 looks at the | character in a type definition and doesn't know what to do. The panel tried to start, ran into this syntax error in the common.py and public/__init__.py libraries, and died.

The command line "surgery" attempts#

Since I needed the panel up quickly, I tried an aggressive approach using sed to remove the problematic type hints:

# Attempt to clear the type unions that broke Python 3.7
podman exec -it aapanel sed -i -E "s/: [^=,)]*\|[^=,)]*//g" /www/server/panel/class/public/common.py

The result? sed is a powerful but dangerous tool. It ended up corrupting regular expressions (Regex) that also used the | character, resulting in an even worse SyntaxError: invalid syntax.

I tried to restore the environment with the official update script:

podman exec -it aapanel curl -sSO http://download.bt.cn/install/update_panel.sh
podman exec -it aapanel bash update_panel.sh

The script updated the panel to v11.5.0 but didn't update the Python binary (the internal pyenv), maintaining the compatibility error.

The strategy shift: reinstallation and migration#

I realized that trying to "patch" Python 3.7 to run 2026 code was a waste of time. I decided to remove the old container and spin up the latest image (aapanel/aapanel:latest).

When trying to bring up the new image with Podman, I ran into a volume mount error: Error: statfs /www/server/data: no such file or directory

This reminded me that, on Rocky Linux, we need to ensure the persistence directories exist on the host before mapping:

sudo mkdir -p /www/wwwroot /www/server/data /www/server/panel/vhost
sudo chown -R $USER:$USER /www/wwwroot /www/server/data /www/server/panel/vhost

Production takeaways#

Even though I managed to start the new container, the experience showed me that for the complexity of aaPanel - which manages compilers, multiple services (Nginx, PHP, MySQL), and has its own Python environment - running it directly on the host (Bare Metal) offers stability that a container sometimes hinders.

If you are facing TypeError issues in aaPanel within old containers, my recommendation is: don't fight Python syntax.

  1. Backup your /www/wwwroot files.
  2. Install aaPanel directly on Rocky Linux using the official script:
sudo yum install -y wget && wget -O install.sh http://www.aapanel.com/script/install_6.0_en.sh && sudo bash install.sh aapanel
  1. Open ports in Firewalld: Don't forget that Rocky is strict. Ports like 7800 (or 8888), 80, 443, and 888 need to be manually opened with firewall-cmd.

At the end of the day, the infrastructure should serve the project, not become the project. Sometimes, the simplest path (native installation) is what ensures you sleep peacefully without "unhealthy" containers at 3 AM.

Enjoy the read? If you've also battled with Python versions in control panel containers, drop a comment on how you solved it!

Was this article helpful?

Leave a quick reaction to help prioritize future technical guides:

CC BY-NC

This post is licensed under CC BY-NC.

Comments

Join the discussion below.

0 comments