.NET's Ubuntu Chiseled images are a type of distroless container image that contain only the minimal set of packages .NET needs, with everything else removed. These images offer dramatically smaller deployment sizes and attack surface compared to our "full" Ubuntu images that are based on the Ubuntu base images. This is achieved through the following features:
- Minimal set of packages required to run a .NET application
- Non-root user by default
- No package manager
- No shell
8.0-noble-chiseled
docker pull mcr.microsoft.com/dotnet/aspnet:8.0-noble-chiseled
docker pull mcr.microsoft.com/dotnet/runtime:8.0-noble-chiseled
docker pull mcr.microsoft.com/dotnet/runtime-deps:8.0-noble-chiseled
We’re not offering a chiseled SDK image as there wasn't a strong need for one, and a chiseled SDK image could be hard to use for some scenarios. You can continue to use the existing full Ubuntu SDK images to build your apps to run on Chiseled. If you have a compelling use case for a distroless SDK image, please leave a comment on this issue and we’ll be happy to reconsider.
Please see our sample Dockerfiles for examples on how to use Ubuntu Chiseled .NET images:
If your app's Dockerfile doesn't depend on any shell scripts for setup, Ubuntu Chiseled images could be a drop-in replacement for our full Ubuntu or Debian images.
Important
Installing additional packages requires the presence of the Chisel manifest, which is currently only available in .NET 10+ images.
Chisel is built on the idea of package slices. Slices are basically subsets of packages, with their own content and set of dependencies to other internal and external slices. Chisel relies on a database of slices that are indexed per Ubuntu release. You can only install packages that are available as slices.
First, acquire chisel
and chisel-wrapper
. chisel-wrapper
(from rocks-toolbox) is used to generate a dpkg status file documenting which packages are installed, which is used by many vulnerability scanning tools.
FROM mcr.microsoft.com/dotnet/nightly/sdk:10.0-preview-noble AS chisel # Docker build arg documentation: https://docs.docker.com/build/building/variables/ARG BASE_IMAGE="mcr.microsoft.com/dotnet/nightly/runtime-deps:10.0-preview-noble-chiseled"# Find the latest chisel releases: https://github.com/canonical/chisel/releasesARG CHISEL_VERSION="v1.X.X"# Find the latest chisel-wrapper releases: https://github.com/canonical/rocks-toolbox/releasesARG CHISEL_WRAPPER_VERSION="v1.X.X"# Install chisel's dependenciesRUN apt-get update \ && apt-get install -y --no-install-recommends \ file \ && rm -rf /var/lib/apt/lists/* # Install chisel and chisel-wrapperRUN chisel_url=https://github.com/canonical/chisel/releases/download/v${CHISEL_VERSION}/chisel_v${CHISEL_VERSION}_linux_amd64.tar.gz \ && curl -fSLOJ ${chisel_url} \ && curl -fSL ${chisel_url}.sha384 | sha384sum -c - \ && tar -xzf chisel_v${CHISEL_VERSION}_linux_amd64.tar.gz -C /usr/bin/ chisel \ && curl -fSL --output /usr/bin/chisel-wrapper https://raw.githubusercontent.com/canonical/rocks-toolbox/${CHISEL_WRAPPER_VERSION}/chisel-wrapper \ && chmod 755 /usr/bin/chisel-wrapper
Then, copy over the filesystem from your desired base image and use chisel
and chisel-wrapper
to install additional slices. See canonical/chisel-releases for available slices.
COPY --from=$BASE_IMAGE / /rootfs/ RUN chisel-wrapper --generate-dpkg-status /rootfs/var/lib/dpkg/status -- \ --release ubuntu-24.04 --root /rootfs/ \ libicu74_libs \ tzdata-legacy_zoneinfo \ tzdata_zoneinfo
Finally, copy the new root filesystem into a scratch base image for your final (runtime) layer.
FROM scratch AS final COPY --link --from=chisel /rootfs / WORKDIR /app COPY --link --from=build /app . USER $APP_UID ENTRYPOINT ["./app"]
Copying the entire filesystem into the final FROM scratch
layer results in a perfectly space- and layer-efficient installation of the additional slices. However, it's important to note that this pattern does not allow any layer sharing with your chosen .NET base image - the first COPY
instruction in the final stage is essentially creating a completely new base image layer. Keep this in mind when creating your Dockerfiles - for example, you may find it beneficial to share layers between multiple .NET images running in the same Kubernetes cluster.