Debian 13: APT is broken (“unmet dependencies”) — fix it without reinstalling anything

Was this helpful?

You run apt upgrade and Debian replies with that cheery, passive-aggressive classic: “unmet dependencies.” Now nothing installs, half your packages are “kept back,” and your terminal feels like it’s judging your life choices.

This is fixable. Almost always. And you can fix it without reinstalling Debian, without “just use Docker,” and without spelunking blindly through random forum pastebins. We’ll diagnose what’s actually broken (sources, pins, holds, dpkg state, or a partial upgrade), then repair it with deliberate steps you can explain to a change-review board.

What “unmet dependencies” really means (and what it doesn’t)

APT isn’t “broken” because it printed an error. It’s broken because APT can’t find a consistent set of package versions that satisfy:

  • What you asked for (install/upgrade/remove),
  • What’s already installed,
  • What your repositories offer,
  • And what your policy says is allowed (pins, priorities, “held” packages).

In practice, “unmet dependencies” usually boils down to one of these:

  1. Mixed repositories: you’ve got Debian stable + testing + unstable, or a third-party repo built for a different Debian release. APT is not a therapist; it can’t reconcile contradictory commitments.
  2. Partial upgrade: some packages advanced to newer versions but their dependency chain didn’t, often due to interrupted upgrades, pinned packages, or changed repo availability.
  3. dpkg state is inconsistent: packages left unconfigured, half-installed, or diversions messed up. APT delegates the actual unpack/configure to dpkg; if dpkg is stuck, APT is stuck.
  4. Package holds: a core library is held, so everything that depends on it can’t move.
  5. Pinning (preferences) has made your system “choose” a version that doesn’t exist in the repos you now have enabled.
  6. Architecture and multiarch oddities: mixing i386/amd64 without intent, or stale foreign-arch packages lingering.

What it’s not: a reason to reinstall, a reason to start deleting random packages, or an invitation to mash apt --fix-broken install until it agrees with you. We’ll use that tool, but only when we understand what it’s going to do.

Fast diagnosis playbook (first/second/third)

If this is production-ish, you want signal fast. Here’s a triage order that works because it separates “APT can’t compute a solution” from “dpkg can’t apply the solution.”

First: confirm the failure mode

  • Can APT compute a plan but dpkg fails to apply it? (dpkg lock, interrupted config, broken maintainer script)
  • Or can APT not compute a plan at all? (repo mix, pins, holds, missing versions)

Second: validate repositories and release alignment

  • Confirm you’re actually on Debian 13 and your sources target Debian 13 (or its codename), consistently.
  • Look for third-party repos pointing to the wrong suite.
  • Look for testing/unstable accidentally enabled.

Third: identify what blocks the dependency graph

  • Held packages.
  • Pinning/preferences (including “origin” pins from third-party repos).
  • One “keystone” package (often libc6, libstdc++, openssl, systemd) that’s stuck at an incompatible version.

Fourth: fix dpkg state if needed

  • Configure unpacked packages.
  • Resolve broken maintainer scripts.
  • Clean locks carefully (rarely needed, but when it is, it is).

Fifth: apply a controlled full upgrade

  • Once repos and policy are sane, do a full-upgrade (or the modern equivalent) to let APT do required removals/installs.
  • Review removals like you’re about to sign a purchase order.

Interesting facts & context (the stuff that matters at 2 a.m.)

  1. APT is older than most cloud tooling. The APT project dates back to the late 1990s; its design assumed unreliable networks and slow mirrors, so it’s picky about consistency and metadata.
  2. dpkg predates APT. dpkg does the actual install/unpack/configure; APT is the planner and fetcher. If dpkg is stuck, APT can only complain politely.
  3. “Depends” isn’t the whole story. Debian packages also use Pre-Depends, which forces installation order and can make the system feel “wedged” if a core package can’t transition.
  4. “Stable” is a promise, not a magical property. Debian stable aims for compatibility, but if you mix suites (stable/testing/unstable), you opt out of that promise.
  5. Pinning is both a scalpel and a chainsaw. APT preferences were designed to allow controlled mixing (like security repos or backports), but a single bad pin can freeze half the OS.
  6. Third-party repos frequently target Ubuntu first. Many vendors publish Debian packages, but their dependency assumptions often align with Ubuntu releases, and that’s how you end up with “Depends: libsslX (>= …) but it is not installable.”
  7. Release files are a trust boundary. APT uses signed repository metadata. When signatures break (expired key, wrong suite), APT will refuse updates—which can indirectly cause unmet dependencies during upgrades.
  8. Multiarch wasn’t always a thing. Mixing architectures became mainstream later; lingering i386 packages on amd64 can create dependency constraints that look unrelated.
  9. “Kept back” is usually APT being careful. APT avoids removals by default; an upgrade may require package transitions that only full-upgrade will accept.

