/보안/NO_PUBKEY apt Error Recovery Runbook — Registering a signed-by Keyring
SecurityNO_PUBKEYapt-key deprecated

NO_PUBKEY apt Error Recovery Runbook — Registering a signed-by Keyring

Is apt update blocked by NO_PUBKEY or "public key is not available"? This diagnostic table and copy-paste runbook shows how to recover safely with signed-by keyring registration and gpg dearmor—instead of the deprecated apt-key.

NO_PUBKEY apt Error Recovery Runbook — Registering a signed-by Keyring

NO_PUBKEY apt Error 30-Second Recovery Runbook — Registering a signed-by Keyring

A Deployment Pipeline Stalled on One Line of apt update

You've probably seen this red line after adding a Docker, Kubernetes, or PostgreSQL repository on a CI runner or a new server and running apt update.

CODE
W: GPG error: https://download.docker.com/linux/ubuntu jammy InRelease:
   The following signatures couldn't be verified because
   the public key is not available: NO_PUBKEY 7EA0A9C3F273FCD8

The most common misread is "Is this a network or firewall problem?" It isn't. apt successfully downloaded the repository metadata (InRelease) and rejected it because the public key needed to verify the signature is not available locally. In other words, NO_PUBKEY is not a download problem—it's a trust problem. So the fix is not "fetch it again" but "safely register that repository's key."

30-Second Diagnostic Table: Pin Down the Cause from the Error Text

Match the string on your terminal against the left column below and jump straight to the matching path.

Error text patternCauseGo to
NO_PUBKEY 7EA0A9C3F273FCD8 (key ID is visible)The repository signing key is not on the local machine. If you know the official key URL, register it the proper wayPath A
The following signatures couldn't be verified because the public key is not availableSame as above. Key not registeredPath A
You don't know the key URL, only NO_PUBKEY <16-digit key ID>Retrieve the key from a keyserver by key IDPath B
EXPKEYSIG 1234ABCD ... <Signing Key>The registered key has expired. Do not bypass; re-download the new keyPath A (key refresh)
Warning: apt-key is deprecated... (see apt-key(8))Using legacy apt-key/trusted.gpg. Slated for removalMigrate via Path A

Recovery Path A — The Proper Replacement for apt-key: keyrings + signed-by

Instead of the deprecated apt-key add, the current standard is to create a per-repository keyring file and explicitly bind it in .sources with signed-by=.

Docker

Bash
# 1) keyring 전용 디렉터리 생성(권한 0755)
sudo install -m 0755 -d /etc/apt/keyrings

# 2) 공식 GPG 키를 내려받아 바이너리 keyring으로 변환(dearmor)
curl -fsSL https://download.docker.com/linux/ubuntu/gpg \
  | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# 3) 이 키로만 검증하도록 저장소 정의(signed-by 명시)
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \
https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo $VERSION_CODENAME) stable" \
  | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt update   # NO_PUBKEY가 사라졌는지 확인

Kubernetes (pkgs.k8s.io — migrated from the old apt.kubernetes.io)

Bash
# 원하는 마이너 버전(v1.30)에 맞는 Release 키를 dearmor
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.30/deb/Release.key \
  | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg

echo "deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] \
https://pkgs.k8s.io/core:/stable:/v1.30/deb/ /" \
  | sudo tee /etc/apt/sources.list.d/kubernetes.list > /dev/null

In 2023, apt.kubernetes.io was retired and migrated to pkgs.k8s.io, with keys split by version. That's when a lot of servers still pointing at the old URL started hitting NO_PUBKEY.

PostgreSQL (apt.postgresql.org)

Bash
# PGDG 서명 키 등록
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
  | sudo gpg --dearmor -o /etc/apt/keyrings/postgresql.gpg

echo "deb [signed-by=/etc/apt/keyrings/postgresql.gpg] \
https://apt.postgresql.org/pub/repos/apt $(. /etc/os-release && echo $VERSION_CODENAME)-pgdg main" \
  | sudo tee /etc/apt/sources.list.d/pgdg.list > /dev/null

If you prefer the deb822 format (.sources), you can write it like this instead of a .list file.

