Naprawa błędu apt „Could not get lock”

Opublikowano 12 min czytania

Dlaczego apt nagle jest zablokowany, jaki proces za tym stoi, jak go znaleźć poleceniami lsof i fuser oraz jak usunąć plik blokady, nie uszkadzając bazy pakietów.

Chcesz szybko doinstalować pakiet, a apt przerywa pracę po sekundzie. Zamiast listy pakietów pojawia się wiersz wyszukiwany na całym świecie miliony razy: Could not get lock. Odruchem wielu poradników jest natychmiastowe skasowanie pliku blokady. Właśnie ten odruch regularnie zamienia niegroźne czekanie w uszkodzoną bazę pakietów. Ten artykuł przechodzi przez kolejność, która sprawdza się na systemie produkcyjnym: najpierw ustal, kto blokuje, potem czekaj, a dopiero na końcu interweniuj.

Wszystkie polecenia wykonujesz jako root. Jeśli pracujesz na zwykłym koncie użytkownika, poprzedź je poleceniem sudo. I od razu jedno zastrzeżenie: temat dotyczy wyłącznie Debiana i Ubuntu. Na AlmaLinux, Rocky Linux, RHEL i Oracle Linux nie ma ani apt-get, ani /var/lib/dpkg, a dnf rozwiązuje tę kwestię zupełnie inaczej, patrz sekcja o różnicach między systemami w dalszej części tekstu.

Dokładna treść komunikatu o błędzie

W zależności od wersji i od tego, co apt akurat próbował zrobić, komunikat wygląda inaczej. Oto warianty, na które możesz trafić:

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

Polska lokalizacja zgłasza to samo jako „Nie udało się uzyskać blokady” albo „Nie udało się zablokować katalogu administracyjnego”. Ważna jest wskazówka w nawiasie: nazwa procesu. unattended-upgr, apt-get, aptitude, packagekitd lub dpkg od razu podpowiadają, gdzie szukać.

Cztery pliki blokady, cztery różne komunikaty

apt i dpkg blokują nie w jednym miejscu, tylko w czterech. To, który plik pojawia się w komunikacie, zdradza, na jakim etapie powstał konflikt:

  • /var/lib/apt/lists/lock chroni pobrane listy pakietów. Ten komunikat pojawia się przy apt update.
  • /var/cache/apt/archives/lock chroni katalog pobierania plików .deb. Ten komunikat pojawia się w trakcie pobierania pakietów przez apt.
  • /var/lib/dpkg/lock-frontend to blokada najwyższego poziomu. Pilnuje, żeby w danej chwili tylko jeden frontend (apt, apt-get, aptitude, Ansible, skrypt instalacyjny) rozmawiał z dpkg. Ten komunikat widuje się najczęściej.
  • /var/lib/dpkg/lock chroni właściwą bazę stanu pakietów. Kto trzyma tę blokadę, ten naprawdę zapisuje właśnie /var/lib/dpkg/status.

Wszystkie cztery pliki są puste. Nie zawierają żadnych danych, żadnego PID, niczego. Blokada nie tkwi w treści pliku, tylko w flock nałożonym na otwarty deskryptor pliku. To kluczowy szczegół, który większość poradników pomija.

Dlaczego kasowanie w ciemno może uszkodzić bazę pakietów

Ponieważ blokada wisi na deskryptorze pliku, a nie na jego nazwie, po skasowaniu dzieje się tak: działający proces zachowuje swój deskryptor i pracuje dalej, jakby nic się nie stało. Plik zniknął z katalogu, ale dla tego procesu nadal istnieje. Twoje drugie wywołanie apt zakłada nowy plik o tej samej nazwie, blokuje go bez problemu i uznaje, że ma wolną drogę.

Od tej chwili dwa procesy zapisują jednocześnie /var/lib/dpkg/status, równolegle rozpakowują pliki do tego samego katalogu docelowego i odpalają sobie nawzajem triggery. Skutki sięgają od pakietów skonfigurowanych w połowie aż po bazę stanu, której dpkg nie potrafi już odczytać. Dokładnie przed tym ostrzega sam apt wierszem N: Be aware that removing the lock file is not a solution and may break your system.

Usunięcie pliku blokady jest dopuszczalne tylko wtedy, gdy udowodnisz, że nie działa już żaden proces, który ją trzyma. Sednem tego poradnika jest właśnie ten dowód, a nie samo rm.

Najczęstszy przypadek: właśnie trwa automatyczna aktualizacja