Before you touch anything: safety rails

If this is a laptop, your “blast radius” is mostly your own afternoon. If it’s a server, you want reversible moves.

  • Start a root shell with a log. You want a transcript when you later explain what changed.
  • Snapshot if you can. LVM snapshot, ZFS snapshot, or VM snapshot. If you have none, at least back up /etc/apt and /var/lib/dpkg.
  • Do not remove core packages blindly. If APT proposes removing systemd, ssh, libc6, or your kernel meta-package, stop and understand why.

One dry truth from operations: “fast” fixes that you can’t explain are just future incidents delivered early.

Paraphrased idea from Richard Cook (resilience engineering): “Success and failure often come from the same everyday work; they’re not separate worlds.”

Practical repair tasks (commands, outputs, decisions)

Below are concrete tasks. Each includes: command, realistic output, what it means, and what decision you make next. Do them in order unless you already know which failure mode you’re in.

Task 1: Confirm the OS release and codename

cr0x@server:~$ cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 13 (trixie)"
NAME="Debian GNU/Linux"
VERSION_ID="13"
VERSION="13 (trixie)"
VERSION_CODENAME=trixie
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"

Meaning: You’re on Debian 13, codename trixie. Repos must match Debian 13 suites (typically trixie, trixie-updates, trixie-security).

Decision: If your sources reference a different codename (like bookworm, sid, or Ubuntu names), fix sources first. Do not try to “force install” your way out.

Task 2: List APT sources, including deb822 and legacy files

cr0x@server:~$ find /etc/apt/sources.list /etc/apt/sources.list.d -maxdepth 1 -type f -print -exec sed -n '1,160p' {} \;
/etc/apt/sources.list
deb http://deb.debian.org/debian trixie main contrib non-free non-free-firmware
deb http://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware
/etc/apt/sources.list.d/vendor.list
deb http://packages.vendor.example/debian bookworm main

Meaning: You have a third-party repo targeting bookworm (Debian 12) on a Debian 13 system. That’s a prime cause of unmet dependencies or downgrades.

Decision: Disable or correct that repo before doing anything else. If the vendor doesn’t support Debian 13, you either replace the software or accept you’re doing unsupported dependency surgery.

Task 3: Update package lists and read the errors like evidence

cr0x@server:~$ sudo apt update
Hit:1 http://deb.debian.org/debian trixie InRelease
Hit:2 http://deb.debian.org/debian trixie-updates InRelease
Hit:3 http://security.debian.org/debian-security trixie-security InRelease
Get:4 http://packages.vendor.example/debian bookworm InRelease [3,215 B]
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
All packages are up to date.

Meaning: APT happily fetched metadata for both Debian 13 and vendor’s Debian 12 repo. “All packages are up to date” only means your metadata is fresh, not that your system is consistent.

Decision: Continue to policy inspection. Don’t upgrade yet.

Task 4: Check what suites APT thinks it can install from (policy view)

cr0x@server:~$ apt-cache policy | sed -n '1,80p'
Package files:
 100 /var/lib/dpkg/status
     release a=now
 500 http://packages.vendor.example/debian bookworm InRelease
     release o=Vendor,a=bookworm,n=bookworm,l=Vendor,c=main,b=amd64
 500 http://security.debian.org/debian-security trixie-security InRelease
     release o=Debian,a=trixie-security,n=trixie-security,l=Debian-Security,c=main,b=amd64
 500 http://deb.debian.org/debian trixie-updates InRelease
     release o=Debian,a=trixie-updates,n=trixie-updates,l=Debian,c=main,b=amd64
 500 http://deb.debian.org/debian trixie InRelease
     release o=Debian,a=trixie,n=trixie,l=Debian,c=main,b=amd64

