우분투 apt Could not get lock /var/lib/dpkg/lock-frontend 해결: unattended-upgr 대기와 dpkg was interrupted 복구

이 글의 핵심

apt의 Could not get lock 오류는 다른 프로세스가 지금 실제로 패키지 작업을 하고 있다는 신호이고, 우분투에서는 그 주인이 자동 보안 업데이트(unattended-upgrades)인 경우가 대부분입니다. 이 글은 잠금이 왜 있는지, 누가 쥐고 있는지 확인하는 법, 기다릴지 안전하게 멈출지 고르는 기준, 잠금 파일을 지우면 왜 dpkg 데이터베이스가 망가질 수 있는지, 그리고 dpkg was interrupted 상태에서 복구하는 순서를 우분투 22.04·24.04 기준으로 설명합니다.

오류 메시지가 말해 주는 것

우분투에서 sudo apt install이나 sudo apt update를 실행했다가 다음 출력을 만나는 경우가 많습니다.

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?

apt update를 실행했다면 경로만 다른 형태가 나옵니다.

E: Could not get lock /var/lib/apt/lists/lock. 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 lock directory /var/lib/apt/lists/

이 메시지에는 필요한 정보가 이미 다 들어 있습니다. 잠금 파일 경로, 잠금을 쥔 프로세스 번호(PID), 그 프로세스 이름입니다. 괄호 안 이름이 unattended-upgr처럼 잘려 보이는 것은 리눅스가 프로세스 이름(comm)을 15자까지만 저장하기 때문입니다. 실제 이름은 unattended-upgrade입니다.

그리고 두 번째 줄의 N: 안내는 apt 개발진이 직접 넣은 경고입니다. 인터넷에 흔한 “lock 파일을 지우면 된다”는 해결법이 실제로 시스템을 망가뜨리는 경우가 많아서, 오류 메시지 안에 대놓고 그러지 말라고 적어 둔 것입니다. 왜 그런지는 아래에서 설명합니다.

기준 환경은 우분투 22.04 LTS(apt 2.4)와 24.04 LTS(apt 2.7)입니다. 20.04 이전의 apt는 It is held by process 부분 없이 open (11: Resource temporarily unavailable) 형태로 출력하지만, 의미와 해결 방법은 같습니다.

sudo를 빠뜨린 경우와 구분하기

먼저 흔한 실수 하나를 걸러 냅니다. sudo 없이 실행하면 비슷하게 생긴 다른 오류가 나옵니다.

E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied)
E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root?

Could not open, Permission denied, are you root?가 보이면 다른 프로세스와는 상관없고 권한 문제입니다. 명령 앞에 sudo를 붙여 다시 실행하면 됩니다. 이 글의 나머지는 Could not get lock과 is another process using it?가 나오는 경우를 다룹니다.

apt와 dpkg는 왜 잠금을 거는가

우분투의 패키지 설치는 두 단계 도구로 이루어집니다. apt(또는 apt-get)가 저장소에서 패키지와 의존성을 계산해 내려받고, 실제로 파일을 풀어 시스템에 설치하는 일은 그 아래의 dpkg가 합니다. dpkg는 어떤 패키지가 어떤 버전으로 설치되었고 어떤 파일을 소유하는지를 /var/lib/dpkg/status와 /var/lib/dpkg/info/ 아래 파일들, 즉 dpkg 데이터베이스에 기록합니다. apt와 dpkg의 관계는 우분투 필수 명령어 글의 apt 절에 더 자세히 정리되어 있습니다.

이 데이터베이스는 동시에 두 프로세스가 고치는 상황을 전제로 만들어지지 않았습니다. 두 개의 dpkg가 동시에 돌면서 하나는 libssl을 업그레이드하고 다른 하나는 libssl에 의존하는 패키지를 설치한다면, 두 쪽이 서로 다른 시점의 데이터베이스를 읽고 써서 기록이 실제 파일 상태와 어긋납니다. 그래서 패키지 작업을 하는 동안에는 잠금을 걸어 한 번에 하나만 작업하게 합니다.

