I’ve set up Ubuntu Server 22.04 on more than 20 VPS instances, and checking package integrity is always the very first thing I do — before anything else. The reason is simple: once I skipped this step and ran apt upgrade directly on a server that still had old PPAs from Ubuntu 20.04. The result was apt locking up completely, forcing me to restore from a snapshot. Two hours wasted. Not fun at all.
Context: When Broken Packages Happen and Why Generic Fixes Aren’t Enough
apt --fix-broken install is the first thing Google suggests, and it resolves about 60% of simple cases. But when conflicts originate from PPAs — especially unmaintained old PPAs — that command just throws an error and does nothing more. At that point, you need to understand why the conflict is happening before you can think about fixing it.
Three scenarios I encounter most often:
- PPA conflict after dist-upgrade: Upgrading Ubuntu to a new version, where an old PPA provides a package at a higher version than the official repo but its dependencies don’t match the new system.
- Half-installed package: A power outage or Ctrl+C mid-install leaves dpkg in a half-installed or half-configured state.
- Dependency hell: Two different PPAs providing different versions of a shared library like
libsslorlibc.
With PPA conflicts, plain apt typically just reports:
E: Unable to correct problems, you have held broken packages.
No additional useful information. This is where aptitude steps in.
Installing aptitude and ppa-purge
aptitude is an alternative package manager to apt, capable of analyzing dependency conflicts in greater detail and suggesting multiple resolution options instead of just reporting an error. ppa-purge specializes in removing a PPA and downgrading all packages from that PPA back to their official repo versions — two tasks in a single command.
sudo apt install aptitude
sudo apt install ppa-purge
If apt install itself is stuck due to broken packages, try resetting dpkg first:
sudo dpkg --configure -a
sudo apt-get install -f
sudo apt install aptitude ppa-purge
Debugging with aptitude: Finding the Root Cause of Conflicts
Viewing the Full Dependency Tree and Resolution Suggestions
Say you see an error related to python3-apt. Instead of using apt, run it with aptitude:
sudo aptitude install python3-apt
Unlike apt, aptitude doesn’t stop at “cannot install” — it shows multiple options, for example:
The following actions will resolve these dependencies:
Keep the following packages at their current version:
1) python3-apt [0.9.3.5ubuntu3 (now)]
Downgrade the following packages:
2) python3 [3.11.2-1 (now, ppa:deadsnakes/ppa) -> 3.10.12-1 (jammy)]
Here aptitude is suggesting two paths: keep python3-apt at its current version, or downgrade python3 from the PPA to the official version. This is critical information that apt doesn’t provide — you now know exactly which package from which PPA is causing the problem.
Using the aptitude TUI to Navigate Multiple Conflicts
When the system has multiple conflicts at once, the aptitude TUI lets you inspect them one by one:
sudo aptitude
In the TUI: press U to update the package list, u to view upgrade candidates — aptitude will highlight conflicts and let you choose a resolution with the e key (examine solution). Press q to quit.
Or use the -f flag to have aptitude automatically select the least destructive option:
sudo aptitude -f install
Checking for Held Packages
One commonly overlooked cause: packages being “held” — meaning their version is pinned. When a package is held, apt refuses to upgrade it or install new dependencies for it, causing a broken chain.
# List all currently held packages
apt-mark showhold
# Remove a hold if needed
sudo apt-mark unhold <package-name>
Resolving PPA Conflicts with ppa-purge
Identifying Which PPA Is Causing the Conflict
List all PPAs currently active on the system:
# View source files in sources.list.d
ls /etc/apt/sources.list.d/
# Filter only PPAs from Launchpad
grep -r "^deb " /etc/apt/sources.list /etc/apt/sources.list.d/ | grep "ppa.launchpad"
To find out which PPA a specific package is installed from instead of the official repo:
apt-cache policy <package-name>
The output will be clear:
python3:
Installed: 3.11.2-1~ubuntu22.04
Candidate: 3.11.2-1~ubuntu22.04
Version table:
*** 3.11.2-1~ubuntu22.04 500
500 http://ppa.launchpad.net/deadsnakes/ppa/ubuntu jammy/main amd64
3.10.12-1 500
500 http://archive.ubuntu.com/ubuntu jammy/main amd64
Immediately clear: python3 is using a version from the deadsnakes/ppa PPA, not the official Ubuntu repo.
Purging the PPA and Downgrading to the Official Version
Once you know which PPA is causing the issue, ppa-purge handles two things at once: removes the PPA from sources.list.d and downgrades all packages from that PPA back to their versions in the official repo.
sudo ppa-purge ppa:deadsnakes/ppa
After ppa-purge completes:
sudo apt update
sudo apt upgrade
sudo apt --fix-broken install
I often run into cases where even after ppa-purge, a few packages are still floating at the wrong version. Check with:
# Find packages not from the official Ubuntu repo
apt list --installed 2>/dev/null | grep -v "/ubuntu " | grep -v "Listing"
Manual Downgrade When ppa-purge Isn’t Enough
Sometimes a PPA has been removed from Launchpad but the packages are still on the machine. ppa-purge can’t handle this case because there’s no longer a remote source to compare against. You’ll need to downgrade manually:
# View available versions of a package in the current repos
apt-cache showpkg <package-name>
# Or use madison for a cleaner view
apt-cache madison <package-name>
# Downgrade to a specific version
sudo apt install <package-name>=<version>
Verification and Monitoring After the Fix
Verifying a Clean System
# Check dpkg for broken packages
sudo dpkg --audit
# Check apt database consistency
sudo apt-get check
# Full upgrade to ensure everything is in sync
sudo apt update && sudo apt full-upgrade
If dpkg --audit produces no output → no broken packages remain. If apt-get check returns exit code 0 → the apt database is consistent. These are the two checks I always run before declaring the job done.
Periodic Health Check Script
After fixing things, I add a quick check script to cron to catch issues early before they become serious problems:
#!/bin/bash
# /usr/local/bin/check-pkg-health.sh
echo "=== Package Health Check: $(date) ==="
echo "--- Broken/unconfigured packages ---"
sudo dpkg --audit
echo "--- Packages from non-official sources ---"
apt list --installed 2>/dev/null | grep -v "/ubuntu " | grep -v "Listing" | head -20
echo "--- Held packages ---"
apt-mark showhold
echo "--- Active PPAs ---"
ls /etc/apt/sources.list.d/ | grep -v ".save" | grep -v ".bak"
# Make it executable
sudo chmod +x /usr/local/bin/check-pkg-health.sh
# Run weekly on Monday morning
sudo crontab -e
# Add the line:
0 9 * * 1 /usr/local/bin/check-pkg-health.sh >> /var/log/pkg-health.log 2>&1
Quick Fixes for Common Errors
Error: “dpkg was interrupted, you must manually run…”
sudo dpkg --configure -a
Error: “The following packages have unmet dependencies”
sudo aptitude install <package> # aptitude will suggest specific resolution options
Error: apt is completely locked (usually because a previous process was killed)
sudo rm /var/lib/dpkg/lock-frontend
sudo rm /var/lib/apt/lists/lock
sudo dpkg --configure -a
The lesson I’ve taken away from debugging situations like this over and over: don’t add PPAs carelessly. Every PPA is a package source outside of Ubuntu’s control, and it can conflict with system packages at any time — especially after a dist-upgrade. If you need a newer version of software, consider using snap, flatpak, or a binary release directly from the vendor instead of a PPA. At the very least, document every PPA you add to a server and why — your future self will thank you for it.

