Fixing the apt error "Could not get lock"

Published on 15 min read

Why apt is suddenly locked, which process is behind it and how to find it with lsof and fuser. Plus how to remove the lock file without damaging the package database.

You want to install one more package quickly, and apt gives up after a second. Instead of the package list you get a line that is searched for millions of times worldwide: Could not get lock. The reflex in many guides is to delete the lock file immediately. That reflex is exactly what turns a harmless waiting situation into a damaged package database. This article works through the order that holds up on a production system: first establish who is locking, then wait, and only then intervene.

All commands run as root. If you work as a regular user, prefix them with sudo. One clarification up front: this topic concerns Debian and Ubuntu only. On AlmaLinux, Rocky Linux, RHEL and Oracle Linux there is neither apt-get nor /var/lib/dpkg, and dnf solves the question in a completely different way, see the section on system differences further down.

The exact wording of the error message

Depending on the version and on what apt was about to do, the output looks different. These are the variants you will run into:

E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1234 (unattended-upgr)
N: Be aware that removing the lock file is not a solution and may break your system.
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it?
E: Could not get lock /var/lib/dpkg/lock - open (11: Resource temporarily unavailable)
E: Unable to lock the administration directory (/var/lib/dpkg/), is another process using it?
E: Could not get lock /var/lib/apt/lists/lock. It is held by process 987 (apt-get)
E: Unable to lock directory /var/lib/apt/lists/
dpkg: error: dpkg frontend lock is locked by another process
dpkg: error: dpkg status database is locked by another process

On a system with a localized locale the wording differs, but the message means the same thing. What matters is the hint in parentheses: the process name. unattended-upgr, apt-get, aptitude, packagekitd or dpkg already tells you where to look.

Four lock files, four different messages

apt and dpkg do not lock in one place but in four. The file named in the message reveals the stage at which the conflict arose:

  • /var/lib/apt/lists/lock protects the downloaded package lists. This message appears with apt update.
  • /var/cache/apt/archives/lock protects the download directory for the .deb files. This message appears while apt is fetching packages.
  • /var/lib/dpkg/lock-frontend is the top-level lock. It makes sure that only one frontend (apt, apt-get, aptitude, Ansible, an installation script) talks to dpkg at any one time. This is the message you will see most often.
  • /var/lib/dpkg/lock protects the actual status database. Whoever holds this lock is genuinely writing to /var/lib/dpkg/status right now.

All four files are empty. They contain no data, no PID, nothing at all. The lock does not live in the content but in a flock on the open file descriptor. That is the decisive point most guides leave out.

Why deleting blindly can damage the package database

Because the lock hangs on the file descriptor and not on the file name, this is what happens when you delete it: the running process keeps its descriptor and carries on undisturbed. The file has disappeared from the directory, but for that process it still exists. Your second apt call creates a new file with the same name, locks it successfully and believes the road is clear.

From that moment on, two processes write to /var/lib/dpkg/status at the same time, unpack files into the same target directory in parallel and run each other's triggers. The result ranges from half-configured packages to a status database that dpkg can no longer read. That is precisely what apt itself warns about with the line N: Be aware that removing the lock file is not a solution and may break your system.

Removing the lock file is acceptable only once you have proven that no process holding it is still running. That proof is the core of this guide, not the rm.

The most common case: an automatic update is running

In roughly nine out of ten cases on a freshly installed server the culprit is harmless and legitimate: unattended-upgrades. Ubuntu enables unattended security updates by default in the server and cloud images, and two systemd timers trigger the run:

  • apt-daily.timer runs at 06:00 and 18:00 with a random delay of up to twelve hours and refreshes the package lists.
  • apt-daily-upgrade.timer runs at 06:00 with a random delay of up to 60 minutes and installs the security updates.

That random delay is the reason the error seems to appear at completely arbitrary times of day. On cloud images the first boot adds to it: cloud-init runs an apt update of its own. Anyone who logs in two minutes after provisioning and wants to install something right away will almost inevitably hit the lock. When you set up a new server it is therefore worth following the order from our checklist for new root servers: take a breath first, install second.

This is how you check the status of the automatic update:

systemctl list-timers 'apt-daily*'
systemctl status unattended-upgrades.service
journalctl -u apt-daily-upgrade.service --since "-2h" --no-pager
tail -n 30 /var/log/unattended-upgrades/unattended-upgrades.log