잠금 파일은 용도별로 네 개입니다.

잠금 파일거는 쪽보호하는 것
/var/lib/dpkg/lock-frontendapt 등 프런트엔드설치 작업 전체 (apt가 dpkg를 여러 번 호출하는 동안)
/var/lib/dpkg/lockdpkgdpkg 데이터베이스
/var/lib/apt/lists/lockapt update저장소 패키지 목록
/var/cache/apt/archives/lockapt내려받은 .deb 캐시

lock-frontend가 따로 있는 이유가 있습니다. apt는 한 번의 apt install 안에서 dpkg를 여러 번 실행합니다. dpkg 하나가 끝나고 다음 dpkg가 시작되기 전의 짧은 틈에 다른 도구가 끼어들면 안 되기 때문에, apt가 작업 전체 동안 프런트엔드 잠금을 쥐고 있는 것입니다.

잠금 파일을 지우면 안 되는 이유

여기서 중요한 사실이 하나 있습니다. apt의 잠금은 “파일이 존재하는지”가 아니라 “어떤 프로세스가 그 파일에 커널 잠금(fcntl)을 걸고 있는지”로 판단합니다. 잠금 파일은 평소에도 늘 그 자리에 있습니다. 지금 ls -l /var/lib/dpkg/lock-frontend를 해 보면 아무 작업도 하지 않을 때도 파일이 보일 것입니다.

커널 잠금은 그 잠금을 건 프로세스가 끝나면, 정상 종료든 강제 종료든 커널이 자동으로 풉니다. 따라서 프로세스가 죽었는데 잠금만 남아 있는 “고아 잠금”은 사실상 생기지 않습니다. 이 말을 거꾸로 하면, Could not get lock ... held by process 1234가 나왔다면 PID 1234는 지금 실제로 살아서 잠금을 쥐고 있다는 뜻입니다.

이 상태에서 sudo rm /var/lib/dpkg/lock-frontend를 하면 무슨 일이 생길까요. 기존 프로세스는 이미 열어 둔 파일(이제 이름이 없는 파일)에 잠금을 계속 쥐고 작업을 이어 갑니다. 새로 실행한 apt는 같은 경로에 새 파일을 만들고, 거기에는 아무도 잠금을 걸지 않았으니 문제없이 잠금을 얻습니다. 결과적으로 잠금이 막으려던 상황, 즉 두 프로세스가 동시에 dpkg 데이터베이스를 고치는 상황이 그대로 일어납니다. dpkg/lock까지 지우면 dpkg 단계의 마지막 보호까지 사라집니다.

그 결과는 운이 좋으면 아무 일도 없고, 운이 나쁘면 이런 형태로 나타납니다.

  • 패키지가 “설치됨”으로 기록되었지만 실제 파일 일부가 없음
  • 설치 후 스크립트가 반쯤 실행된 채 멈춰 다음 apt 실행마다 오류
  • /var/lib/dpkg/status나 updates/ 아래 파일이 깨져 dpkg 자체가 파싱 오류를 냄

특히 커널, libc, systemd처럼 핵심 패키지가 업그레이드되는 중이었다면 다음 부팅에 문제가 생길 수도 있습니다. 잠금 파일 삭제는 기다리면 몇 분 안에 끝날 일을 복구에 한참 걸리는 일로 바꿀 수 있습니다.

잠금을 쥔 프로세스 확인하기

오류 메시지에 PID가 나오지만, 그 프로세스가 무엇을 하는 중인지 확인해 두면 판단이 쉬워집니다.

# 메시지에 나온 PID의 전체 명령줄과 실행 시간
ps -o pid,etime,cmd -p 1234
    PID     ELAPSED CMD
   1234       03:41 /usr/bin/python3 /usr/bin/unattended-upgrade

ELAPSED는 그 프로세스가 실행된 시간입니다. 몇 분이면 정상적으로 일하는 중일 가능성이 큽니다. 몇 시간째라면 무언가에 막혀 있을 수 있습니다.