W mniej więcej dziewięciu przypadkach na dziesięć na świeżo postawionym serwerze winowajca jest niegroźny i całkowicie uprawniony: unattended-upgrades. Ubuntu domyślnie włącza bezobsługowe aktualizacje bezpieczeństwa w obrazach serwerowych i chmurowych, a całą operację uruchamiają dwa timery systemd:

  • apt-daily.timer startuje o 06:00 i 18:00 z losowym opóźnieniem sięgającym dwunastu godzin i odświeża listy pakietów.
  • apt-daily-upgrade.timer startuje o 06:00 z losowym opóźnieniem sięgającym 60 minut i instaluje aktualizacje bezpieczeństwa.

Losowe opóźnienie odpowiada za to, że błąd pojawia się pozornie o zupełnie dowolnych porach. W obrazach chmurowych dochodzi do tego pierwszy start: cloud-init sam wykonuje przy pierwszym uruchomieniu apt update. Kto zaloguje się dwie minuty po udostępnieniu serwera i chce od razu coś zainstalować, wpada w blokadę niemal nieuchronnie. Przy stawianiu nowego serwera opłaca się więc trzymać kolejności z naszej listy kontrolnej dla nowego serwera root: najpierw odetchnij, potem instaluj.

Stan automatycznej aktualizacji sprawdzisz tak:

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

Kto trzyma blokadę? Diagnostyka za pomocą lsof i fuser

Przed każdą ingerencją stoi pytanie, czy ktoś w ogóle jeszcze pracuje. Dwa narzędzia odpowiadają na nie wiarygodnie. Jeśli ich brakuje, znajdziesz je w pakietach lsofpsmisc, które oczywiście da się zainstalować dopiero wtedy, gdy blokada zniknie. Na systemach produkcyjnych oba należą więc do wyposażenia podstawowego.

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

Typowe wyjście wygląda tak:

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

Litera W za numerem deskryptora pliku oznacza założoną blokadę zapisu. Tylko taki wpis rzeczywiście blokuje apt, samo otwarte odwołanie bez W już nie. Tutaj wszystko jest więc w porządku, proces pracuje.

Jeśli natomiast nie wraca żadne wyjście, blokady nie trzyma już nikt. Licz się z tym, że lsof w tym normalnym przypadku zwraca kod wyjścia 1, całkiem bez komunikatu o błędzie. To samo dotyczy fuser. W skrypcie z set -e albo w łańcuchu spiętym przez && diagnostyka przerywa się więc akurat wtedy, gdy wynik jest dobry. Napisz tam lsof /var/lib/dpkg/lock-frontend || true.

Ważne zastrzeżenie co do siły dowodowej: puste wyjście oznacza „nikt nie blokuje” tylko wtedy, gdy plik blokady nie został wcześniej skasowany. Jeśli usunięto go w chwili, gdy proces wciąż go trzymał, ten proces nadal trzyma osierocony i-node, ale pod nazwą pliku lsof nie widzi już nic. Zawsze sprawdzaj więc dodatkowo listę procesów.

Bez lsof to samo załatwi fuser:

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

Rzut oka na listę procesów pokazuje dodatkowo, jak długo operacja już trwa. Kolumna etimes podaje czas działania w sekundach, co ułatwia ocenę sytuacji:

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

Wynik czytaj tak:

  • Czas działania poniżej dziesięciu minut, status S lub R: normalna praca. Czekaj.
  • Czas działania powyżej godziny, ruch sieciowy, wolne serwery lustrzane: nadal wiarygodne. Sprawdź poleceniem tail -f /var/log/apt/term.log, czy cokolwiek się rusza.
  • Status D (uninterruptible sleep) przez dłuższy czas: proces wisi na ścieżce wejścia i wyjścia. Przyczyną jest zwykle pełny albo uszkodzony dysk, a nie sam apt.
  • Status T (zatrzymany): ktoś wstrzymał operację skrótem Ctrl+Z. Wznów ją poleceniem kill -CONT PID.
  • Proces już nie istnieje, a blokada zostaje: teraz, i dopiero teraz, usunięcie pliku blokady jest uzasadnione.

Częstą i niedocenianą przyczyną jest pełna partycja: dpkg przerywa pracę w środku rozpakowywania i zostawia dokładnie taki stan. Jeśli df -h /var pokazuje blisko 100%, przeczytaj najpierw, jak uporządkować pełny dysk w Linuksie, a system pakietów naprawiaj dopiero potem.

Czekaj poprawnie, zamiast przerywać: DPkg::Lock::Timeout

Od apt 2.x istnieje opcja, która usuwa dużą część kłopotów w skryptach. Zamiast przerywać od razu, apt czeka na zwolnienie blokady zadaną liczbę sekund:

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