Meaning: The vendor repo has equal priority (500) to Debian. That means APT may select vendor packages when versions look “better,” pulling in dependencies that don’t exist in Debian 13.

Decision: If the vendor repo is mismatched, disable it now; if it’s correct but too “attractive,” pin it lower or to specific packages only.

Task 5: Identify the exact dependency conflict

cr0x@server:~$ sudo apt install vendor-agent
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Some packages could not be installed. This may mean that you have
requested an impossible situation or if you are using the unstable
distribution that some required packages have not yet been created
or been moved out of Incoming.
The following information may help to resolve the situation:

The following packages have unmet dependencies:
 vendor-agent : Depends: libssl3 (>= 3.0.0) but it is not installable
               Depends: libc6 (>= 2.37) but 2.36-9 is to be installed
E: Unable to correct problems, you have held broken packages.

Meaning: The package wants a libc6 newer than what APT is prepared to install, and it thinks libssl3 isn’t installable under your current repo/pin setup.

Decision: Don’t brute force install. Determine which repo is offering which versions of libc6 and libssl3, and whether you’re stuck on an older libc due to holds/pins or partial upgrades.

Task 6: Check for held packages (classic foot-gun)

cr0x@server:~$ apt-mark showhold
libc6
linux-image-amd64

Meaning: libc6 being held is a big deal. Holding libc6 can freeze the world because everything depends on it either directly or indirectly.

Decision: If the hold was intentional for a specific change window, schedule the window and unhold. If it was accidental (or inherited from a golden image), unhold now.

Task 7: Unhold packages (only after understanding why they were held)

cr0x@server:~$ sudo apt-mark unhold libc6 linux-image-amd64
Canceled hold on libc6.
Canceled hold on linux-image-amd64.

Meaning: APT is now allowed to upgrade these packages.

Decision: Re-run the dependency calculation. If this was the sole blocker, your “unmet dependencies” may evaporate.

Task 8: Look for pinning and preferences that force odd choices

cr0x@server:~$ grep -R --line-number --no-messages '' /etc/apt/preferences /etc/apt/preferences.d 2>/dev/null
/etc/apt/preferences.d/00-local-pin:1:Package: *
/etc/apt/preferences.d/00-local-pin:2:Pin: release a=bookworm
/etc/apt/preferences.d/00-local-pin:3:Pin-Priority: 990

Meaning: Someone pinned the entire system toward bookworm. On Debian 13, that’s basically telling APT to prefer Debian 12 packages. That’s how you get FrankenDebian.

Decision: Remove or adjust the pin. If you need pinning, pin only the specific package(s) you need, not Package: *.

Joke #1: Pinning Package: * is like fixing a squeaky door by moving the entire house two inches to the left.

Task 9: Check candidate versions for the conflicting packages

cr0x@server:~$ apt-cache policy libc6 libssl3 vendor-agent
libc6:
  Installed: 2.36-9
  Candidate: 2.39-5
  Version table:
     2.39-5 500
        500 http://deb.debian.org/debian trixie/main amd64 Packages
 *** 2.36-9 100
        100 /var/lib/dpkg/status
libssl3:
  Installed: (none)
  Candidate: 3.3.1-1
  Version table:
     3.3.1-1 500
        500 http://deb.debian.org/debian trixie/main amd64 Packages
vendor-agent:
  Installed: (none)
  Candidate: 1.8.2-4
  Version table:
     1.8.2-4 500
        500 http://packages.vendor.example/debian bookworm/main amd64 Packages

Meaning: Debian 13 offers newer libc6 and libssl3. The vendor agent is coming from a Debian 12 repo, but its deps could still be satisfiable if APT isn’t pinned/held into older versions.

Decision: Fix the vendor repo suite first (or disable it). Then do a controlled full upgrade so libc6 transitions cleanly.

Task 10: Temporarily disable a mismatched third-party repo