메시지에 PID가 안 나오는 오래된 버전이거나 다른 잠금 파일까지 한꺼번에 보고 싶다면, 잠금 파일을 쥔 프로세스를 직접 조회합니다.

sudo fuser -v /var/lib/dpkg/lock-frontend /var/lib/dpkg/lock /var/lib/apt/lists/lock /var/cache/apt/archives/lock
                     USER        PID ACCESS COMMAND
/var/lib/dpkg/lock-frontend:
                     root       1234 F.... unattended-upgr
/var/lib/dpkg/lock:  root       1871 F.... dpkg

lsof로도 같은 정보를 볼 수 있습니다.

sudo lsof /var/lib/dpkg/lock-frontend

fuser는 psmisc 패키지, lsof는 lsof 패키지에 들어 있습니다. 일반 우분투 설치에는 대부분 들어 있지만, 최소 설치 이미지나 컨테이너에는 없을 수 있습니다. 그런데 그런 도구를 설치하려고 apt를 쓰면 같은 잠금에 걸리므로, 없으면 ps로 패키지 관련 프로세스를 훑어보는 방법이 현실적입니다.

ps aux | grep -E 'apt|dpkg|unattended|packagekit' | grep -v grep

여기서 흔히 보이는 주인은 다음과 같습니다.

  • unattended-upgr: 자동 보안 업데이트. 서버와 데스크톱 모두 기본으로 켜져 있습니다.
  • apt-get 또는 apt.systemd.daily: 하루 한 번 도는 패키지 목록 갱신·다운로드 작업.
  • packagekitd: 데스크톱의 “소프트웨어” 앱(GNOME Software)이나 업데이트 알림이 뒤에서 쓰는 데몬.
  • apt / dpkg: 다른 터미널 창이나 다른 SSH 세션에서 내가(또는 동료가) 실행해 둔 명령.

마지막 경우가 의외로 많습니다. 다른 탭에서 apt upgrade를 실행하고 [Y/n] 질문에 답하지 않은 채 잊어버리면, 그 프로세스가 잠금을 계속 쥐고 있습니다. 그 터미널을 찾아 답해 주거나 Ctrl+C로 취소하면 바로 풀립니다. 질문 단계에서는 아직 dpkg가 시작되지 않았으므로 취소해도 안전합니다.

부팅 직후에 자주 나는 이유

새로 만든 클라우드 VM에 SSH로 들어가자마자 apt install을 하면 거의 매번 이 오류를 봅니다. 우분투는 systemd 타이머 두 개로 자동 업데이트를 돌립니다.

systemctl list-timers 'apt-daily*'
NEXT                        LEFT     LAST                        PASSED  UNIT                    ACTIVATES
Thu 2026-10-22 06:31:12 UTC 8h left  Wed 2026-10-21 06:12:40 UTC 15h ago apt-daily-upgrade.timer apt-daily-upgrade.service
Thu 2026-10-22 08:04:55 UTC 10h left Wed 2026-10-21 20:47:03 UTC 1h ago  apt-daily.timer         apt-daily.service

apt-daily.service는 패키지 목록을 갱신하고 업데이트를 미리 내려받으며, apt-daily-upgrade.service가 unattended-upgrades를 실행해 보안 업데이트를 설치합니다. 두 타이머 모두 Persistent=true로 설정되어 있어서, 꺼져 있던 동안 놓친 실행이 있으면 부팅 후 곧바로(약간의 무작위 지연 뒤) 실행됩니다. 클라우드 이미지는 만들어진 지 시간이 지난 상태로 부팅되니 밀린 업데이트가 많고, 여기에 cloud-init의 패키지 작업까지 겹치면 몇 분 동안 apt를 쓸 수 없는 것이 정상입니다.

기다리는 것이 기본값인 이유와 기다리는 방법

unattended-upgrades가 잠금을 쥐고 있다면 가장 안전한 선택은 기다리는 것입니다. 보안 업데이트 몇 개는 보통 수 분이면 끝납니다. 진행 상황은 로그로 확인할 수 있습니다.