Who holds the lock? Diagnosis with lsof and fuser

Before any intervention comes the question of whether anyone is still working at all. Two tools answer that reliably. If they are missing, they come from the packages lsof and psmisc, which of course you can only install once the lock is gone. On production systems both therefore belong in the base setup.

lsof /var/lib/dpkg/lock-frontend
lsof /var/lib/dpkg/lock
lsof /var/cache/apt/archives/lock
lsof /var/lib/apt/lists/lock

A typical output looks like this:

COMMAND     PID USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
unattended 1234 root    5uW  REG  254,1        0 1049 /var/lib/dpkg/lock-frontend

The W after the file descriptor number means: write lock set. Only this entry really blocks apt, a merely open handle without W does not. So everything is in order here, the process is working.

If no output comes back at all, nobody is holding the lock any more. Expect lsof to return exit code 1 in this perfectly normal case, entirely without an error message. The same applies to fuser. In a script with set -e or in a chain with &&, the diagnosis therefore aborts at exactly the moment the result is good. Write lsof /var/lib/dpkg/lock-frontend || true there.

An important caveat about how much this proves: empty output really means "nobody is locking" only if the lock file was not deleted beforehand. If it was removed while a process still held it, that process keeps the orphaned inode, but under the file name lsof no longer sees anything. Always check the process list in addition.

Without lsof, fuser does the same job:

fuser -v /var/lib/dpkg/lock-frontend

And a look at the process list also shows how long the operation has been running. The etimes column prints the runtime in seconds, which helps with the assessment:

ps -eo pid,ppid,etimes,stat,cmd | grep -E 'apt|dpkg|unattended' | grep -v grep

Interpret the result like this:

  • Runtime under ten minutes, state S or R: normal operation. Wait.
  • Runtime over an hour, network access, slow mirrors: still plausible. Check with tail -f /var/log/apt/term.log whether anything is moving.
  • State D (uninterruptible sleep) for a long stretch: the process is stuck in the input and output path. The cause is usually a full or faulty disk, not apt itself.
  • State T (stopped): someone paused the run with Ctrl+Z. Let it continue with kill -CONT PID.
  • Process no longer exists, lock remains: now, and only now, removing the lock file is justified.

A common and underestimated cause is a full partition: dpkg gives up in the middle of unpacking and leaves exactly this state behind. If df -h /var sits close to 100%, first read how to clean up a full disk on Linux, and repair the package management only afterwards.

Waiting properly instead of aborting: DPkg::Lock::Timeout

Since apt 2.x there has been an option that makes a large part of the trouble in scripts unnecessary. Instead of aborting immediately, apt waits a given number of seconds for the lock to be released:

apt-get -o DPkg::Lock::Timeout=60 install -y htop

The value -1 means waiting indefinitely. To make it permanent, put it in a configuration file of your own. The missing file extension is not an oversight, apt reads the file even without .conf:

echo 'DPkg::Lock::Timeout "300";' > /etc/apt/apt.conf.d/99lock-timeout
apt-config dump DPkg::Lock::Timeout

The second line is the counter check. It has to print DPkg::Lock::Timeout "300";, only then is the file really in effect.

And now the limitation that appears in almost no guide and that makes the difference when it matters: The option does not cover all four locks. Measured on Debian 11, 12 and 13 as well as Ubuntu 22.04 and 24.04, in each case with a foreign process holding the lock via fcntl:

LockDoes apt wait with DPkg::Lock::Timeout?
/var/lib/dpkg/lock-frontendyes, exactly the configured time
/var/lib/dpkg/lockyes
/var/cache/apt/archives/lockno, gives up in under a second
/var/lib/apt/lists/lockno, gives up in under a second

In practice that means two things. First: with apt-get update the option achieves nothing at all, because what is involved there is the lists lock. Even with DPkg::Lock::Timeout=-1 the call aborts immediately with E: Could not get lock /var/lib/apt/lists/lock and exit code 100 instead of waiting. Second: even with install the timeout helps only as long as the blocker holds the frontend lock. If a parallel process is busy with a download and therefore holds the archives lock, apt again does not wait a single second.

For Ansible roles, cloud-init scripts and deployment pipelines an outer retry loop therefore belongs on top, or the whole action gets serialized through flock:

