Build your container/vm image elsewhere and deploy updates as entirely new images or snapshots or whatever you want.
Personally I prefer buildroot and consider VM as another target for embedded o/s images.
I'm handling it by using a slim debian or ubuntu, then using apt to install these packages with necessary dependencies.
For everything easy, like one basic binary, I use the most minimal image but as soon as it gets just a little bit annoying to set it up and keep it maintained, i start using apt and a nightly build of the image.
microcontainer=$(buildah from registry.access.redhat.com/ubi8/ubi-micro)
micromount=$(buildah mount $microcontainer)
yum install \
--installroot $micromount \
--releasever 8 \
--setopt install_weak_deps=false \
--nodocs -y \
httpd
(from https://www.redhat.com/en/blog/introduction-ubi-micro published in 2021)Even without explicit support in the pacakage manager, you could also roll your own solution by running the package manager in a chroot environment, which would then need to be seeded with the package manager's own dependencies, of course (and use user-mode qemu to run pre- and post-installation scripts within the chroot in the case of cross-architecture builds).
Whether this yields a minimal container when pointed at a repository intended to be used to deploy a full OS is another question, but using a package manager to build a root filesystem offline isn't hard to pull off.
As for how to do this in the context of building an OCI container, tools like Buildah[1] exist to support container workflows beyond the conventional Dockerfile approach, providing straightforward command line tools to create containers, work with layers, mount and unmount container filesystems, etc.
[1] https://github.com/containers/buildah/blob/main/README.md
I may be a little out of touch here, because the last time I did this, we used a wholly custom package manager.
Most Makefiles allow you to specify an alternate DESTDIR on install.
For example i run a gcs fuse driver, it has other dependencies apt 'just' resolves.
It doesn't matter much were i pull them from though, i only do this with packages which have plenty of dependencies and i don't want to assemble my own minimal image.
I run systemd, sshd and xpra (remote X11) inside my arch container.
- linux base: kernel + libc + some low tooling
- jvm base: adding the JRE/JDK you need
- your application version
FROM ubuntu:24.04
COPY --from=ghcr.io/owner/image:latest /usr/local/bin/somebinary /usr/local/bin/somebinary
CMD ["somebinary"]
Not as simple when you need shared dependencies
So is running `docker build` and the `RUN apt update` line doing a cache hit, except the latter is silent.
The problem solved by pinning to the snapshot is not to magically be secure, it's knowing what a given image is made of so you can trivially assert which ones are safe and which ones aren't.
In both cases you have to rebuild an image anyway so updating the snapshot is just a step that makes it explicit in code instead of implicit.
If you've pulled in a dependency from outside the base image, there will be no new base image version to alert you to an update of that external dependency. Unless your container regularly runs something like apt update && apt list --upgradable, you will be unaware of security fixes newly available from apt.
Also I'm tired of doing these hacks:
# increase to bust cache entry
RUN true 42 && apt update
Pinning to a snapshot just makes so many things easier.I don’t mind being somewhat behind, but it seems like there are a lot of packages that don’t get regular updates. It’s okay to have packages that aren’t updated, but those packages should be clearly distinguishable.
software component image
both should be version pinned for auditing
Reproducible can sometimes be a goal, but repeatable is always important.
I do think for this case specifically (base images for a specific distro), they should be reproducible.
It is a managed service that keeps a cached copy of your dependencies at a specific time. You can pin your dependencies within a Dockerfile and have reproducible docker images.
NIX FIXES THIS.