AI summary
Overview: This document outlines a complete procedure to migrate a server from Debian 10 to Debian 12 using the supported intermediate release. It covers pre-upgrade assessment, backup and rollback options, the two-stage upgrade process, major compatibility considerations, and the steps required to validate and stabilize the system after the migration.
Core message: The upgrade must be performed sequentially through the intermediate release, with comprehensive preparation, verified rollback mechanisms, and careful validation at each stage to avoid dependency conflicts, broken services, or unbootable systems.
Preparation and safeguards: Before changing releases, audit package state, held packages, third-party repositories, kernel modules, available disk space, and active services/ports. Ensure out-of-band console access and create reliable backups such as hypervisor or LVM snapshots and logical database dumps. Preserve critical configuration and system directories when snapshots are not available.
Staged upgrade approach: First bring the existing release fully up to date, disable external repositories, update package sources to the intermediate release, and perform a conservative then full upgrade while resolving configuration-file decisions manually. After validating the intermediate state, repeat the process to move to the target release, again handling prompts for bootloader, library, and service changes deliberately and during a maintenance window.
Compatibility and risk areas: Expect staged ABI and library transitions (C library, TLS library, Python, init system and others) that can break binaries, drivers, or integrations if jumped over. Third-party repos, DKMS-managed modules, container networking, and service unit changes commonly require attention, rebuilding, or configuration adjustments during the migration.
Post-upgrade validation and hardening: Reboot and confirm the new release and kernel, verify no failed units or package audit issues, remove obsolete packages, reconcile kernel images and modules, re-enable external repositories conservatively, re-run security baseline checks, and capture a final validated snapshot as the new rollback baseline.
How to Upgrade a Server From Debian 10 (Buster) to Debian 12 (Bookworm)
This guide covers the complete migration path from Debian 10 (Buster) to Debian 12 (Bookworm) via the mandatory intermediate release, Debian 11 (Bullseye). It addresses pre-upgrade auditing, backup and rollback architecture, the staged upgrade procedure, compatibility considerations for Bookworm, and post-upgrade verification.
1. Why You Cannot Skip Debian 11
The only supported upgrade path is sequential:
Debian 10 (Buster) → Debian 11 (Bullseye) → Debian 12 (Bookworm)
Skipping Bullseye creates nonlinearly growing dependency conflicts because core ABI transitions happen in stages.
Key reasons
- glibc 2.28 to 2.36
A two-step ABI transition. Skipping intermediate compatibility shims causes runtime failures in packages compiled against older symbol versions. - OpenSSL 1.1.1 to 3.0
These are distinct major versions with incompatible API and ABI surfaces. A staged upgrade through Bullseye (which ships 1.1.1) allows identification of applications linked against libssl1.1 before Bookworm makes them blocking. - Python 3.7 to 3.9 to 3.11
Package namespace changes, removal of distutils, and PEP 668 pip restrictions accumulate across two releases and cannot be resolved in a single jump. - systemd 241 to 252
Sandboxing defaults tightened, deprecated unit directives removed. Services with custom unit files need intermediate validation on Bullseye. - Package renames and splits
iptables/nftables integration, python-is-python3, libpcre2/libpcre3, and non-free-firmware separation. APT cannot resolve these transitions in one step.
2. Pre-Upgrade Planning
2.1 Common Failure Modes
Understanding these categories prevents the majority of upgrade failures.
| Failure Category | Description | Mitigation |
| Third-party repos | Docker, Kubernetes, Elastic, MongoDB repos are release-specific; will 404 or conflict after the switch | Disable before upgrading; re-enable after |
| DKMS modules | ZFS, NVIDIA, WireGuard tied to specific kernel ABI; break on new kernel install | Verify vendor support for target kernel |
| Held packages | apt-mark hold prevents upgrades; creates deadlocks if core libs are held | Review and unhold before starting |
| PHP version conflicts | ondrej/php and debian.org PHP packages conflict after dist-upgrade | Disable sury.org repo before Stage 1 |
| Database major versions | MariaDB 10.3 → 10.5 → 10.11; PostgreSQL independent; MySQL absent from Bookworm | Take logical dumps; validate engine compatibility |
| Insufficient disk space | APT unpacks before removing; 2–4 GB needed in /var and /usr | Ensure 4 GB free before each stage |
| conffile divergence | Modified config files prompt for resolution; silent defaults can overwrite customizations | Review diffs; never use -y blindly |
2.2 Pre-Upgrade Audit Commands
# Check for broken package states
dpkg –audit
dpkg –configure -a && apt –fix-broken install
# List held packages
apt-mark showhold
# Inventory all active repositories
grep -rh “^deb” /etc/apt/sources.list /etc/apt/sources.list.d/ | sort | uniq
# Check available disk space
df -h
# Identify DKMS modules
dkms status
# Save baseline for post-upgrade comparison
systemctl list-units –type=service –state=active > /root/services-before.txt
ss -tulpn > /root/ports-before.txt

