Ubuntu/Debian: E: Could not get lock /var/lib/dpkg/lock-frontend — 누가 쥐고 있고 어떻게 기다리는지
안녕하세요, BlueByte입니다. 새로 띄운 Ubuntu VM 에서 sudo apt-get install 을 치자마자 첫 줄에서 E: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1234 (unattended-upgr) 가 뜨고, 이어서 E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), is another process using it? 로 멈춥니다. 고장난 것은 없습니다. 다른 패키지 관리자가 같은 시스템에서 작업 중이고, apt 는 둘을 동시에 돌리지 않을 뿐입니다. 오늘은 누가 락을 쥐고 있는지, apt 와 apt-get 이 그 앞에서 왜 다르게 구는지, 살아 있는 보유자와 중단된 보유자 각각의 해결, 그리고 락 파일 삭제가 왜 유일하게 하면 안 되는 일인지 하나씩 짚어보겠습니다.
apt-get 은 즉시 실패하고 apt 는 기다리는 이유
두 명령은 같은 락을 잡습니다. 차이는 인내심입니다. Debian apt 변경 이력에는 1.9.11 의 apt(8): Wait for lock 과 2.0.0 의 절대 시각을 보여주도록 손본 메시지가 기록되어 있습니다. Ubuntu 24.04 의 apt 2.8.3 에서 apt-config dump 를 보면 그 인내심이 어디서 오는지 나옵니다.
apt-config dump | grep Lockbinary::apt::DPkg::Lock::Timeout "120";
binary::apt:: 범위는 apt 바이너리에만 적용되므로, apt install 은 Waiting for cache lock: Could not get lock /var/lib/dpkg/lock-frontend. It is held by process 1234 (unattended-upgr)... 을 찍으며 2 분 동안 재시도하고, 그런 설정이 없는 apt-get install 은 바로 포기합니다. 터미널에서 apt 를 치는 사람보다 apt-get 을 쓰는 스크립트가 이 오류를 훨씬 자주 만나는 이유가 여기 있습니다.
락 파일 세 개와 각각이 내는 메시지
직접 확인해 보면, 각 락을 POSIX 락으로 붙잡고 apt 를 돌렸을 때 세 가지 메시지가 나옵니다. /var/lib/dpkg/lock-frontend 는 install·upgrade·remove 중에 위의 두 줄을 냅니다. /var/lib/apt/lists/lock 은 apt-get update 중에 E: Could not get lock /var/lib/apt/lists/lock. It is held by process 1234 (python3) 와 E: Unable to lock directory /var/lib/apt/lists/ 를 냅니다. /var/cache/apt/archives/lock 은 다운로드 캐시를 지킵니다. 셋 다 항상 존재하는 파일 위의 fcntl 락이라 파일이 있다는 사실 자체는 아무 뜻이 없고, 보유자가 종료하는 순간 커널이 락을 풉니다. 저희 재현에서도 붙잡고 있던 프로세스가 끝나자 오류는 그 즉시 사라졌고 어떤 정리도 필요 없었습니다.
누가 쥐고 있나: apt 가 찍은 PID 를 읽고 타이머를 보기
apt 가 이미 PID 와 프로세스 이름을 알려주니, 최소 Ubuntu 이미지에는 깔려 있지도 않은 lsof 나 fuser 대신 거기서 시작하세요.
ps -p 1234 -o pid,ppid,etime,cmd
systemctl list-timers 'apt-daily*' --no-pager
systemctl status apt-daily-upgrade.service unattended-upgrades.service --no-pager PID PPID ELAPSED CMD
1234 1 02:41 /usr/bin/python3 /usr/bin/unattended-upgrade
NEXT LEFT LAST PASSED UNIT ACTIVATES
Mon 2026-09-21 06:45:41 KST 6h Sun 2026-09-20 06:53:41 KST 17h ago apt-daily-upgrade.timer apt-daily-upgrade.service
보유자 이름이 unattended-upgr 이면 Ubuntu 의 자동 보안 업데이트입니다. Ubuntu Server 문서는 그 뒤의 systemd 타이머 둘을 설명합니다. apt-daily.timer 는 패키지 목록을 갱신하고 apt-daily-upgrade.timer 는 업그레이드를 설치하며, 둘 다 /usr/lib/apt/apt.systemd.daily 를 실행합니다. 저희가 확인한 호스트에서 업그레이드 타이머는 06:00 에 최대 1 시간의 무작위 지연을 두고 발화하고, Persistent=true 는 문서 표현대로 타이머 시각에 머신이 꺼져 있었다면 다음 부팅 직후 바로 발화할 수 있다는 뜻입니다. 방금 부팅한 VM 에 로그인했더니 이미 업그레이드 중인 경우가 그토록 흔한 이유가 이 마지막 구절입니다. 진행 상황은 /var/log/unattended-upgrades/unattended-upgrades.log 에, dpkg 세부는 그 옆 unattended-upgrades-dpkg.log 에 있습니다.
해결 1: 돌고 있는 업그레이드를 끝내게 두거나 apt-get 에 기다리라고 하기
살아서 진행 중인 보유자에게 필요한 것은 시간뿐입니다. 로그를 지켜보다 끝나면 명령을 돌리세요.
sudo tail -f /var/log/unattended-upgrades/unattended-upgrades.log스크립트라면 초 단위 타임아웃을 넘겨 apt-get 에 apt 와 같은 인내심을 주세요. 락이 풀리거나 타임아웃이 끝날 때까지 대기 줄이 찍힙니다.
sudo apt-get -o DPkg::Lock::Timeout=600 install -y nginx자동 실행을 일찍 멈춰야 한다면 kill -9 대신 systemd 를 쓰세요. Ubuntu 의 unattended-upgrades 서비스는 unattended-upgrade-shutdown --wait-for-signal 이라는 감시 프로세스를 돌리는데, 그 정지 경로는 실행 중인 unattended-upgrade 에 SIGTERM 을 보내고, 그 프로세스는 SIGTERM received, will stop 을 남기고 지금 처리 중인 패키지를 끝낸 뒤 종료합니다. 유닛은 여기에 TimeoutStopSec=1800 까지 허용합니다. 그래서 systemctl stop unattended-upgrades 는 잠시 멈춰 있다가 dpkg 를 일관된 상태로 남깁니다. 겁먹지 않아도 됩니다. 이것이 바로 원하는 동작입니다.
해결 2: 죽은 보유자나 중단된 dpkg 는 하던 일을 마무리하기
ps -p 에서 그 PID 는 사라졌는데 다른 dpkg 나 apt 가 새 보유자라면 그것도 기다리세요. 이전 실행이 설치 도중 죽었다면 락은 비어 있지만 apt 가 이렇게 말합니다. E: dpkg was interrupted, you must manually run 'sudo dpkg --configure -a' to correct the problem. dpkg 매뉴얼은 --configure -a(또는 --pending)를 언팩됐지만 아직 구성되지 않은 모든 패키지를 구성하는 동작으로 정의하는데, 중단된 실행이 남기는 상태가 정확히 그것입니다. 어떤 패키지인지 외울 필요는 없습니다. dpkg 가 찾아냅니다.
sudo dpkg --configure -a
sudo apt-get install -f두 번째 명령은 중단이 깨뜨린 의존성을 고칩니다. 업그레이드 중 전원이 나간 머신은 보통 둘 다 필요합니다.
락 파일 삭제가 잘못된 지름길인 이유
rm /var/lib/dpkg/lock-frontend 라는 포럼 조언이 살아남는 이유는 되는 것처럼 보이기 때문입니다. 다음 apt 실행이 파일을 다시 만들고 시작합니다. 하지만 락은 경로가 아니라 열린 파일 디스크립터에 걸려 있어서, 경로를 지워도 쥐고 있는 프로세스에는 아무 영향이 없습니다. 결과는 두 패키지 관리자가 /var/lib/dpkg/status 를 동시에 쓰는 것이고, 위의 중단된 dpkg 상태는 그중 가벼운 결말입니다. dpkg 매뉴얼은 dpkg 1.19.1 부터 프런트엔드가 설정하는 DPKG_FRONTEND_LOCKED 환경 변수를 설명하는데, dpkg 자신이 프런트엔드 락을 잡지 않게 하는 이 조율을 삭제된 파일이 우회해 버립니다.
실제 사례: 새 VM 의 cloud-init 이 unattended-upgrades 와 경쟁하다
한 팀의 프로비저닝 스크립트가 Ubuntu 24.04 첫 부팅 약 40 초 뒤에 apt-get install -y docker.io 를 돌렸는데, VM 다섯 대 중 한 대가 프런트엔드 락 오류로 실패했습니다. 실패한 VM 에서 ps -p 를 보니 unattended-upgrade 의 경과 시간이 1 분 미만이었습니다. persistent 타이머가 부팅 때 발화한 것입니다. 스크립트에 -o DPkg::Lock::Timeout=300 을 넣자 모든 VM 이 실패 대신 보안 실행이 끝날 때까지 기다렸고, 로그로 확인하니 그 이미지에서 실행은 2~3 분이 걸렸습니다.
고쳐졌는지 확인하고 이미지와 CI 에서 재발을 막기
고친 뒤 sudo apt-get check 가 락 줄 없이 돌아와야 합니다. 이 명령이 프런트엔드 락을 잡는다는 것을 저희가 확인했으니 진짜 프로브입니다. 그다음 pgrep -af unattended-upgrade 에는 항상 떠 있는 unattended-upgrade-shutdown --wait-for-signal 감시 프로세스만 있고 /usr/bin/unattended-upgrade 워커는 없어야 합니다. ps 는 두 이름을 모두 unattended-upgr 로 잘라 보여주니 속지 마세요. 골든 이미지와 CI 러너에서 경쟁을 없애려면 Ubuntu 문서대로 /etc/apt/apt.conf.d/20auto-upgrades 에 APT::Periodic::Unattended-Upgrade "0" 을 두거나, 업데이트는 유지하되 systemctl edit apt-daily-upgrade.timer 에 Persistent=false 를 넣어 놓친 실행이 다음 부팅이 아니라 다음 예약 시각을 기다리게 하세요. 어느 쪽이든 스크립트의 모든 apt-get 에는 DPkg::Lock::Timeout 을 남겨 두세요. 락이 비어 있을 때는 비용이 전혀 없습니다.
인접 오류: update 중의 lists 락과 중단된 dpkg 메시지
Could not get lock /var/lib/apt/lists/lock 은 apt-get update 에서 나오며 목록 갱신, 대개 apt-daily.service 가 돌고 있다는 뜻입니다. 같은 대기 규칙이 적용됩니다. dpkg was interrupted 는 락이 아니라 락이 깨진 뒤의 복구 단계입니다. 그리고 락 경로에 Permission denied 가 뜨면 누가 쥐고 있는 것이 아니라 sudo 를 빠뜨린 것입니다.
다음에 설치가 락에서 멈추면 이 순서를 거꾸로 따라가 보세요. apt 가 찍은 PID 를 읽고, 무엇을 하는지 보고, 기다리거나 apt-get 에 타임아웃을 주고, 실행이 실제로 중단됐을 때만 dpkg --configure -a 를 꺼내면 됩니다.
관련 질문
apt 는 기다렸을 텐데 apt-get 은 왜 즉시 실패했나요?
Ubuntu 는 대기를 apt 바이너리에만 설정합니다. apt-config dump 에 binary::apt::DPkg::Lock::Timeout "120" 이 보입니다. apt-get 은 기본 타임아웃이 없으므로 스크립트에서는 -o DPkg::Lock::Timeout=<초> 를 넘겨 같은 대기 동작을 얻으세요.
/var/lib/dpkg/lock-frontend 를 그냥 지우면 안 되나요?
안 됩니다. 락은 열린 파일 디스크립터 위의 fcntl 락이라 경로를 지워도 보유자는 계속 돌고, 두 번째 패키지 관리자가 /var/lib/dpkg/status 를 동시에 쓰게 됩니다. 보유자가 종료하면 커널이 락을 풀고, 아무 프로세스도 쥐고 있지 않으면 이 오류는 아예 나오지 않습니다.
보유자가 unattended-upgrades 인지 어떻게 아나요?
메시지가 프로세스 이름을 알려주고, ps -p <pid> -o pid,ppid,etime,cmd 가 /usr/bin/unattended-upgrade 와 경과 시간을 보여줍니다. 진행 상황은 /var/log/unattended-upgrades/unattended-upgrades.log 에 있고, systemctl list-timers 'apt-daily*' 가 타이머가 언제 발화했는지 보여줍니다.
실행 도중에 unattended-upgrades 를 멈춰도 안전한가요?
kill -9 대신 systemctl stop unattended-upgrades 를 쓰세요. 서비스의 감시 프로세스가 실행 중인 unattended-upgrade 에 SIGTERM 을 보내고, 그 프로세스는 신호를 받았다고 로그를 남긴 뒤 처리 중인 패키지를 끝내고 종료합니다. 유닛은 여기에 최대 1800 초를 허용합니다.
dpkg --configure -a 는 언제 필요한가요?
apt 가 dpkg was interrupted 라고 말할 때만입니다. dpkg 매뉴얼은 --configure -a 를 언팩됐지만 아직 구성되지 않은 모든 패키지를 구성하는 동작으로 정의하는데, 죽은 실행이 남기는 상태가 그것입니다. 이어서 apt-get install -f 로 의존성을 고치세요.
참고 자료
- Ubuntu Server docs — Automatic updates (unattended-upgrades, apt-daily / apt-daily-upgrade timers, Persistent catch-up at boot, /var/log/unattended-upgrades/, 20auto-upgrades)
- dpkg(1) man page — --configure package...|-a|--pending, DPKG_FRONTEND_LOCKED (since dpkg 1.19.1)
- Debian apt changelog — 1.9.11 "apt(8): Wait for lock", 2.0.0 lock-wait message with absolute time
Haneul Seo
Infrastructure engineer · 10+ years running Linux fleets
같은 카테고리 다른 글
systemd: Start request repeated too quickly
systemd는 StartLimitIntervalSec(기본 10초) 안에 StartLimitBurst(기본 5회)보다 많이 시작된 유닛의 시작을 거부하며, Restart=도 이 제한에 포함됩니다. 기본 RestartSec 100ms로는 크래시하는 서비스가 1초도 안 되어 다섯 번을 소진합니다. 저널에서 진짜 크래시를 찾아 고치고, reset-failed를 실행한 뒤, RestartSec로 재시작에 여유를 주세요.
SSH: Received disconnect ... Too many authentication failures
agent가 서버가 허용하는 것보다 많은 키를 내밀고 있습니다. sshd가 들여다보는 공개키 하나하나가 MaxAuthTries 시도를 한 번씩 소모하고 — 기본 6회, 하드닝된 호스트에서는 3회인 경우가 많습니다 — 정작 맞는 키는 차례가 오기 전에 연결이 끊깁니다. IdentitiesOnly=yes와 명시적 IdentityFile로 키 하나를 고정하면 시도 횟수가 1로 떨어집니다.
Active Directory: 복제가 error 1722(The RPC server is unavailable)로 실패
RPC는 하위 계층이 연결에 실패했을 때 1722(0x6ba, RPC_S_SERVER_UNAVAILABLE)를 보고합니다. 따라서 진짜 원인은 RPC 자신이 아니라 DNS, 차단된 포트, 두 도메인 컨트롤러 중 한쪽의 호스트 설정입니다. repadmin이 어느 파트너가 실패하는지 알려주고, dcdiag /test:dns가 이름 해석을 배제하며, Test-NetConnection과 동적 포트 범위가 방화벽 문제를 가릅니다. 가장 흔한 실수는 TCP 135만 허용하고 49152–65535는 막아 둔 규칙입니다.
Windows Server RDS: The remote session was disconnected because there are no Remote Desktop License Servers available to provide a license
120일 RD Licensing 유예 기간이 끝났는데 세션 호스트가 쓸 수 있는 라이선스 서버가 없어 세션을 거부하는 상황입니다. GetGracePeriodDays 의 DaysLeft 가 0 이고 SpecifiedLSList 가 비어 있으면 몇 초 만에 확인됩니다. 해결은 활성화된 라이선스 서버와, 호스트 버전을 감당할 만큼 새 CAL 입니다. 2019 CAL 은 2022 세션 호스트를 서비스하지 못합니다. 여기에 배포 또는 Licensing 정책 설정과 두 서버 사이의 RPC 포트 개방이 따라옵니다.
Windows 11: NAS 공유 폴더를 열 때 "조직의 보안 정책이 인증되지 않은 게스트 액세스를 차단" (0x80070035)
Windows 10 Enterprise/Education/Pro for Workstations, Windows 11 Pro, Windows Server 2019 이후의 SMB 클라이언트는 기본적으로 게스트 로그온을 거부하고, Windows 11 24H2 Enterprise/Pro/Education 은 게스트 세션이 할 수 없는 SMB 서명까지 요구합니다. 그래서 게스트 접근만 제공하는 NAS 공유는 'block unauthenticated guest access' 대화상자, Error code 0x80070035, 또는 System error 3227320323 으로 실패하고, SmbClient/Security 로그에 Event ID 31017 'Rejected an insecure guest logon' 이 남습니다. Microsoft 가 권하는 해결은 NAS 에 실제 계정을 만들고 펌웨어에서 서명을 지원하게 하는 것입니다. Set-SmbClientConfiguration -EnableInsecureGuestLogons $true(24H2 는 -RequireSecuritySignature $false 까지)는 비상구이며, 그 클라이언트의 서명과 암호화를 포기하는 대가가 있습니다.
macOS: xcrun: error: invalid active developer path (/Library/Developer/CommandLineTools)
macOS의 /usr/bin에 있는 git·make·clang 같은 개발 명령은 활성 developer 디렉터리로 넘겨주는 shim이고, xcrun은 xcode-select가 가리키는 디렉터리에 도구가 없다고 보고하는 것입니다 — 대개 macOS 메이저 업그레이드가 /Library/Developer/CommandLineTools를 비워 두었거나, Xcode가 옮겨지거나 삭제된 경우입니다. xcode-select -p와 패키지 영수증을 확인한 뒤 xcode-select --install로 Command Line Tools를 다시 설치하거나, 실제로 있는 Xcode를 xcode-select로 가리키면 됩니다.