cr0x@server:~$ sudo mv /etc/apt/sources.list.d/vendor.list /etc/apt/sources.list.d/vendor.list.disabled
cr0x@server:~$ sudo apt update
Hit:1 http://deb.debian.org/debian trixie InRelease
Hit:2 http://deb.debian.org/debian trixie-updates InRelease
Hit:3 http://security.debian.org/debian-security trixie-security InRelease
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done

Meaning: You removed the incompatible package universe from consideration. That alone often resolves “impossible situation” errors.

Decision: Now align your installed packages with Debian 13 by upgrading fully. Later, re-enable the vendor repo only if it has a Debian 13 suite.

Task 11: Check dpkg for half-configured/half-installed packages

cr0x@server:~$ dpkg -C
The following packages are only half configured, probably due to problems
configuring them the first time. The configuration should be retried using
dpkg --configure <package> or the configure menu option in dselect:
 systemd 252.22-1  system and service manager

Meaning: dpkg is reporting an inconsistent state. Even if APT can compute a plan, it may fail until dpkg finishes configuration.

Decision: Repair dpkg state before attempting larger upgrades.

Task 12: Configure pending packages

cr0x@server:~$ sudo dpkg --configure -a
Setting up systemd (252.22-1) ...
Created symlink /etc/systemd/system/multi-user.target.wants/cron.service → /lib/systemd/system/cron.service.
Processing triggers for dbus (1.14.10-1) ...
Processing triggers for libc-bin (2.36-9) ...

Meaning: dpkg completed pending configuration and ran triggers. This is foundational.

Decision: Re-run the upgrade planning step. If dpkg errors out here, stop and fix that error specifically (often a broken maintainer script or missing file).

Task 13: Let APT fix missing dependencies (with eyes open)

cr0x@server:~$ sudo apt-get -f install
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Correcting dependencies... Done
The following additional packages will be installed:
  libpam-systemd systemd-sysv
The following packages will be upgraded:
  libpam-systemd systemd-sysv
2 upgraded, 0 newly installed, 0 to remove and 17 not upgraded.
Need to get 1,214 kB of archives.
After this operation, 0 B of additional disk space will be used.
Do you want to continue? [Y/n] y
...

Meaning: APT found a minimal set of upgrades to restore consistency. Note it didn’t propose removals. Good sign.

Decision: Accept if the changes are reasonable. If it proposes removals of core services, stop and reconsider your repo alignment and pins.

Task 14: Run a full upgrade to allow transitions

cr0x@server:~$ sudo apt full-upgrade
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Calculating upgrade... Done
The following packages will be upgraded:
  base-files libc6 libc-bin libssl3 openssh-client openssh-server
The following packages will be installed:
  linux-image-6.12.0-1-amd64 linux-headers-6.12.0-1-amd64
The following packages will be removed:
  linux-image-6.1.0-18-amd64
8 upgraded, 2 newly installed, 1 to remove and 0 not upgraded.
Need to get 76.4 MB of archives.
After this operation, 312 MB of additional disk space will be used.
Do you want to continue? [Y/n] y
...

Meaning: This is a normal kernel transition: install new kernel, remove old one. libc6 and libssl3 upgrades are coherent with Debian 13.

Decision: Proceed. If you’re remote, ensure you won’t brick SSH: keep an active session, consider a second session, and don’t kill the network mid-upgrade.

Task 15: If APT still can’t resolve, ask it to explain why

cr0x@server:~$ apt -o Debug::pkgProblemResolver=yes install vendor-agent
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
Starting pkgProblemResolver with broken count: 0
Investigating (0) vendor-agent:amd64 < none -> 1.8.2-4 @un puN Ib >
Broken vendor-agent:amd64 Depends on libc6:amd64 >= 2.37
  Considering libc6:amd64 100 as a solution to vendor-agent:amd64 9999
  Rejected libc6:amd64 100 because of held packages or policy
E: Unable to correct problems, you have held broken packages.

Meaning: The debug output shows rejection due to “policy” (pins/preferences) or holds. You now know where to look: apt-mark showhold and /etc/apt/preferences*.

Decision: Remove the policy constraint, then retry. Don’t randomly install older versions to appease the resolver.

Task 16: Inspect dependency chain for a specific package