Wartość -1 oznacza czekanie bez ograniczeń. Na stałe ustawisz to we własnym pliku konfiguracyjnym. Brak rozszerzenia nie jest przeoczeniem, apt czyta ten plik także bez .conf:

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

Drugi wiersz to kontrola. Musi wypisać DPkg::Lock::Timeout "300";, dopiero wtedy plik naprawdę obowiązuje.

A teraz ograniczenie, o którym nie pisze prawie żaden poradnik i które w razie kłopotów robi całą różnicę: opcja nie obejmuje wszystkich czterech blokad. Sprawdzone pomiarowo na Debianie 11, 12 i 13 oraz Ubuntu 22.04 i 24.04, za każdym razem z obcym procesem trzymającym blokadę przez fcntl:

BlokadaCzy apt czeka z DPkg::Lock::Timeout?
/var/lib/dpkg/lock-frontendtak, dokładnie przez ustawiony czas
/var/lib/dpkg/locktak
/var/cache/apt/archives/locknie, przerwanie po niecałej sekundzie
/var/lib/apt/lists/locknie, przerwanie po niecałej sekundzie

W praktyce znaczy to dwie rzeczy. Po pierwsze: przy apt-get update opcja nie daje nic, bo w grę wchodzi tam blokada list. Nawet z DPkg::Lock::Timeout=-1 wywołanie przerywa się natychmiast komunikatem E: Could not get lock /var/lib/apt/lists/lock i kodem wyjścia 100, zamiast poczekać. Po drugie: nawet przy install timeout pomaga tylko dopóty, dopóki blokujący trzyma blokadę frontendu. Jeśli równoległy proces wisi akurat na pobieraniu i trzyma przez to blokadę archiwum, apt również nie czeka ani sekundy.

Do ról Ansible, skryptów cloud-init i potoków wdrożeniowych dochodzi więc dodatkowo zewnętrzna pętla powtórzeń albo cała operacja zostaje zserializowana przez flock:

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

Na Debianie 12, Debianie 13, Ubuntu 22.04 i Ubuntu 24.04 opcja jest dostępna. Na bardzo starych systemach (Debian 9, Ubuntu 16.04) apt jej nie zna i po cichu ją ignoruje, nie zgłaszając błędu.

Gdy naprawdę nie działa już żaden proces: usuwanie pliku blokady

Poleceniami lsofps udowodniłeś, że nikt już nie pracuje przy systemie pakietów. Dopiero teraz przychodzi czas na ingerencję. Jeśli jednak działa jeszcze proces, który musisz zakończyć, użyj najpierw łagodnego sygnału i nigdy od razu kill -9:

kill -TERM 1234

SIGTERM daje procesowi unattended-upgrades szansę na czyste dokończenie trwającego wywołania dpkg. SIGKILL w środku rozpakowywania zostawia natomiast dokładnie te pakiety zainstalowane w połowie, które potem mozolnie sprzątasz. Po SIGTERM odczekaj co najmniej 30 sekund i sprawdź jeszcze raz.

Jeśli lista procesów jest czysta, usuń pliki blokady:

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

Pliki powstaną automatycznie przy kolejnym wywołaniu apt. Nie musisz ich tworzyć ręcznie ani nadawać im konkretnych uprawnień. Z drugiej strony oznacza to również, że kasowanie nie naprawia ani błędnych uprawnień, ani błędnego właściciela, usuwa tylko nazwę. Kto liczy na to, że w ten sposób coś naprawi, szuka w złym miejscu. Jeszcze łagodniejsze od rm jest samo opróżnienie plików, na przykład przez : > /var/lib/dpkg/lock-frontend: i-node zostaje wtedy zachowany, a wciąż działający stary proces pozostaje widoczny w lsof.

Po ingerencji: dpkg --configure -a

Ten krok nie jest opcjonalny. Przerwana operacja zostawia pakiety w stanie „rozpakowany, ale nieskonfigurowany”. apt odmawia wtedy pracy komunikatem:

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

Polecenie wykonuje wszystkie zaległe kroki konfiguracji:

dpkg --configure -a

Potem następuje sprawdzenie uszkodzonych zależności:

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

Ciekawy szczegół przy późniejszej kontroli: w katalogu /var/lib/dpkg/updates/ dpkg prowadzi własny journal. Jeśli katalog po dpkg --configure -a jest pusty, wszystko zostało wykonane. Jeśli leżą tam jeszcze ponumerowane pliki, operacja nie doszła do końca.

Jeśli mimo wszystko poszło źle: błędy następcze i droga powrotna