3. Backup and Rollback Architecture
Never commence a Debian upgrade without a verified, tested rollback path. An upgrade that breaks the bootloader or produces an unbootable system is only recoverable via out-of-band access.
Verify IPMI, iDRAC, or iLO console access before beginning. Attempting a major distribution upgrade over SSH without console fallback is an unacceptable operational risk.
3.1 Hypervisor Snapshots (Preferred)
For KVM, VMware, or Proxmox, a consistent snapshot taken immediately before any repository modification is the fastest and most reliable rollback mechanism.
virsh snapshot-create-as –domain myvm \
–name “pre-debian11-$(date +%Y%m%d)” \
–atomic
3.2 LVM Snapshots
Ensure 15–25% free space in the volume group before creating a snapshot, as the snapshot grows with writes during the upgrade.
lvcreate -L20G -s -n root_snap_prebullseye /dev/mapper/vg0-root
# To roll back:
systemctl isolate rescue.target
lvconvert –merge /dev/vg0/root_snap_prebullseye && reboot
3.3 Database Logical Dumps
Always take logical (SQL) dumps rather than binary file copies. Major version upgrades can change on-disk storage formats.
# MySQL / MariaDB
mysqldump –all-databases –single-transaction –routines \
–triggers –events > /backup/all_dbs_$(date +%Y%m%d).sql
# PostgreSQL
pg_dumpall -U postgres > /backup/postgres_all_$(date +%Y%m%d).sql
3.4 Critical Directories (When Snapshots Are Unavailable)
| Directory | Contents | Priority |
| /etc | All system configurations | Critical |
| /var/lib/dpkg | Package database | Critical |
| /boot | Kernel, initrd, GRUB config | Critical |
| /var/lib/mysql or /var/lib/postgresql | Database files (prefer logical dump) | Critical |
| /var/www | Web content | High |
| /opt, /srv | Third-party apps, service data | Medium |
4. Stage 1: Debian 10 to Debian 11 (Bullseye)
4.1 Fully Update Debian 10 First
The upgrade must start from a fully current Buster installation. Upgrading from a system with accumulated pending updates compounds the dependency resolution problem.
apt update && apt upgrade && apt full-upgrade
apt autoremove –purge && apt autoclean
reboot
# Confirm the latest point release (should be 10.13)
lsb_release -a && cat /etc/debian_version
4.2 Disable Third-Party Repositories
for f in /etc/apt/sources.list.d/*.list; do
mv “$f” “${f}.disabled”
done
4.3 Update sources.list
Replace all instances of buster with bullseye:
deb http://deb.debian.org/debian bullseye main contrib non-free
deb http://security.debian.org/debian-security bullseye-security main contrib non-free
deb http://deb.debian.org/debian bullseye-updates main contrib non-free
apt update
Verify no 404 or GPG errors before proceeding.
4.4 Perform the Upgrade
# Phase 1: in-place replacements only
apt upgrade –without-new-pkgs
# Phase 2: full dependency resolution
apt full-upgrade
When APT prompts about modified configuration files:
- Press D to view the diff.
- Keep your version (N) for customized files, such as:
- sshd_config
- nginx.conf
- php.ini
- Accept the maintainer version (Y) for files you have not changed.
Never use -y or DEBIAN_FRONTEND=noninteractive without a reviewed conffile policy.
If APT reports that critical packages (database engine, web server, VPN daemon) need to be removed, stop and investigate before proceeding.
4.5 Post-Bullseye Validation
reboot
lsb_release -a
uname -r
dpkg –audit
systemctl –failed –no-pager
journalctl -p err -b –no-pager | head -50
diff /root/ports-before.txt <(ss -tulpn)
# Cleanup
apt autoremove –purge
dpkg -l | grep ‘^rc’ | awk ‘{print $2}’ | xargs apt purge -y
apt clean
Expected:
- Debian 11
- Kernel 5.10.x
- No failed units
- No package audit errors
4.6 Bullseye Compatibility Notes
iptables/nftables
Debian 11 defaults to iptables-nft.
update-alternatives –display iptables
- PHP
Bullseye ships PHP 7.4. Test applications that relied on PHP 7.3 behavior. - Python
python3 now resolves to 3.9. Validate virtual environments and library dependencies. - OpenSSL
Still 1.1.1 in Bullseye. The 3.0 transition comes in Stage 2.
5. Stage 2: Debian 11 to Debian 12 (Bookworm)
Do not begin Stage 2 until:
- dpkg –audit returns no output
- systemctl –failed shows nothing
- all critical services are operational
- a fresh Bullseye snapshot exists
5.1 Disable Third-Party Repositories Again
for f in /etc/apt/sources.list.d/*.list; do
mv “$f” “${f}.disabled”
done
5.2 Update sources.list for Bookworm
deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
deb http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware
timedatectl status
apt update
5.3 Perform the Upgrade
apt upgrade –without-new-pkgs
apt full-upgrade
5.4 Bookworm-Specific Upgrade Prompts
GRUB on software RAID grub-install /dev/sda && grub-install /dev/sdb && update-grub
OpenSSL 3 transition
find /usr/bin /usr/sbin /usr/local/bin -type f -executable \
-exec ldd {} 2>/dev/null \; | grep libssl1.1 | sort | uniq
SSH reconfiguration. Bookworm deprecates RSA keys below 2048 bits and removes some legacy cipher suites.
Service restarts. Prefer manually controlling restart timing during maintenance windows.
5.5 Post-Bookworm Validation
reboot
lsb_release -a && cat /etc/debian_version
uname -r
dpkg –audit
systemctl –failed –no-pager
journalctl -p err -b –no-pager | head -100
# Find obsolete packages
aptitude search ‘~o’
# Cleanup
apt autoremove –purge
dpkg -l | grep ‘^rc’ | awk ‘{print $2}’ | xargs apt purge -y
apt clean
Expected:
- Debian 12.x
- Kernel 6.1.x
- No failed services
- No audit errors

6. Bookworm Compatibility Reference
6.1 OpenSSL 3.0
OpenSSL 3.0 introduced substantial compatibility changes.
Legacy provider configuration
[openssl_init]
providers = provider_sect
[provider_sect]
default = default_sect
legacy = legacy_sect
[default_sect]
activate = 1
[legacy_sect]
activate = 1
Additional notes
- ENGINE API deprecated
- HSM integrations may fail until updated
- Self-signed certificates require:
- subjectAltName
- keyUsage
- extendedKeyUsage
6.2 PHP 8.2
Removed functionality
- create_function()
- each()
- Dynamic properties without #[AllowDynamicProperties]
Other changes
- mbstring.func_overload removed
- @ no longer suppresses fatal errors
- Type coercion behavior changed significantly
6.3 Python 3.11
- PEP 668 blocks system-wide pip install
- distutils deprecated
- asyncio event loop defaults changed
6.4 nftables and Container Networking
nft list ruleset
iptables-legacy -L -n
docker network inspect bridge
docker run –rm alpine ping -c 2 8.8.8.8
Validate:
- Docker networking
- Kubernetes kube-proxy
- Calico
- Flannel
6.5 Re-enabling Docker After Bookworm
echo “deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/debian bookworm stable” \
> /etc/apt/sources.list.d/docker.list
apt update
apt install docker-ce docker-ce-cli containerd.io \
docker-buildx-plugin docker-compose-plugin
docker info && docker run –rm hello-world
6.6 Kubernetes Nodes
# Before upgrade
kubectl drain <node-name> –ignore-daemonsets –delete-emptydir-data
# After upgrade
kubectl uncordon <node-name>
kubectl get nodes
kubectl describe node <node-name>
7. Final Cleanup and Hardening
7.1 Kernel Cleanup
dpkg -l | grep linux-image
uname -r
apt purge linux-image-5.10.x-x-amd64
update-grub
7.2 Security Baseline Verification
# SSH hardening
sshd -T | grep -E ‘permitrootlogin|passwordauthentication|pubkeyauthentication’
# Firewall
nft list ruleset | head -30
# PAM
pam-auth-update –status
# World-writable files
find /etc /usr/bin /usr/sbin -perm -002 -type f 2>/dev/null
7.3 Re-enabling Third-Party Repositories
mv /etc/apt/sources.list.d/docker.list.disabled \
/etc/apt/sources.list.d/docker.list
apt update
apt –dry-run upgrade
7.4 Final Snapshot
After completing all validation, take a snapshot or backup of the clean Bookworm system. This becomes the new rollback baseline and serves as evidence of the post-upgrade state for change management purposes.
Can I upgrade directly from Debian 10 to Debian 12?
The only supported path is:
Debian 10 (Buster) → Debian 11 (Bullseye) → Debian 12 (Bookworm)
Skipping Debian 11 can break package dependency resolution, OpenSSL transitions, Python runtime compatibility, and system libraries such as glibc.
How long does the upgrade usually take?
| System Type | Estimated Time |
| Small VM | 30–60 minutes |
| Production web server | 1–2 hours |
| Database server | 2–4 hours |
| Kubernetes node | Depends on the workload evacuation time |
Is it safe to upgrade over SSH?
Only if you have verified out-of-band console access, such as:
- IPMI
- iDRAC
- iLO
- Hypervisor console access
A failed kernel, GRUB issue, or networking problem can permanently disconnect SSH access during the upgrade.
Should I use apt upgrade or apt full-upgrade?
Both are required.
apt upgrade –without-new-pkgs
apt full-upgrade
Why must third-party repositories be disabled first?
Third-party repositories are usually release-specific and can introduce:
- dependency conflicts
- broken package chains
- outdated signing keys
- incompatible package versions
Disable them before each stage and re-enable them individually afterward.
How much free disk space is required?
| Mount Point | Recommended Free Space |
| / | 4 GB |
| /var | 2–4 GB |
| /boot | Space for multiple kernels/initramfs images |
Do I need to reboot between upgrade stages?
Yes. Reboot after:
- Debian 10 → Debian 11
- Debian 11 → Debian 12
This ensures the new kernel, libraries, and services load correctly.