cr0x@server:~$ apt-cache depends vendor-agent | sed -n '1,80p'
vendor-agent
  Depends: libc6
  Depends: libssl3
  Depends: systemd
  Depends: adduser
  Depends: ca-certificates

Meaning: This is not exotic; it’s a typical agent relying on core libs and systemd.

Decision: If core libs are blocked, fix core libs first. Don’t treat the agent as the problem; treat it as the messenger.

Task 17: Clean up stale packages and caches (after recovery)

cr0x@server:~$ sudo apt autoremove --purge
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following packages will be REMOVED:
  linux-image-6.1.0-18-amd64*
0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
After this operation, 287 MB disk space will be freed.
Do you want to continue? [Y/n] y
...

Meaning: You’re cleaning old kernels or orphaned deps. This doesn’t fix dependency problems; it reduces clutter after you fix them.

Decision: Safe after you confirm you have a bootable new kernel installed. On servers, keep at least one known-good old kernel until after a reboot.

Task 18: Verify system health after the upgrade

cr0x@server:~$ sudo systemctl is-system-running
running

Meaning: Systemd sees the system as healthy. If it returns degraded, check failed units.

Decision: If degraded, run systemctl --failed, fix service issues, then reboot on your schedule (kernel/libc upgrades often warrant one).

Joke #2: “Unmet dependencies” is APT’s way of saying, “I can do anything… except agree with your past self.”

Three corporate-world mini-stories (why this happens in grown-up environments)

Incident #1: The wrong assumption (“It’s just one repo line”)

They had a fleet of Debian servers built from a golden image that lived for years. The image was “mostly stable,” which in corporate language means nobody wanted to be the person who touched it. A new observability agent was required for an audit, and the vendor provided a repo snippet. Someone added it, ran apt update, then installed the agent. It worked. On one box.

Two weeks later, an automated security job ran apt upgrade on the fleet. APT started pulling a newer lib from the vendor repo on some hosts—only the ones that had a certain optional package installed. Those machines ended up with a partial upgrade: a couple of libraries advanced, but core packages stayed back due to holds that were introduced years earlier to freeze kernels during a bad driver era.

The symptom was classic: “Depends: libc6 (>= …) but … is to be installed.” SSH still worked, but the audit agent didn’t install on half the fleet, and the compliance report flagged them as missing required telemetry. That’s how “unmet dependencies” becomes a business problem.

The wrong assumption: that a single vendor repo line is “local” to the vendor package. It wasn’t. Repos are not scoped. The moment you add a repo, you add an entire competing universe of packages that APT may consider for upgrades unless you pin it down.

The fix was boring and correct: disable the vendor repo by default, then add a pin that allowed only the agent packages (and their exact dependencies) to come from that origin. The rest stayed pure Debian. They also removed libc holds after a planned reboot window. The incident ended not with heroics, but with a policy file and a checklist.

Incident #2: An optimization that backfired (“Let’s cache packages on a local mirror”)

A different company tried to be smart. They built a local APT proxy to reduce external bandwidth and speed up provisioning. Good instinct. Then they “optimized” further by mirroring only a subset of repositories: main, no security, and only amd64. The reasoning was: “We only install from main, and security comes later.” Security, famously, does not come later.

During a routine upgrade wave, some machines were pointed at the local mirror, others at the public Debian mirrors. A handful of packages had newer versions in security and updates, and those versions required library transitions not present in the truncated mirror. The result was a split-brain package world: identical hostnames in different racks, but different candidate versions. Dependency resolution failed on the local-mirror hosts.

The team tried to “fix” it by forcing individual packages with apt install package=version. That worked until it didn’t: pinned versions drifted, and the next upgrade cycle reintroduced conflicts. The optimization created a new class of outages: the mirror itself became an unreliable source of truth.

The eventual repair was: mirror the full set of suites used in production (including security), keep metadata consistent, and enforce repo configuration via configuration management. The cost was extra disk. The benefit was that upgrades stopped being a coin toss.

Incident #3: The boring practice that saved the day (“We snapshot before upgrades”)

A storage-heavy environment (think: data processing nodes) ran Debian with ZFS-on-root. They had a rule: before any OS upgrade run, take a boot-environment snapshot. It wasn’t glamorous. It was a line in their runbook and a pre-upgrade hook in their automation.