W tym miejscu inne poradniki się kończą. Poniższe komunikaty pojawiają się wtedy, gdy skasowałeś za wcześnie albo ubiłeś proces zbyt brutalnie:

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)

Wyjście prowadzi przez wymuszone usunięcie zepsutego pakietu i ponowną instalację. Opcji --force-remove-reinstreq używaj wyłącznie dla tego jednego pakietu, którego problem dotyczy, nigdy hurtowo:

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

Drugi wariant dotyczy list plików:

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

Naprawia to ponowna instalacja tego samego pakietu poleceniem apt-get install --reinstall. To, które pakiety w ogóle są w niespójnym stanie, wypisuje:

dpkg --audit

W najgorszym przypadku uszkodzony jest sam /var/lib/dpkg/status, co poznasz po komunikatach w rodzaju dpkg: unrecoverable fatal error, aborting: parsing file '/var/lib/dpkg/status'. Pomagają wtedy dwie kopie zapasowe, które system zakłada automatycznie: /var/lib/dpkg/status-old oraz rotowane codziennie kopie w /var/backups/dpkg.status.0 aż po dpkg.status.6.gz. Skopiuj z powrotem nowszą z nich, zanim pomyślisz o ponownej instalacji systemu. Wcześniej koniecznie zrób kopię zapasową uszkodzonego pliku.

Różnice między systemami

„Jednego rozwiązania dla wszystkich” tutaj nie ma, punkt wyjścia różni się wyraźnie:

  • Ubuntu 22.04 i 24.04: unattended-upgrades jest w obrazach serwerowych aktywne, więc błąd to codzienność. Dochodzi do tego needrestart, który po każdej instalacji otwiera interaktywne okno dialogowe i trzyma operację razem z blokadą otwartą, dopóki ktoś nie potwierdzi. W skryptach ustawiaj więc DEBIAN_FRONTEND=noninteractive.
  • Debian 12 i Debian 13: timery apt-daily.timerapt-daily-upgrade.timer również istnieją, natomiast to, czy aktualizacje faktycznie idą automatycznie, zależy od obrazu i od /etc/apt/apt.conf.d/20auto-upgrades. Sprawdzaj, zamiast zakładać.
  • Kontenery: w obrazie Dockera nie działa ani systemd, ani unattended-upgrades. Blokada oznacza tam praktycznie zawsze równoległe kroki podczas budowania obrazu albo warstwę z cache'u, w której został plik blokady. Kto regularnie buduje obrazy, znajdzie wprowadzenie w naszym wpisie o Dockerze na Debianie i Ubuntu.
  • Systemy desktopowe: tam blokadę trzyma często packagekitd albo graficzny menedżer aktualizacji, a nie apt.
  • AlmaLinux, Rocky Linux i RHEL: w tej postaci problem tam nie występuje, dnf używa /var/run/dnf.pid i domyślnie czeka, zamiast przerywać. Komunikat brzmi wtedy Waiting for process with pid ... to finish. Czym jeszcze różni się ta rodzina systemów, pokazuje wpis o htopie na AlmaLinux, Rocky i RHEL.

Po czym poznasz, że wszystko jest znów czyste

Cztery kontrole, które razem dają jasny obraz:

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

dpkg --audit w idealnym przypadku nie wypisuje nic. apt-get check kończy się wierszami o czytaniu list pakietów i bez błędu. apt-get --fix-broken install zgłasza 0 upgraded, 0 newly installed, 0 to remove and 0 not upgraded. A apt-get update przechodzi bez komunikatu o blokadzie. Dodatkowo ls /var/lib/dpkg/updates/ powinno pokazać pusty katalog, a zaglądając do /var/log/dpkg.log, powinieneś zobaczyć ostatnie akcje zakończone statusem status installed, a nie half-configured:

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

Zapobiegaj, zamiast naprawiać

Żeby ten błąd w ogóle nie zjadał czasu, pomagają cztery nawyki:

  1. Ustaw timeout, a mimo to powtarzaj. DPkg::Lock::Timeout/etc/apt/apt.conf.d/99lock-timeout każe aptowi czekać przy blokadzie frontendu i blokadzie dpkg, zamiast przerywać. Ponieważ blokada archiwum i blokada list do tego nie należą, w skryptach dochodzi zewnętrzna pętla powtórzeń. Jedno z drugim pokrywa praktycznie wszystkie przypadki.
  2. Nigdy nie aktualizuj w zwykłej sesji SSH. Jeśli połączenie zerwie się w trakcie apt upgrade, dpkg przerywa pracę w środku operacji. Dłuższe aktualizacje uruchamiaj w tmux albo screen. Podstawy znajdziesz we wpisie połącz się z serwerem przez SSH.
  3. Unikaj w międzyczasie Ctrl+C. Podczas pobierania przerwanie jest niegroźne, ale podczas rozpakowywania i konfigurowania tworzy dokładnie te pakiety zainstalowane w połowie, o których mowa w sekcji wyżej.
  4. Nie pozwól, żeby własne prace serwisowe kolidowały z timerami systemowymi. Kto prowadzi własną aktualizację według harmonogramu, ustawia ją w innych godzinach i z timeoutem. Jak zrobić to porządnie, opisują wpisy o cronjobach w Linuksie oraz o własnych usługach systemd.