sudo tail -f /var/log/unattended-upgrades/unattended-upgrades.log
# dpkg 단계의 상세 출력은 이쪽에 남습니다
sudo tail -f /var/log/unattended-upgrades/unattended-upgrades-dpkg.log

로그가 계속 갱신되면 일하는 중이니 기다립니다. 터미널을 붙잡고 있기 싫다면 잠금이 풀릴 때까지 기다렸다가 자동으로 설치하도록 걸어 둘 수 있습니다. apt 1.9.11부터는 잠금 대기 시간을 옵션으로 줄 수 있습니다.

# 잠금이 풀릴 때까지 최대 600초 기다린 뒤 설치
sudo apt-get -o DPkg::Lock::Timeout=600 install -y nginx

이 옵션은 프로비저닝 스크립트나 CI에서 특히 유용합니다. 스크립트에서 sleep 60을 넣어 대충 기다리는 것보다 정확하고, 잠금이 일찍 풀리면 바로 진행합니다. 버전이나 환경에 따라 대화형 터미널에서 apt를 실행하면 오류 대신 Waiting for cache lock: Could not get lock ... 메시지를 띄우고 스스로 기다리는 경우도 있습니다. 이때는 그대로 두면 됩니다.

기다릴 수 없을 때 안전하게 멈추는 방법

급하게 패키지를 설치해야 하거나, unattended-upgrades가 몇 시간째 끝나지 않는다면 멈출 수 있습니다. 여기서 핵심은 프로세스를 kill -9로 죽이지 말고 systemd 서비스를 통해 멈추는 것입니다.

sudo systemctl stop apt-daily-upgrade.service apt-daily.service

systemd는 먼저 SIGTERM을 보냅니다. unattended-upgrades는 SIGTERM을 받으면 진행 중인 패키지 작업을 마친 뒤 멈추도록 만들어져 있어서, 패키지 설치 도중에 끊기는 상황을 피합니다. 그래서 명령이 즉시 끝나지 않고 잠시 걸릴 수 있습니다. 기다리세요. kill -9(SIGKILL)는 프로세스가 아무런 정리 기회 없이 즉시 죽기 때문에, dpkg가 패키지 파일을 풀거나 설정 스크립트를 실행하던 중이라면 그 패키지가 반쯤 설치된 상태로 남습니다. 이것이 다음 절에서 다룰 dpkg was interrupted 상태의 가장 흔한 원인입니다.

헷갈리기 쉬운 이름이 하나 있습니다. unattended-upgrades.service라는 서비스도 있지만, 이것은 종료 시 업데이트를 마무리하는 용도이고 지금 돌고 있는 업데이트 작업을 담당하지 않습니다. 실제 작업은 apt-daily-upgrade.service 아래에서 돕니다.

작업하는 동안 다시 시작되지 않게 하려면 타이머도 잠시 멈춥니다.

sudo systemctl stop apt-daily.timer apt-daily-upgrade.timer

# ... 필요한 패키지 작업 ...

# 끝나면 반드시 다시 켭니다
sudo systemctl start apt-daily.timer apt-daily-upgrade.timer

stop은 재부팅하면 원래대로 돌아오므로, 깜빡 잊어도 다음 부팅에는 자동 업데이트가 다시 동작합니다. disable은 영구적이니 이 목적에는 쓰지 않는 편이 좋습니다.

packagekitd가 주인이라면 열려 있는 “소프트웨어” 앱이나 업데이트 창을 닫으면 대개 풀립니다. 그래도 남아 있으면 sudo systemctl stop packagekit으로 멈출 수 있고, 이 데몬은 필요할 때 다시 자동으로 시작됩니다.

E: dpkg was interrupted 복구 순서

dpkg가 작업 도중에 끊기면 다음 apt 실행에서 이 오류가 납니다.

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

전원이 나갔거나, 업데이트 중 재부팅했거나, 위에서 말한 kill -9를 했거나, 디스크가 가득 차서 설치가 실패한 경우입니다. 메시지가 시키는 명령이 맞는 해결책이지만, 순서를 지켜야 합니다.

