DEV Community

Cover image for Solved: What to do with this?
Darian Vance
Darian Vance

Posted on • Originally published at wp.me

Solved: What to do with this?

🚀 Executive Summary

TL;DR: The ‘apt-key is deprecated’ warning indicates a shift from a globally trusted keyring to a more secure, per-repository key management system. To resolve this, PPA keys should be moved to the modern ‘signed-by’ method, ensuring CI/CD pipelines and server provisioning continue to function on modern Debian and Ubuntu systems.

🎯 Key Takeaways

  • The ‘apt-key is deprecated’ warning signals a security enhancement, moving from a single global keyring (/etc/apt/trusted.gpg) to isolated, per-repository keys.
  • The ‘Right Way’ to fix this involves creating a dedicated /etc/apt/keyrings/ directory for GPG keys and explicitly linking them to repository source files using the [signed-by=…] attribute.
  • While a ‘Quick Fix’ involves piping dearmored keys to /etc/apt/trusted.gpg.d/, it maintains a global trust model and is not recommended for long-term production use.
  • A ‘Nuclear Option’ script can help migrate existing keys from trusted.gpg to individual files in trusted.gpg.d, but still requires manual updates to sources.list.d files with the signed-by attribute.
  • Automating the ‘Right Way’ fix in base images, Ansible roles, or CI runners prevents future build failures and enhances infrastructure security.

Fix the notorious “apt-key is deprecated” warning by moving your PPA keys to the modern, secure “signed-by” method, ensuring your CI/CD pipelines and server provisioning don’t break on modern Debian and Ubuntu systems.

So, Your Build is Failing. Let’s Talk About ‘apt-key’.

I remember it vividly. It was 2 AM, and a P1 incident ticket landed in my queue. A critical deployment pipeline for our main monolith, prod-monolith-alpha, was failing. The error? A cryptic warning message that the on-call junior engineer had never seen before: Warning: apt-key is deprecated. Manage keyring files in trusted.gpg.d instead. The build script, which had worked flawlessly for three years, was now halting on a simple apt-get update. A base image had been updated automatically, and suddenly, our entire deployment process was dead in the water.

If you’re reading this, you’ve probably just hit the same wall. Your Ansible playbook, your Dockerfile, your trusty server setup script—it’s all screaming at you about apt-key. Don’t panic. You’re not alone, and this is a good thing, I promise. It’s the system forcing us to be more secure. Let’s break it down.

First, The “Why”: What’s a Keyring and Why Did It Break?

Think of the old apt-key system like a single master key for an entire apartment building. When you used apt-key add some_repo.gpg, you were adding a key to a global, trusted keyring (/etc/apt/trusted.gpg). This meant that the key for the Node.js repository could, in theory, be used to validate packages from the PostgreSQL repository. If any one of those keys were compromised, an attacker could potentially sign malicious packages and get them trusted as if they were official.

The new method is about isolation. Instead of one global keyring, every software repository you add gets its own key, stored in a dedicated place (/etc/apt/keyrings/). Then, in your repository configuration file, you explicitly tell APT: “For this specific repository, you must use this specific key to verify its packages.” It’s like giving each apartment its own unique key instead of a master key. It’s more work, but it’s a hell of a lot more secure.

The Solutions: From “Get Me Out of Here” to “Built to Last”

I’ve seen this problem tackled a few ways in the wild. Here are three approaches, from the quick-and-dirty to the architecturally sound.

Solution 1: The Quick & Dirty Fix (The Band-Aid)

Let’s say your build is on fire and you just need it working right now. This method is the direct descendant of the old way, but it puts the key in the “correct” directory without fully embracing the new system. It still uses a global trust model, but it silences the deprecation warning in many cases.

You probably have a line in your script that looks like this:

# The OLD, broken way
wget -qO - https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
Enter fullscreen mode Exit fullscreen mode

The quick fix is to dearmor the key and dump it into the trusted.gpg.d directory. This directory is for dropping in key files that APT will trust globally.

# The QUICK FIX: Pipe the key to the trusted.gpg.d directory
wget -qO - https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/docker.gpg
Enter fullscreen mode Exit fullscreen mode

