Could not get lock /var/lib/dpkg/lock-frontend: solución

Lanzas por SSH un script de instalación largo en un servidor Ubuntu 24.04 (Docker, swap, dependencias). A mitad de camino se corta la conexión, vuelves a entrar, ejecutas de nuevo el script y falla al primer apt-get:

E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1234 (apt-get)
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?

El mensaje no habla de un fallo: dice que otro proceso tiene el candado del gestor de paquetes. Este artículo explica cómo averiguar cuál es, qué hacer con él sin dañar el sistema y cómo lanzar trabajos largos para que no dependan de tu terminal. Los comandos se contrastaron con los manuales de apt, dpkg y systemd, y con la documentación de Ubuntu Server; no se ejecutaron sobre un servidor.

Por qué pasa

El gestor de paquetes permite que un solo proceso modifique la base de datos de paquetes a la vez, y lo garantiza con archivos de bloqueo como /var/lib/dpkg/lock-frontend. Cuando el candado está en uso, el segundo proceso se niega a continuar e indica el PID y el nombre del que lo tiene (aquí, apt-get).

Si la sesión SSH se corta a mitad de una instalación, desaparece el terminal, pero eso no garantiza que terminen todos los procesos que lanzó el script. Un apt-get o un dpkg que ya estaba en marcha puede seguir trabajando sin que nadie lo mire, y mientras lo haga conserva el candado. Es la razón por la que el segundo intento falla: el primero todavía no terminó.

También puede ser otro proceso del sistema. La documentación de Ubuntu Server explica que unattended-upgrades puede ejecutarse al arrancar la máquina y, mientras trabaja, impide otras operaciones de paquetes: si necesitas instalar algo, debe esperar a que terminen las actualizaciones. Es frecuente en un servidor recién creado.

La advertencia del propio mensaje lo resume: borrar el archivo de bloqueo no es la solución y puede dañar el sistema.

Solución

  1. Identifica el proceso que tiene el candado. El mensaje ya da el PID. Con él y con las herramientas del sistema:

    ps -p 1234 -o pid,etime,cmd
    sudo fuser -v /var/lib/dpkg/lock-frontend
    sudo lsof /var/lib/dpkg/lock-frontend
    ps -ef | grep -E 'apt|dpkg'

    ps -p muestra el comando completo y el tiempo que lleva en marcha (etime). fuser -v lista los procesos que usan el archivo, en formato parecido a ps; lsof hace algo equivalente, usa el que tengas instalado. La última línea busca cualquier proceso de apt o dpkg (verás también la propia línea de grep).

  2. Comprueba si avanza. Mira el registro de dpkg y el registro de tu script:

    sudo tail -f /var/log/dpkg.log
    tail -f instalar.log

    Si aparecen líneas nuevas (paquetes que se descomprimen, se configuran), el proceso sigue trabajando.

  3. Si avanza, espera a que termine. Este bucle se detiene cuando nadie usa el archivo (fuser devuelve un código distinto de cero si ningún proceso accede a él):

    while sudo fuser /var/lib/dpkg/lock-frontend >/dev/null 2>&1; do sleep 5; done; echo "candado libre"

    Después vuelve a lanzar el script. Antes, comprueba que se pueda repetir sin efectos secundarios: instalar un paquete ya instalado no cambia nada, pero crear un archivo de swap o agregar una línea a /etc/fstab por segunda vez sí puede.

  4. Si lleva mucho tiempo sin avanzar, deténlo con cuidado. Pide primero una terminación normal y espera:

    sudo kill 1234

    Evita la señal SIGKILL (kill -9) salvo como último recurso: puede dejar paquetes a medio instalar. Si el proceso se interrumpió, repara el estado de dpkg antes de seguir:

    sudo dpkg --configure -a
    sudo apt-get install -f

    Según el manual de dpkg, --configure -a configura todos los paquetes desempaquetados pero sin configurar. La opción -f (--fix-broken) de apt-get intenta corregir un sistema con dependencias rotas.

  5. No borres los archivos de bloqueo (rm /var/lib/dpkg/lock-frontend y similares). El mensaje de apt lo desaconseja de forma explícita, y el candado lo tiene un proceso vivo, no el archivo.

  6. Lanza los trabajos largos desacoplados de tu terminal. Tres opciones, de menos a más robusta:

    # 1. En una sesión nueva, sin terminal asociado
    sudo setsid -f bash ./instalar.sh > instalar.log 2>&1 < /dev/null
    tail -f instalar.log
    
    # 2. Dentro de tmux: puedes desconectarte y volver
    tmux new -s instalacion
    bash ./instalar.sh
    # Ctrl+b d para desacoplar; después: tmux attach -t instalacion
    
    # 3. Como servicio transitorio de systemd
    sudo systemd-run --unit=instalacion /bin/bash /ruta/instalar.sh
    journalctl -u instalacion -f

    El manual de setsid indica que ejecuta un programa en una sesión nueva (y -f obliga a crear un proceso nuevo, de modo que el comando devuelve el control enseguida). Lanza el script con sudo desde el principio: sin terminal no podrás responder a una petición de contraseña a mitad del trabajo. En tmux, new -s crea una sesión con nombre y attach -t se conecta a ella; la tecla para desacoplar por defecto es Ctrl+b seguida de d. Con systemd-run, el comando corre como un servicio gestionado por systemd, en un entorno limpio y separado, con el gestor de servicios como proceso padre.

  7. Si el trabajo debe sobrevivir a un reinicio, crea un servicio. Archivo /etc/systemd/system/instalacion.service:

    [Unit]
    Description=Instalacion inicial del servidor
    Wants=network-online.target
    After=network-online.target
    
    [Service]
    Type=oneshot
    ExecStart=/bin/bash /opt/instalador/instalar.sh
    RemainAfterExit=yes
    Restart=on-failure
    RestartSec=30
    
    [Install]
    WantedBy=multi-user.target
    sudo systemctl daemon-reload
    sudo systemctl enable instalacion.service
    sudo systemctl start --no-block instalacion.service

    En Type=oneshot, el tiempo de espera de arranque está desactivado por defecto, así que una instalación larga no se corta por duración. Restart=on-failure está permitido para este tipo (el manual rechaza always y on-success) y relanza el script si termina con error, por lo que el script debe poder repetirse sin efectos secundarios. RemainAfterExit=yes mantiene el servicio como activo cuando el script termina bien. enable lo habilita para los próximos arranques. Sin --no-block, systemctl start espera a que el arranque de la unidad se complete, y en un servicio oneshot eso ocurre cuando el script termina; con la opción, solo encola el trabajo y te devuelve la consola.