1단계: 잠금을 쥔 프로세스가 정말 없는지 확인합니다. 위의 fuser나 ps 명령으로 확인합니다. 아직 unattended-upgrades가 돌고 있는데 dpkg --configure -a를 실행하면, 이 명령도 같은 잠금에 걸려 거부되거나 위에서 말한 동시 실행 문제가 생깁니다.

2단계: 디스크 공간을 확인합니다. df -h /와 df -h /boot를 봅니다. 공간이 부족해서 끊긴 경우라면 공간을 확보하지 않고 다시 시도해도 같은 지점에서 실패합니다. 특히 /boot가 별도 파티션인 시스템은 오래된 커널이 쌓여 커널 업데이트 중에 가득 차는 일이 흔합니다. 용량과 inode 부족을 구분해 정리하는 방법은 Linux 디스크 full vs inode full 해결 순서에 있습니다.

3단계: 반쯤 설치된 패키지들의 설정을 마무리합니다.

sudo dpkg --configure -a

dpkg는 “풀기(unpack)“와 “설정(configure)” 두 단계로 패키지를 설치합니다. 이 명령은 풀기까지만 되고 설정이 안 된 모든 패키지를 찾아 설정 단계를 마저 실행합니다. 출력이 길게 나오고 시간이 걸릴 수 있으니 중간에 끊지 마세요.

4단계: 깨진 의존성을 정리합니다.

sudo apt-get install -f
# 같은 의미: sudo apt --fix-broken install

중단된 작업 때문에 의존성이 맞지 않는 패키지가 생겼다면, 필요한 패키지를 추가로 설치하거나 문제 패키지를 제거하자고 제안합니다. 무엇을 제거하려는지 목록을 꼭 읽어 보세요. 드물게 중요한 패키지를 제거하자고 할 때가 있는데, 그럴 때는 n으로 멈추고 원인을 먼저 확인하는 편이 안전합니다.

5단계: 원래 하려던 작업을 다시 실행합니다.

sudo apt update
sudo apt upgrade

여기까지 오류 없이 끝나면 복구된 것입니다. 중단 전에 하던 작업이 업그레이드였다면 이 단계에서 마저 진행됩니다.

dpkg —configure -a도 실패할 때

3단계에서 특정 패키지의 설치 후 스크립트가 실패하면 이런 출력이 나옵니다.

dpkg: error processing package some-package (--configure):
 installed some-package package post-installation script subprocess returned error exit status 1
Errors were encountered while processing:
 some-package

이것은 dpkg 데이터베이스 문제가 아니라 그 패키지의 설정 스크립트가 실패한 것입니다. 바로 위쪽 출력에 실제 원인이 찍혀 있는 경우가 많습니다. 서비스가 시작되지 않았다면 journalctl -xeu 서비스이름으로, DKMS 모듈 빌드 실패라면 /var/lib/dkms/ 아래 로그로 원인을 찾습니다. NVIDIA 드라이버처럼 커널 모듈을 빌드하는 패키지에서 자주 일어나는데, 그 경우는 우분투 24.04 NVIDIA 드라이버 설치와 오류 해결을 참고하세요.

dpkg 자체가 파일을 읽지 못하는 오류도 있습니다.

dpkg: error: parsing file '/var/lib/dpkg/updates/0003' near line 0:
 newline in field name '#padding'

dpkg가 데이터베이스 변경분을 updates/ 디렉터리에 기록하던 도중 끊겨 그 파일이 깨진 경우입니다. 이 단계부터는 dpkg 데이터베이스를 직접 다루게 되니, 무엇을 고치기 전에 먼저 통째로 백업합니다.

sudo cp -a /var/lib/dpkg /var/lib/dpkg.backup-$(date +%F)