Darian’s Take: Look, I get it. Sometimes you just need to stop the bleeding. This will often work and get your pipeline green again. But you haven’t actually fixed the underlying security issue. You’ve just moved the key. Don’t leave this in a production script long-term. You’re just kicking the can down the road.

Solution 2: The “Right Way” Fix (The Permanent Solution)

This is the method you should be using. It fully embraces the new per-repository key model. It’s a two-step process: place the key in a dedicated, isolated keyring directory, and then tell the repository source list where to find it.

Step 1: Create the keyrings directory and download the key there.

We’ll create a dedicated directory for our keys. This is best practice to keep things organized and secure.

# Create the directory if it doesn't exist
sudo install -m 0755 -d /etc/apt/keyrings

# Download the key, dearmor it, and save it to the new directory
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg

# Set proper permissions for the key file
sudo chmod a+r /etc/apt/keyrings/docker.gpg
Enter fullscreen mode Exit fullscreen mode

Step 2: Modify your repository source file to use the new key.

Now, we create or edit the repository’s .list file in /etc/apt/sources.list.d/. The magic is the [signed-by=...] part. This explicitly tells APT which key to use for this repo.

# Add the repository to your sources list
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

# Now, update your package list
sudo apt-get update
Enter fullscreen mode Exit fullscreen mode

No warnings. Clean, secure, and future-proof. This is how we do it on all our new server images and CI runners like ci-runner-bionic-04.

Solution 3: The “Nuclear” Option (For Legacy Systems)

What if you’ve inherited a server like legacy-prod-db-01 with a dozen old keys in the main trusted.gpg keyring? Migrating them one by one is a pain. This is an advanced approach for cleaning up a messy system. It’s a script to extract all the keys from the old keyring and move them to the new format.

WARNING: This is a powerful script. It directly manipulates your system’s trust stores. Test it on a non-critical machine first. I am not responsible if you break your package manager. You have been warned.

#!/bin/bash
# Script to migrate keys from legacy apt-key to the new format

set -e

echo "Starting migration of keys from trusted.gpg..."

# Export all keys from the main keyring to individual files
apt-key exportall | gpg --dearmor > /tmp/temp-trusted-export.gpg

# Split the exported file into individual keys
gpg --enarmor < /tmp/temp-trusted-export.gpg | csplit -z -f /tmp/key- -b "%02d.asc" - "/-----BEGIN PGP PUBLIC KEY BLOCK-----/" "{*}"

# Process each individual key file
for keyfile in /tmp/key-*.asc; do
  if [ -s "$keyfile" ]; then
    echo "Processing ${keyfile}..."
    # Get the fingerprint to use as the filename
    fingerprint=$(gpg --show-keys --with-colons "${keyfile}" | grep '^fpr' | cut -d: -f10)
    if [ -n "$fingerprint" ]; then
      # Dearmor the key and place it in the trusted.gpg.d directory
      echo "Moving key with fingerprint ${fingerprint}"
      gpg --dearmor < "${keyfile}" > "/etc/apt/trusted.gpg.d/${fingerprint}.gpg"
    else
      echo "Could not get fingerprint for ${keyfile}"
    fi
  fi
done

echo "Cleaning up temporary files..."
rm /tmp/temp-trusted-export.gpg /tmp/key-*.asc

echo "Migration complete. You should now audit your source.list files to use [signed-by]."
echo "This script does NOT automatically update your source lists."
Enter fullscreen mode Exit fullscreen mode

This script gets you halfway there—it moves the keys. You still need to go into your /etc/apt/sources.list.d/ files and add the [signed-by=...] attribute for each repository, pointing to the new key files. It’s a cleanup tool, not a magic wand.

My Final Two Cents

It’s tempting to use the quick fix and move on. We’re all under pressure. But taking the extra ten minutes to implement Solution #2, the “Right Way,” will save your future self (or the next engineer) a massive headache. It’s a small change that makes your infrastructure more robust and secure.

Automate it, put it in your base images, bake it into your Ansible roles. Stop using apt-key add today. Your future self will thank you when you’re sleeping soundly instead of troubleshooting a broken build at 2 AM.


Darian Vance

👉 Read the original article on TechResolve.blog


☕ Support my work

If this article helped you, you can buy me a coffee:

👉 https://buymeacoffee.com/darianvance

Top comments (0)