for i in $(seq 30); do apt-get update && break; sleep 10; done
flock /var/lib/apt/lists/lock apt-get update

On Debian 12, Debian 13, Ubuntu 22.04 and Ubuntu 24.04 the option is available. On very old systems (Debian 9, Ubuntu 16.04) apt does not know it and silently ignores it without throwing an error.

When no process is running any more: removing the lock file

You have proven with lsof and ps that nobody is working on the package management any more. Only now comes the intervention. If a process is still running that you have to terminate, use the friendly signal first and never go straight to kill -9:

kill -TERM 1234

A SIGTERM gives unattended-upgrades the chance to finish the running dpkg call cleanly. A SIGKILL in the middle of unpacking, by contrast, leaves behind exactly the half-installed packages that you then have to clean up laboriously. After the SIGTERM, wait at least 30 seconds and check again.

Once the process list is clean, remove the lock files:

rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/cache/apt/archives/lock /var/lib/apt/lists/lock

The files are created again automatically on the next apt call. You have to generate them neither by hand nor with particular permissions. Conversely, that also means: deleting them repairs neither wrong permissions nor a wrong owner, it only removes the name. Anyone hoping for a repair from it is looking in the wrong place. And gentler than the rm is to merely truncate the files, for example with : > /var/lib/dpkg/lock-frontend, because then the inode is preserved and an old process that is still running stays visible in lsof.

After the intervention: dpkg --configure -a

This step is not optional. An aborted run leaves packages in the state "unpacked but not configured". apt then refuses to continue with:

E: dpkg was interrupted, you must manually run 'dpkg --configure -a' to correct the problem.

The command works through all outstanding configuration steps:

dpkg --configure -a

After that comes the check for broken dependencies:

apt-get --fix-broken install -y
apt-get check

An interesting detail for the follow-up: the dpkg journal lives under /var/lib/dpkg/updates/. If the directory is empty after dpkg --configure -a, everything has been processed. If numbered files are still sitting there, the run did not complete.

When it went wrong anyway: follow-up errors and the way back

This is where other guides stop. These messages turn up when the deletion came too early or the kill was too hard:

dpkg: error processing package nginx (--configure):
 package is in a very bad inconsistent state; you should
 reinstall it before attempting configuration
Errors were encountered while processing:
 nginx
E: Sub-process /usr/bin/dpkg returned an error code (1)

The way out leads through a forced removal of the broken package and a fresh installation. Use --force-remove-reinstreq exclusively for the one affected package, never across the board:

dpkg --remove --force-remove-reinstreq nginx
apt-get install -y nginx

A second variant concerns the file lists:

dpkg: warning: files list file for package 'libssl3' missing; assuming package has no files currently installed

A reinstall of the same package with apt-get install --reinstall repairs that. Which packages are in an unclean state at all is listed by:

dpkg --audit

In the worst case /var/lib/dpkg/status itself is damaged, recognizable by messages such as dpkg: unrecoverable fatal error, aborting: parsing file '/var/lib/dpkg/status'. Two backup copies that the system creates automatically then help: /var/lib/dpkg/status-old and the daily rotated copies under /var/backups/dpkg.status.0 through dpkg.status.6.gz. Copy the newer of the two back before you even consider reinstalling the system. Be sure to back up the broken file first.

Differences between the systems

There is no "one solution for all" here, the starting position differs considerably:

  • Ubuntu 22.04 and 24.04: unattended-upgrades is active in the server images, so the error is everyday business. On top of that comes needrestart, which opens an interactive dialog after every installation and keeps the run and its lock open until somebody confirms. In scripts you therefore set DEBIAN_FRONTEND=noninteractive.
  • Debian 12 and Debian 13: the timers apt-daily.timer and apt-daily-upgrade.timer exist here as well. Whether updates really run automatically depends on the image and on /etc/apt/apt.conf.d/20auto-upgrades. Check instead of assuming.
  • Containers: a Docker image runs neither systemd nor unattended-upgrades. A lock there practically always means parallel steps in the build or a cached layer with a lock file left behind. If you build images regularly, our article on Docker on Debian and Ubuntu is a good place to start.
  • Desktop systems: there it is often packagekitd or the graphical update manager holding the lock, not apt.
  • AlmaLinux, Rocky Linux and RHEL: the problem does not exist in this form there, dnf uses /var/run/dnf.pid and waits by default instead of aborting. The message then reads Waiting for process with pid ... to finish. How else this family differs is shown in the article on htop on AlmaLinux, Rocky and RHEL.