우분투는 매일 dpkg 상태 파일을 /var/backups/dpkg.status.0(가장 최근), .1.gz, .2.gz 식으로 백업해 둡니다. status 파일 자체가 깨졌다면 이 백업이 마지막 수단이 될 수 있습니다. 다만 백업 시점 이후 설치한 패키지 정보는 빠지므로, 복원 후에는 그 사이 설치한 패키지를 다시 설치해 기록을 맞춰야 합니다. 이 단계는 시스템마다 상황이 달라서 정해진 한 줄짜리 해결책이 없습니다. 운영 서버라면 스냅샷이나 백업에서 복원하는 쪽이 더 빠르고 확실할 수 있습니다.

잠금을 해결한 뒤 이어서 만나는 설치 오류

잠금 문제를 풀고 apt install을 다시 했는데 다른 오류가 나는 경우도 있습니다. 잠금과는 무관하지만 같은 흐름에서 자주 만나므로 짧게 정리합니다.

E: Unable to locate package some-tool

apt가 그 이름의 패키지를 목록에서 찾지 못한 것입니다. 갓 만든 서버나 Docker 컨테이너는 패키지 목록이 비어 있는 경우가 많으니 sudo apt update를 먼저 실행합니다. 그래도 안 되면 이름 오타를 확인하고(apt search 사용), 해당 패키지가 universe 저장소에 있다면 sudo add-apt-repository universe로 저장소를 켭니다. 그 우분투 버전에 아예 없는 패키지일 수도 있습니다.

Package some-tool is not available, but is referred to by another package.
This may mean that the package is missing, has been obsoleted, or
is only available from another source

E: Package 'some-tool' has no installation candidate

이름은 알려져 있지만 설치할 수 있는 버전이 없다는 뜻입니다. 다른 패키지가 그 이름을 언급하고 있을 뿐 현재 활성화된 저장소에 실제 패키지가 없는 경우로, 예전 우분투 버전에서 쓰던 패키지 이름이 새 버전에서 바뀌었거나 빠진 경우가 대표적입니다. apt-cache policy some-tool로 어떤 저장소에서 어떤 버전을 볼 수 있는지 확인하고, 해당 우분투 버전에서의 새 패키지 이름을 찾아야 합니다.

자동 업데이트 시간을 옮겨 충돌을 줄이기

lock 오류가 반복되는 근본 원인이 “내가 작업하는 시간과 자동 업데이트 시간이 겹친다”라면, 자동 업데이트를 끄기보다 실행 시각을 옮기는 것이 보안과 편의를 둘 다 챙기는 방법입니다.

sudo systemctl edit apt-daily-upgrade.timer

편집기가 열리면 주석 사이의 빈 공간에 다음을 적고 저장합니다.

[Timer]
OnCalendar=
OnCalendar=*-*-* 03:00
RandomizedDelaySec=30m

첫 줄의 빈 OnCalendar=는 원래 설정된 시각을 지우는 역할입니다. 이 줄이 없으면 기존 시각에 새 시각이 추가되어 하루 두 번 실행됩니다. 저장 후 systemctl list-timers 'apt-daily*'로 다음 실행 시각이 바뀌었는지 확인합니다. 단, Persistent=true 설정 때문에 서버가 꺼져 있다가 켜지면 놓친 실행은 여전히 부팅 직후에 돌아갑니다. 부팅 직후 스크립트에서는 앞에서 본 DPkg::Lock::Timeout 옵션을 함께 쓰는 것이 좋습니다.

자동 업데이트를 완전히 끄고 싶다면 sudo dpkg-reconfigure unattended-upgrades에서 “No”를 선택하면 됩니다. 그러면 /etc/apt/apt.conf.d/20auto-upgrades의 APT::Periodic::Unattended-Upgrade 값이 "0"으로 바뀝니다. 되돌릴 때도 같은 명령에서 “Yes”를 고르면 됩니다. 끈 상태로 둔다면 보안 업데이트를 직접 챙겨야 한다는 점을 기억해 두세요. 새 서버를 받았을 때 함께 해 두면 좋은 기본 설정은 우분투 설치 후 개발자가 처음 할 일에서 이어서 볼 수 있습니다.