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.
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 7EA0A9C3F273FCD8The 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 pattern | Cause | Go 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 way | Path A |
The following signatures couldn't be verified because the public key is not available | Same as above. Key not registered | Path A |
You don't know the key URL, only NO_PUBKEY <16-digit key ID> | Retrieve the key from a keyserver by key ID | Path B |
EXPKEYSIG 1234ABCD ... <Signing Key> | The registered key has expired. Do not bypass; re-download the new key | Path A (key refresh) |
Warning: apt-key is deprecated... (see apt-key(8)) | Using legacy apt-key/trusted.gpg. Slated for removal | Migrate 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
# 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)
# 원하는 마이너 버전(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/nullIn 2023,
apt.kubernetes.iowas retired and migrated topkgs.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)
# 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/nullIf you prefer the deb822 format (.sources), you can write it like this instead of a .list file.
# /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.gpgRecovery 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.
# 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.gpgFrom there, bind signed-by=/etc/apt/keyrings/<repo>.gpg in .sources/.list the same way as Path A.
⚠️ Security Notes
- Why was
apt-key adddeprecated? 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 --fingerprintagainst 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/keyringsand bind them only withsigned-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-keyentirely →/etc/apt/keyrings+signed-by= - Keep a separate keyring file per repository (isolation principle)
- Always compare against the official fingerprint with
gpg --fingerprintwhen registering a key - Monitor key expiration to stay ahead of EXPKEYSIG → re-download before expiry
- Standardize new servers on deb822
.sourcesinstead 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.
Nodelog는 모든 콘텐츠의 내용과 출처를 공개 전에 검토합니다. 환경(OS·버전)에 따라 결과가 달라질 수 있는 기술 정보는 공식 문서와 함께 확인하며, 검토 기준과 정정 원칙은 편집 정책에서 안내합니다. 오류를 발견하시면 이메일로 제보해 주세요 — 확인 후 신속히 정정합니다.
Comments
Be the first to comment.