One night, a partial upgrade happened anyway: a network flap during package downloads. dpkg ended up with a core package unpacked but not configured. APT errors cascaded. The node was still reachable, but services were stuck restarting, and the on-call person was juggling multiple alerts.

Instead of improvising, they followed the boring path. They used dpkg to configure pending packages, ran apt-get -f install, and reattempted the full upgrade. When a maintainer script failed due to a local customization, they didn’t hack it in-place; they rolled back to the snapshot, corrected the customization, and reran the upgrade.

It wasn’t dramatic, which is the point. The snapshot turned “unmet dependencies” from “maybe rebuild the node” into “repeatable repair.” They still fixed the underlying issue, but they did it without panic.

Common mistakes: symptom → root cause → fix

1) Symptom: “Depends: X but it is not installable” right after adding a vendor repo

Root cause: The repo targets a different Debian release (or Ubuntu), or you’re missing required components (like contrib, non-free-firmware) for your needs.

Fix: Disable the repo; confirm correct suite/codename; re-enable only if it matches Debian 13. If you must keep it, pin it to specific packages, not the whole universe.

2) Symptom: “You have held broken packages”

Root cause: Explicit holds via apt-mark hold, or pins that effectively hold a version.

Fix: apt-mark showhold, then unhold intentionally. Inspect /etc/apt/preferences* for broad pins.

3) Symptom: APT suggests removing half your desktop / your SSH server

Root cause: Mixed suites (stable + testing + unstable), or a third-party repo that replaces core packages, or an accidental architecture mix.

Fix: Make sources consistent. If you want Debian 13 stable, use only Debian 13 suites (plus backports if intentionally configured). Remove foreign-arch packages if accidental.

4) Symptom: “dpkg was interrupted, you must manually run dpkg –configure -a”

Root cause: Interrupted install/upgrade, abrupt reboot, killed apt process, or a maintainer script hang.

Fix: Run sudo dpkg --configure -a. If it fails, read the exact error, fix the file/service it references, then rerun. Don’t keep retrying without changing anything.

5) Symptom: “E: Could not get lock /var/lib/dpkg/lock-frontend”

Root cause: Another apt/dpkg process is running (or one crashed and left confusion).

Fix: Identify the process with ps, wait if legitimate, or stop it if it’s wedged. Don’t delete lock files as your first move; that’s how you corrupt state.

6) Symptom: “Packages have been kept back” for core components

Root cause: Upgrade requires installing new packages/removing old ones; APT’s conservative upgrade won’t do it.

Fix: Use apt full-upgrade and review the plan. If the plan is insane, your sources/pins are still wrong.

7) Symptom: libc6 or openssl transitions fail

Root cause: One repo is missing (security/updates), or third-party packages require versions from a different suite, or you have pinned older libc/openssl.

Fix: Ensure Debian repos are complete and aligned, remove holds/pins, temporarily remove the third-party package if needed, complete the base OS transition, then add third-party packages back.

8) Symptom: “The following signatures were invalid” and then upgrade breaks later

Root cause: You stopped getting updates, so your system drifted behind. Later you try to install something that assumes newer dependencies.

Fix: Fix repo signing/keys first, run apt update, then do a full upgrade to resync.

Checklists / step-by-step plan

Checklist A: Make APT consistent again (the safe baseline)

  1. Confirm release: /etc/os-release must match Debian 13 codename.
  2. Inspect sources: remove/disable mismatched suites and random third-party repos.
  3. Run apt update and ensure it completes without repo errors.
  4. Check holds: apt-mark showhold; unhold core packages unless you have a written reason.
  5. Check pinning: inspect /etc/apt/preferences* for broad pins; remove/limit them.
  6. Fix dpkg state: dpkg -C, then dpkg --configure -a.
  7. Run apt-get -f install to repair dependency breaks.
  8. Run apt full-upgrade to complete transitions.
  9. Reboot if kernel/libc/systemd changed (usually yes). Then verify services.