I jeszcze uwaga co do pozornie najprostszego wyjścia: apt remove unattended-upgrades owszem, likwiduje konflikty blokad, ale odbiera ci też automatyczne aktualizacje bezpieczeństwa. Na serwerze dostępnym publicznie to kiepski interes. Znacznie rozsądniej jest zachować automatyczną aktualizację, a własne operacje nauczyć cierpliwości.


W skrócie: lsof na wskazany plik blokady, sprawdzenie listy procesów, czekanie. Dopiero gdy udowodnisz, że nic już nie działa, usuwasz pliki blokady, a potem zawsze dpkg --configure -aapt-get check. Z DPkg::Lock::Timeout w konfiguracji apt i pętlą powtórzeń wokół apt-get update reszta załatwia się sama.

Najczęstsze pytania

Czy mogę po prostu skasować plik blokady?
Tylko wtedy, gdy wcześniej udowodnisz poleceniem lsof albo fuser, że żaden proces już jej nie trzyma. Blokada wisi na otwartym deskryptorze pliku, a nie na nazwie pliku. Jeśli skasujesz plik w chwili, gdy proces pracuje, na tej samej bazie pakietów pracują potem dwie operacje naraz, a to ją uszkadza.
Jak długo mam czekać, zanim zainterweniuję?
Przy unattended-upgrades dziesięć do trzydziestu minut to norma, przy dużych aktualizacjach przez wolne serwery lustrzane bywa i dłużej. Sprawdź poleceniem tail -f /var/log/apt/term.log, czy coś się jeszcze rusza. Dopóki dochodzą tam nowe wiersze, operacja pracuje i nie wolno w nią ingerować.
Co oznacza nazwa procesu unattended-upgr w komunikacie o błędzie?
To automatyczna aktualizacja bezpieczeństwa, uruchomiona przez timery systemd apt-daily.timer i apt-daily-upgrade.timer. Nazwa jest skrócona do 15 znaków. Tę operację powinieneś pozwolić dokończyć, a nie przerywać.
Dlaczego po usunięciu pliku blokady potrzebuję dpkg --configure -a?
Przerwana operacja zostawia pakiety w stanie rozpakowany, ale nieskonfigurowany. dpkg --configure -a nadrabia te zaległe kroki. Bez tego polecenia apt odmawia każdej kolejnej instalacji komunikatem dpkg was interrupted.
Jak zapobiec temu błędowi w skryptach i rolach Ansible?
Poleceniem apt-get -o DPkg::Lock::Timeout=300 zamiast pętli czekającej na nazwy procesów. Na stałe wpisz DPkg::Lock::Timeout "300"; do /etc/apt/apt.conf.d/99lock-timeout, wartość -1 oznacza czekanie bez ograniczeń. Ważne: opcja działa tylko przy blokadzie frontendu dpkg i przy blokadzie dpkg. Przy blokadzie archiwum i blokadzie list apt mimo wszystko nie czeka, dlatego przy apt-get update nie daje ona żadnego efektu, co zostało sprawdzone pomiarowo. Tam potrzebujesz własnej pętli powtórzeń, na przykład: for i in $(seq 30); do apt-get update && break; sleep 10; done
Czy ten błąd występuje też na AlmaLinux albo Rocky Linux?
W tej postaci nie. dnf blokuje przez /var/run/dnf.pid i domyślnie czeka na drugą operację, zamiast przerywać. Komunikat brzmi tam Waiting for process with pid ... to finish.
apt po naprawie nadal zgłasza błędy dotyczące jednego pakietu, co teraz?
Sprawdź poleceniem dpkg --audit, którego pakietu dotyczy problem, usuń dokładnie ten jeden przez dpkg --remove --force-remove-reinstreq PAKIET, a potem zainstaluj go ponownie. Tej opcji nigdy nie stosuj hurtowo do wszystkich pakietów.

apt dpkg Debian Ubuntu Zarządzanie pakietami Rozwiązywanie problemów unattended-upgrades Linux