Learn Docker With My Newest Course

Dive into Docker takes you from "What is Docker?" to confidently applying Docker to your own projects. It's packed with best practices and examples. Start Learning Docker →

Reduce Docker Images by 20 MB by Verifying APT Packages without gnupg

reduce-docker-images-by-20-mb-by-verifying-apt-packages-without-gnupg.jpg

When working with package sources you can pass the plain text asc key into signed-by instead of a binary gpg key.

Quick Jump:

When configuring the sources list for a package you can use the signed-by option to pass in a GPG key. You’ll want to save the key in /etc/apt/keyrings since that’s what APT suggests since they deprecated apt-key.

Did you know you can pass in the plain text ASCII key directly too? Here’s a snippet from the apt-key man pages:

Make sure to use the “asc” extension for ASCII armored keys and the “gpg” extension for the binary OpenPGP format (also known as “GPG key public ring”).

# How Does This Work?

Let’s go over a real example. I’ll use installing Node because I recently encountered this in my example Docker web apps since they use esbuild but this isn’t limited to Node. It will work with all signed apt packages.

Node’s official documentation for installing itself on Debian says to do:

sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg
sudo mkdir -p /etc/apt/keyrings
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
  | sudo gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg

It suggests using the binary version of their GPG key. That’s what running gpg --dearmor /etc/apt/keyrings/nodesource.gpg on their key will produce.

Then it says to add that binary key to your sources list:

NODE_MAJOR=20
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_$NODE_MAJOR.x nodistro main" \
  | sudo tee /etc/apt/sources.list.d/nodesource.list

Notice the signed-by option is pointing to the binary .gpg file.

Optimizations

If you curl down curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key this is a plain text / ASCII PGP key.

Based on the man pages we looked at earlier all we need to do is rename this file to be nodesource.asc and it will work because of its file extension. Then we can reference it with signed-by=/etc/apt/keyrings/nodesource.asc.

Since we don’t need to generate a binary GPG key we don’t need to apt install gnupg. We can also avoid referencing ca-certificates since technically curl installs this package.

Technically you don’t need to mkdir /etc/apt/keyrings on modern versions of Debian and Ubuntu since it exists by default but that’s only a quick aside.

# Trying It Out in Docker

I did my tests in the official Debian Bookworm slim Docker image. You can run that with: docker container run -it debian:bookworm-slim

At the time of this blog post it comes with 88 packages that are installed by default. You can check by running apt list --installed and then piping it to | wc -l, you need to subtract 1 because the first line of output of apt list --installed isn’t a package.

Now try running: apt-get update && apt-get install gnupg, notice that it prompts you that it will use 26.1 MB of extra disk space to install it. Even if you use apt-get install --no-install-recommends gnupg it’s still 19.5 MB of files. There’s also 113 installed packages.

If you have a ~200 MB image that’s a ~10% savings or even ~5% with a ~400 MB image. Not only that but it’ll speed up your builds since there’s less being installed.

Does It Still Work?

Yes, you can spin up a new container and run apt-get update && apt-get install -y curl and then run this to install Node:

curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key -o /etc/apt/keyrings/nodesource.asc \
  && echo "deb [signed-by=/etc/apt/keyrings/nodesource.asc] https://deb.nodesource.com/node_20.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list \
  && apt-get update && apt-get install -y --no-install-recommends nodejs

You can run node --version to verify it works as well as apt-get update to see that it picks up the nodesource list.

To be doubly safe it really works you can manually modify the nodesource.asc file such as replacing 1 character and then when nodejs tries to be installed you’ll get a GPG error that says the signature couldn’t be verified.

I’ve applied this method to my example Docker Rails app since it installs Node this way. The other example apps use the base Node image for its assets where Node is already installed.

I also use this method to install Docker itself on Debian / Ubuntu within my Ansible role. This optimization saves having to run 1 extra task to create a binary GPG key.

The demo video goes over running some of the commands we came across in this post.

# Demo Video

Timestamps

  • 0:51 – Going over the process of adding a package to your sources list
  • 2:30 – Checking out the man pages for apt-key
  • 2:54 – Running a Debian Bookworm slim Docker container which has 88 packages
  • 3:47 – There’s 113 packages after installing gnupg, it uses +26 MB of space
  • 5:27 – That could be a 5-10% reduction in image size
  • 5:38 – Making sure everything works, including GPG key verification
  • 8:06 – I made this change to my example Rails app and Ansible role for Docker

References

Are you using this method of verifying signed keys? Let me know below.

Never Miss a Tip, Trick or Tutorial

Like you, I'm super protective of my inbox, so don't worry about getting spammed. You can expect a few emails per year (at most), and you can 1-click unsubscribe at any time. See what else you'll get too.



Comments