Checklist B: If you must keep a third-party repo

  1. Ensure the repo targets Debian 13 suite (not Debian 12, not Ubuntu).
  2. Prefer repo scoping via pinning:
    • Pin by origin and package name, not globally.
    • Keep vendor packages at lower priority unless explicitly installed.
  3. Document the exception: what packages come from vendor, why, and how you roll back if they disappear.
  4. Re-test upgrades in a staging environment that mirrors production sources exactly.

Checklist C: When you’re already in a partial-upgrade mess

  1. Stop background automation (unattended upgrades, config management package tasks) until you finish repair.
  2. Repair dpkg configuration first (dpkg --configure -a).
  3. Then repair dependencies (apt-get -f install).
  4. Then do the full upgrade (apt full-upgrade).
  5. Only after the base OS is consistent, re-enable third-party repos and reinstall their packages if needed.

FAQ

1) Do I really need apt full-upgrade? Sounds scary.

Sometimes, yes. If a transition requires installing new dependencies or removing obsolete packages, apt upgrade will refuse. full-upgrade is not reckless; it’s honest. Read the plan before you accept.

2) What’s the difference between APT and dpkg in this mess?

APT decides what should happen; dpkg performs it. If APT can’t find a valid solution, that’s a policy/repo/version issue. If dpkg can’t configure packages, that’s an interrupted install or a broken script/config.

3) Is it okay to delete dpkg lock files?

Rarely. First confirm there isn’t a legitimate apt/dpkg process running. Deleting locks while dpkg is active is a great way to corrupt state. If you’re tempted, you probably need to slow down and inspect processes.

4) Why does APT say a package “is not installable” when it exists in the repo?

Because “installable” means “installable under current constraints”: suite, architecture, pins, and dependency versions. A package can exist but be excluded by pinning, wrong suite, or missing components.

5) Can I fix unmet dependencies by downloading random .deb files?

You can also fix a leaky pipe with chewing gum. It may hold long enough to ruin your weekend. Prefer consistent repositories and let APT manage versions unless you’re doing a tightly controlled one-off repair.

6) How do I know if I have mixed Debian releases?

apt-cache policy shows the release lines. If you see multiple suites (like trixie plus bookworm or sid) at similar priority, you’re mixing. Also inspect /etc/apt/sources.list* for suite names.

7) What if the only way to satisfy dependencies is to remove a key service?

That’s usually a sign your repositories are wrong or you’ve pinned yourself into a corner. Fix sources and pins first. If it’s a third-party package causing conflicts, remove that package temporarily, complete the OS upgrade, then reinstall the third-party package from a compatible repo.

8) Should I use aptitude instead?

It can offer alternative solutions interactively, which is useful when you understand the system. It’s not a substitute for correct repos/pins. If your sources are wrong, aptitude will still present you with bad choices—just more creatively.

9) Why does this happen during Debian upgrades more than “normal days”?

Release transitions and point updates trigger coordinated changes: libc, openssl, systemd, kernels. If you have holds, pins, or missing suites (like security), those transitions fail loudly.

10) When is reinstall actually justified?

When you’ve got untracked manual edits, unknown third-party repos, and years of drift—plus no snapshots and no time to reason about it. Reinstall is an operational decision, not a technical requirement. Most of the time, you can repair faster than you can rebuild safely.

Conclusion: next steps you should actually do

If APT is yelling about unmet dependencies on Debian 13, treat it like a consistency problem, not a willpower problem.

  1. Make sources sane. One Debian release, complete suites (main + updates + security), and no mismatched vendor repos.
  2. Remove policy traps. Unhold core packages, undo broad pins, and stop mixing releases unless you’re doing it deliberately with tight constraints.
  3. Repair dpkg state. dpkg -C then dpkg --configure -a before trying big upgrades.
  4. Let APT complete transitions. Use apt-get -f install to restore consistency, then apt full-upgrade to finish the job.
  5. Afterwards, lock in boring correctness. Configuration-manage your repo files, document third-party repos, and snapshot before upgrades if you can.

The goal isn’t to “make the error go away.” The goal is to restore a system where every package version has a clear origin and an upgrade path that doesn’t require prayer.

← Previous
WordPress 502 Bad Gateway: PHP-FPM, Nginx, Cloudflare — How to Find the Culprit
Next →
WordPress caching that doesn’t break carts and forms

Leave a comment