Cómo verificar que quedó resuelto

sudo fuser -v /var/lib/dpkg/lock-frontend
echo $?
sudo dpkg --audit

Si nadie usa el archivo, fuser no imprime procesos y el código de salida es distinto de cero. dpkg --audit busca paquetes instalados solo en parte o con datos de control dañados; no debe listar nada. Si lanzaste el trabajo con setsid, tmux o systemd-run, cierra la sesión SSH, vuelve a entrar y comprueba que el registro (instalar.log o journalctl -u instalacion) sigue creciendo.

Variantes y errores relacionados

Otros archivos de bloqueo

El mismo error puede nombrar /var/lib/dpkg/lock o /var/lib/apt/lists/lock. El procedimiento es idéntico: el mensaje indica el proceso que lo tiene.

Lo tiene unattended-upgrades

Si el proceso es de actualizaciones automáticas, lo correcto es esperar. Según la documentación de Ubuntu, esas actualizaciones las lanzan los temporizadores apt-daily.timer y apt-daily-upgrade.timer, y sus registros están en /var/log/unattended-upgrades.

apt pide ejecutar dpkg --configure -a

Suele indicar que una instalación anterior se interrumpió. Ejecuta el comando como se indica en el paso 4 y repite después la instalación.

Dos instaladores a la vez

Si lanzaste el trabajo como servicio y además a mano, el segundo chocará con el candado del primero. Elige un solo mecanismo y revisa con systemctl status instalacion si ya está en marcha.

Resumen

  • El error significa que un proceso vivo (por ejemplo, el apt-get de la primera ejecución) tiene el candado de dpkg; no es un archivo corrupto.
  • Identifícalo con el PID del mensaje, ps y fuser -v; si avanza, espera; si está atascado, termínalo con kill y ejecuta sudo dpkg --configure -a.
  • No borres los archivos de bloqueo: el propio apt advierte que puede dañar el sistema.
  • Lanza instalaciones largas con setsid -f, tmux o systemd-run, con el registro en un archivo.
  • Si debe sobrevivir a reinicios, usa un servicio Type=oneshot con Restart=on-failure y un script que se pueda repetir.

Referencias

Comentarios