NGINX (“engine X”) is a high-performance web and reverse proxy server created by Igor Sysoev. It can be used both as a standalone web server and as a proxy to reduce the load on back-end HTTP or mail servers.
… but you knew that already
You probably also know that NGINX is one of the most popular container applications out there, with over 8 billion pulls on Docker Hub and starred by more than 19 thousand users. Even the Ubuntu-based NGINX image is one of the most popular container images within our Ubuntu namespace.
Let’s now talk about a few other interesting facts you might have not paid enough attention to yet:
Image | Size (compressed) | Size (decompressed) | Vulnerabilities |
---|---|---|---|
nginx:1.24-alpine-slim | 5.2MB | 11.5MB | |
nginx:1.22 (Debian) | 56MB | 142MB | 45 LOW |
ubuntu/nginx:1.22-23.04_beta | 53MB | 142MB | 3 MEDIUM, 3 LOW |
* The aforementioned values have been collected for amd64
only.
A key takeaway from this table is that the image’s size is correlated with the amount of vulnerabilities it has. And this sounds obvious! I mean, it is only logical that the more stuff you have inside your image the more likely it is for some of that stuff to be vulnerable.
Furthermore, if we need an application container, then we should only containerize the application! We don’t need extra software and utilities we’ll never use at runtime!
Chiselled NGINX
At the risk of sounding like a broken record, Chisel is great! It allows us to breakdown packages from the Ubuntu archives, in order to create minimal filesystems that contain only the necessary bits and pieces for the target packages to be functional.
We can do that with NGINX, and we have!
Can we build our own Chiselled NGINX container image? Sure…
FROM ubuntu:mantic as build-base
ARG TARGETARCH
# NGINX needs the www-data user
ARG UID=33 GID=33
WORKDIR /opt
# Get the Chisel binary into the build stage
ADD https://github.com/canonical/chisel/releases/download/v0.8.0/chisel_v0.8.0_linux_${TARGETARCH}.tar.gz chisel.tar.gz
RUN apt update && apt install -y ca-certificates \
&& tar -xvf chisel.tar.gz -C /usr/bin/ \
&& mkdir rootfs \
&& install -d -m 0755 -o $UID -g $GID rootfs/var/www \
&& chisel cut --release ubuntu-23.10 --root $PWD/rootfs \
base-files_var \
base-passwd_data \
nginx_bins \
nginx-common_index
FROM scratch
COPY --from=build-base /opt/rootfs /
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]
And voilà! Get this into a Dockerfile
and then run:
docker buildx build -t ubuntu/chiselled-nginx:1.24 --load .
If all goes well, your image will be loaded into the local Docker daemon and you can then test it with:
docker run --rm -p 8080:80 ubuntu/chiselled-nginx:1.24
Check localhost:8080
on your browser to ensure it’s working
Rocking it…
Is the aforementioned Dockerfile a mouthful? Fear no more here’s your
rockcraft.yaml
:
name: nginx
base: bare
build-base: ubuntu:22.04
version: "1.24"
summary: A high-performance reverse proxy & web server, based on Ubuntu
description: |
Nginx ("engine X") is a high-performance web and reverse proxy server
created by Igor Sysoev. It can be used both as a standalone web server
and as a proxy to reduce the load on back-end HTTP or mail servers.
license: BSD-2-Clause
platforms:
amd64:
services:
nginx:
override: replace
command: nginx -g 'daemon off;'
startup: enabled
on-failure: shutdown
parts:
nginx:
plugin: nil
build-packages:
- git
build-environment:
- WWW_UID: 33
- WWW_GID: 33
override-build: |
install -d -m 0755 -o $WWW_UID -g $WWW_GID $CRAFT_PART_INSTALL/var/www
chisel cut --release ubuntu-23.10 \
--root $CRAFT_PART_INSTALL \
base-files_var \
base-passwd_data \
nginx_bins \
nginx-common_index
NOTE: Rockcraft only supports LTS Ubuntu bases, so the above file is overriding the build to force the installation of slices from a different Ubuntu release. Once NGINX is sliced for an LTS release, the above
rockcraft.yaml
can be simplified, as you’ll only need to usestage-packages
and list the slices without having to worry about sourcing the Chisel release and installing it manually.
With the above, get your Chiselled NGINX ROCK by running:
rockcraft -v
Wanna make sure it’s working? Convert the ROCK’s OCI archive to the local Docker daemon and run the container as in the previous example:
skopeo copy oci-archive:nginx_1.24_amd64.rock docker-daemon:ubuntu/chiselled-nginx-rock:1.24
docker run --rm -p 8080:80 ubuntu/chiselled-nginx-rock:1.24
You’ll see Pebble initializing the NGINX service, and your webpage will once more be available at localhost:8080
.
So what?
Well, let’s reuse the table from above and compare the existing NGINX images with ours:
Image | Size (compressed) | Size (decompressed) | Vulnerabilities |
---|---|---|---|
nginx:1.24-alpine-slim | 5.2MB | 11.5MB | |
nginx:1.22 (Debian) | 56MB | 142MB | 45 LOW |
ubuntu/nginx:1.22-23.04_beta | 53MB | 142MB | 3 MEDIUM, 3 LOW |
ubuntu/chiselled-nginx:1.24 | 5.4MB | 13.7MB | |
ubuntu/chiselled-nginx-rock:1.24 | 8.8MB | 21MB |
Pretty cool right? We can now create Ubuntu-based container images for NGINX, that are amongst the smallest and most secure, while also providing additional capabilities like Pebble!
Challenge: could NGINX be sliced even further to make for a smaller image? If you think so, please make a proposal in chisel-releases