How to tell that everything is clean again

Four checks that are meaningful when taken together:

dpkg --audit
apt-get check
apt-get --fix-broken install -y
apt-get update

dpkg --audit ideally prints nothing at all. apt-get check ends with the lines about reading the package lists and without errors. apt-get --fix-broken install reports 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. And apt-get update runs through without a lock message. In addition, ls /var/lib/dpkg/updates/ should show an empty directory, and a look into /var/log/dpkg.log should show the last actions ending with the state status installed instead of half-configured:

tail -n 20 /var/log/dpkg.log

Prevention instead of repair

Four habits keep the error from becoming a time sink in the first place:

  1. Set a timeout and retry anyway. DPkg::Lock::Timeout in /etc/apt/apt.conf.d/99lock-timeout makes apt wait on the frontend lock and the dpkg lock instead of aborting. Because the archives lock and the lists lock are not covered, an outer retry loop is added in scripts. The two together cover practically every incident.
  2. Never update in a plain SSH session. If the connection drops during apt upgrade, dpkg breaks off in the middle of the run. Start longer updates in tmux or screen. The basics are in the article on connecting to your server via SSH.
  3. Avoid Ctrl+C while it runs. During the download an abort is uncritical, during unpacking and configuring it produces exactly the half-installed packages from the section above.
  4. Do not let your own maintenance runs collide with the system timers. If you run your own update on a schedule, put it at a different time and give it a timeout. How to set that up cleanly is described in the articles on cron jobs on Linux and on custom systemd services.

And one more note on the seemingly simplest way out: apt remove unattended-upgrades does eliminate the lock conflicts, but it also takes your automatic security updates away. On a server that is publicly reachable, that is a bad trade. It makes far more sense to keep the automatic update and make your own operations patient.


In short: run lsof on the lock file named in the message, check the process list, wait. Only once nothing is demonstrably running any more, remove the lock files, and after that always run dpkg --configure -a and apt-get check. With DPkg::Lock::Timeout in the apt configuration and a retry loop around apt-get update, the rest takes care of itself.

Frequently asked questions

Can I simply delete the lock file?
Only if you have first proven with lsof or fuser that no process is holding it any more. The lock hangs on the open file descriptor, not on the file name. If you delete it while a process is working, two operations then run on the same package database at the same time, and that damages it.
How long should I wait before I intervene?
With unattended-upgrades, ten to thirty minutes is normal, and longer still for large updates over slow mirrors. Check with tail -f /var/log/apt/term.log whether anything is still moving. As long as new lines keep appearing there, the run is working and you do not intervene.
What does the process name unattended-upgr in the error message mean?
That is the automatic security update started by the systemd timers apt-daily.timer and apt-daily-upgrade.timer. The name is truncated to 15 characters. You should let this run finish rather than abort it.
Why do I need dpkg --configure -a after removing the lock file?
An aborted run leaves packages in the state unpacked but not configured. dpkg --configure -a works through these outstanding steps. Without that command apt refuses every further installation with the message dpkg was interrupted.
How do I prevent the error in scripts and Ansible roles?
With apt-get -o DPkg::Lock::Timeout=300 instead of a wait loop on process names. To make it permanent, add DPkg::Lock::Timeout "300"; to /etc/apt/apt.conf.d/99lock-timeout, and the value -1 means waiting indefinitely. Important: the option only takes effect on the dpkg frontend lock and the dpkg lock. On the archives lock and the lists lock apt still does not wait, which is why it demonstrably achieves nothing with apt-get update. There you need a retry loop of your own, for example: for i in $(seq 30); do apt-get update && break; sleep 10; done
Does this error occur on AlmaLinux or Rocky Linux as well?
Not in this form. dnf locks through /var/run/dnf.pid and waits for the other operation by default instead of aborting. The message there reads Waiting for process with pid ... to finish.
apt still reports errors about a single package after the repair, what now?
Check with dpkg --audit which package is affected, remove exactly that one with dpkg --remove --force-remove-reinstreq PACKAGE and then install it again. Never apply the option across all packages.

apt dpkg Debian Ubuntu Package management Troubleshooting unattended-upgrades Linux