INI
# /etc/apt/sources.list.d/docker.sources
Types: deb
URIs: https://download.docker.com/linux/ubuntu
Suites: jammy
Components: stable
Signed-By: /etc/apt/keyrings/docker.gpg

Recovery Path B — When You Don't Know the Key URL: Retrieve by key ID from a Keyserver

If all you have is the key ID from the error (NO_PUBKEY 7EA0A9C3F273FCD8) and you couldn't find the official key URL, fetch it from a keyserver. This path has man-in-the-middle risk, so you must verify the fingerprint.

Bash
# 1) 임시 keyring에 키ID로 키 회수(전역 keyring 오염 방지)
gpg --no-default-keyring --keyring /tmp/tmp.gpg \
  --keyserver keyserver.ubuntu.com \
  --recv-keys 7EA0A9C3F273FCD8

# 2) 지문 확인 — 공식 문서에 공개된 지문과 눈으로 대조(★필수)
gpg --no-default-keyring --keyring /tmp/tmp.gpg --fingerprint 7EA0A9C3F273FCD8

# 3) 지문이 일치하면 export → dearmor로 저장소 keyring 생성
gpg --no-default-keyring --keyring /tmp/tmp.gpg \
  --export 7EA0A9C3F273FCD8 \
  | sudo tee /etc/apt/keyrings/<repo>.gpg > /dev/null

# 4) 임시 keyring 정리
rm /tmp/tmp.gpg

From there, bind signed-by=/etc/apt/keyrings/<repo>.gpg in .sources/.list the same way as Path A.

⚠️ Security Notes

  • Why was apt-key add deprecated? The legacy approach dumps every key into a single global keyring (trusted.gpg). That means a key you added for Docker can also verify signatures for the PostgreSQL repository. Compromise one key and the trust boundary expands to the entire system. Per-repository keyrings + signed-by= isolate that trust boundary to a single repository.
  • Risk of the keyserver path: Anyone can upload keys to a keyserver, so a spoofed key with the same key ID can appear. Always compare the full fingerprint from gpg --fingerprint against the official documentation.
  • EXPKEYSIG is not something to bypass: This is a key-expiration signal. Don't ignore it or turn off verification—re-download the new key.

What Not to Do

  • [trusted=yes] — Turns off signature verification entirely, so tampered packages install without complaint.
  • apt-get --allow-unauthenticated — Forces past authentication failures. This opens the door to supply-chain attacks.
  • Dumping keys indiscriminately into /etc/apt/trusted.gpg.d — That regresses to global trust. Keep keyrings in /etc/apt/keyrings and bind them only with signed-by.

One Line from the Field

When you manage a fleet of servers, leftover apt-key add in an Ansible playbook will freeze every deployment the moment you upgrade to Ubuntu 24.04. I standardized keyring registration in the role itself, stored key fingerprints as variables, and asserted them against gpg --fingerprint output—so EXPKEYSIG refreshes became predictable too.

Recurrence-Prevention Checklist

  • Ban global apt-key entirely → /etc/apt/keyrings + signed-by=
  • Keep a separate keyring file per repository (isolation principle)
  • Always compare against the official fingerprint with gpg --fingerprint when registering a key
  • Monitor key expiration to stay ahead of EXPKEYSIG → re-download before expiry
  • Standardize new servers on deb822 .sources instead of .list

FAQ

Q. It still works if I register with apt-key add. Do I really have to switch? A. Ubuntu 22.04 already prints a deprecated warning, and the 24.04 trajectory is toward removing apt-key. Global keyrings also expand the trust boundary, so migrating to signed-by now is the safer move.

Q. Why is gpg --dearmor necessary? A. .asc/.gpg keys are usually ASCII-armored (text), but the keyring that signed-by references must be binary. --dearmor converts the text key into a binary keyring.

Q. EXPKEYSIG appeared. Can I just ignore it and install anyway? A. No. Bypassing an expired key means you can no longer detect supply-chain tampering. The right answer is to re-download the repository's latest key and refresh the keyring.

확인 정보
✦ ✦ ✦
편집 검토 · Editorial Review

Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.

편집 책임 · Nodelog 기술 편집팀·발행 · ·업데이트 ·
관련 공식 문서OWASP 공식 문서

Comments

Be the first to comment.