Docker Container Image Security: 13 Best Practices

 

Transcript:

In this video, we will discuss the best practices for securing Docker container images. How can we reduce the risk of attacks on our Docker container images? The short answer is by reducing the attack surface and time spent on detecting the malicious activity. Yeah, but how to do that? Watch and find out.

Keep your container images lean and boring. The smaller the attack surface in your production image, the better. Therefore, try to minimize the number of components that represent a potentially exploitable path. Depending on your application, consider using a minimalistic Linux distro, a distloress image, or even a scratch. Use multi-stage to transfer the app into a final base image. The final image should contain the immutable set of components required for your application to run. No package manager if possible. A package manager in production kills immutability and widens the attack surface. Attackers can use the package manager to install malicious packages with the vulnerabilities. Therefore, if your use case allows to do so, try to use slimbased images without a package manager. Non route and minimum privileges. Running the container as root or giving it a full set of privileges will give the intruders a host level access to container resources and the ability to exercise kernel attacks. So set the unprivileged user and group in the docker file to run the container. Also, if possible, try to avoid using the privileged flag in production because it gives all Linux kernel capabilities to the container. Instead, limit the granted privileges only to those needed by the container. You can first run cap drop all just to be sure and then add the required privileges with the cap at.

Strive towards immutability when possible. Immutability means that the container must correspond to the container image. It means no config tweaks, uh no changes in the privileges, no patches or fixes in the production. If something changes, you rebuild and redeploy the artifact. The key approach to more secure containers in this regard is to use base images with only the essential set of components required by your application and it should be immutable. Also, there shouldn't be any package manager and uh no privilege escalation. Also, you can use the runtime settings like uh tmpfs or mount to store the data outside the container. You can prevent the privilege escalation at runtime by using the security opt no new privileges option. You can also use the readonly flag to run the container with the readonly file system if it fits your use case. Aim for deterministic container images. Deterministic means that given the same input, the build produces the exact same bites. This way you can detect tempering or dependency change between the build runs. Pin everything to achieve determinism tool chain build system OS version. Also pin the base image by digest not by tag because tags can drift. But the digest always points to the image that you used in the first place.

Use SBOMs. A software bill of materials or an SBOM gives you a list of what's inside your image. Direct and indirect dependencies, their versions, licenses, and so on. SBOMS accelerate CV remediation because you can match a new CVE to the affected image. Generate an SBOMB with every image and store it alongside the image digest. There are several open-source tools for generating sbombs. For instance, sift or cyclonx generator. You can generate an SBOM at the pre-built stage to check the dependency state. But generating an sbomb at the build stage is a must because this way you get the list of exactly what you ship and run.

Integrate provenence. Provenence proves where this image came from by providing the metadata about where, when, and how this image was built. It is metadata that software consumers can verify independently of uh software producers. The important part of provenence is software attestation. You can use cosign for that. So you sign the artifact and the software consumer then verifies the signature and the required station in their CI/CD. Other provenence processes and tools are described in the supply chain levels for software artifacts framework or salsa for short. Drop me a comment if you want a separate video on that. Kow do-it-yourself base images. Quite a few teams build their images on some random base which can be poorly maintained, infected with CVS, lacking SBOMs, and just inviting malicious actors. So opt for a well-maintained regularly updated base image from a trusted vendor. The images should come with an SBOMB and should be easily verified by the check samp for instance instead of uh Alpine Linux with only the community support. You can use alpakita Linux with LTS releases and SLA for patches from bellsoft. Use LTS versions if possible. Consider using base images with LTS software versions. LTS releases are supported for several years. Plus, they have clearly defined security updates policies. For instance, NOJS LTS versions are released every year and receive security patches and critical fixes for 2 and 1/2 years. Java LTS versions are released every 2 years. And the support period depends on the JDK vendor. It varies from 3 years to more than seven years. As for the operating systems, Ubuntu LTS versions are released every two years and receive security patches for 5 years. Alpaquita LTS versions come out every 2 years and receive security patches for 4 years. Update base image regularly. If you don't update the base image, you accumulate nonvulnerabilities and extend the attack surface. Reliable vendors update their images constantly. But when a vendor releases an updated version of their image, your builds won't be updated automatically. So set up automatic uh updates monitoring using tools like dependabot and renovate. This way you can promptly rebuild, rescan and resign the container image.

No secrets in containers. Secrets baked into the image are a serious security risk. Use a dedicated secrets management tools like hash corp worlds, Google secrets manager or kubernetes secrets. You can store your secrets there and then patch them at runtime. Use security scanners. Security scanners are tools that analyze the software environments for security flaws. Scanners vary in purpose. They can scan dependencies, configs, sbombs, container images. They can search for CVS, secrecy, and configs. They can scan the source code for security weaknesses or even monitor the malicious activity. So you can use them at different stages of software development life cycle. You should also define policies for acting upon the scanning results. For instance, you can block or allow the build or deploy, create an additional artifact, perform a manual review and so on. Implement host hardening. Containers share the host kernel. So if the host is vulnerable, everything on it is vulnerable. You can use a dedicated OS for Kubernetes nodes such as Bottle Rock or a minimalistic OS such as Alpakita LTS. The key is that the system must be immutable and without any unnecessary components. Update the kernel on a regular basis plus enable Linux security modules to set the required security policies. Okay, I know this whole info may seem overwhelming. So, if you want to learn more about these approaches, I have written a dedicated article with a more detailed description of each approach and code samples. The link is in the description.

 

Summary

This video shows how to build a complete UI for a Spring Boot application using Vaadin, a Java web framework that provides rich, customizable UI components entirely in Java. The guide walks through creating a login page, a main layout with navigation, and a data grid that supports filtering, lazy loading, and detailed dialogs. It also demonstrates how to build a multi-tab dialog with read-only details and an admin-only edit form using Vaadin Binder for validation and data binding. The video explains how to integrate Spring Security, handle database data from MongoDB, and build responsive layouts. Finally, it shows how to apply a custom cyberpunk theme and encourages exploring Vaadin’s broader capabilities.

About Catherine

Java developer passionate about Spring Boot. Writer. Developer Advocate at BellSoft

Social Media

Videos
card image
Apr 2, 2026
Java Memory Options You Need in Production

JVM memory tuning can be tricky. Teams increase -Xmx and assume the problem is solved. Then the app still hits OOM. Because maximum heap size is not the only thing that affects memory footprint. The JVM uses RAM for much more than heap: metaspace, thread stacks, JIT/code cache, direct buffers, and native allocations. That’s why your process can run out of memory while heap still looks “fine”. In this video, we break down how JVM memory actually works and how to control it with a minimal, production-safe set of flags. We cover heap sizing (-Xms, -Xmx), dynamic resizing, direct memory (-XX:MaxDirectMemorySize), and total RAM limits (-XX:MaxRAMPercentage) — especially in containerized environments like Docker and Kubernetes. We also explain GC choices such as G1, ZGC, and Shenandoah, when defaults are enough, and why GC logging (-Xlog:gc*) is mandatory before tuning. Finally, we show how to diagnose failures with heap dumps and OOM hooks. This is not about adding more flags. It’s about understanding what actually consumes memory — and making decisions you can justify in production.

Videos
card image
Mar 26, 2026
Java Developer Roadmap 2026: From Basics to Production

Most Java roadmaps teach tools. This one teaches order — the only thing that actually gets you to production. You don’t need to learn everything. You need to learn the right things, in the right sequence. In this video, we break down a practical Java developer roadmap for 2026 — from syntax and OOP to Spring, databases, testing, and deployment. Structured into 8 levels, it shows how real engineers grow from fundamentals to production-ready systems. We cover what to learn and what to ignore: core Java, collections, streams, build tools, Git, SQL and JDBC before Hibernate, the Spring ecosystem, testing with JUnit, and deployment with Docker and CI/CD. You’ll also understand why most developers get stuck — jumping into frameworks too early, skipping SQL, or treating tools as knowledge. This roadmap gives you a clear path into real-world Java development — with priorities, trade-offs, and production context.

Further watching

Videos
card image
Apr 30, 2026
Java Flight Recorder Tutorial: How to Profile Java Applications

High CPU, GC spikes, or slow startup are common production issues, but logs and metrics don’t always reveal what the JVM is actually doing. Java Flight Recorder (JFR) provides a precise, low-overhead view of JVM behavior, safe for use even in production environments. In this video, you’ll learn how to use JFR to identify real bottlenecks such as CPU hotspots, memory allocation pressure, thread contention, and I/O stalls. We walk through the full workflow, including starting recordings with JVM flags, controlling them via jcmd, running JFR inside Docker containers, and attaching to live systems using ephemeral containers. Then we analyze a real Spring Boot recording in JDK Mission Control, breaking down GC behavior, allocation patterns, thread states, and method-level hotspots. If you want to move from symptoms to root cause with more confidence, this approach will help. Full article with commands and examples: [https://bell-sw.com/blog/how-to-profile-java-applications-with-jfr-beginner-s-guide/](https://bell-sw.com/blog/how-to-profile-java-applications-with-jfr-beginner-s-guide/)

Videos
card image
Apr 22, 2026
Dynamic SQL Queries with Spring Data JPA in 6 Minutes

If your repository layer has multiple queries for different filter combinations, your data access logic is already getting harder to maintain. In this video, we implement dynamic SQL queries in Spring Data JPA using Specifications — a composable approach that helps avoid query duplication and keeps your filtering logic clean. We build a flexible filtering system with optional parameters (category, language, format, price) and show how Specification.unrestricted() skips empty filters, while Specification.allOf(...) combines them into a single query. We also address a common issue: string-based field access. It’s fragile and can break at runtime when your model changes. Using the JPA Static Metamodel, we move to compile-time safety. The result is a cleaner, more maintainable way to implement dynamic filtering in Spring-based applications.

Videos
card image
Apr 8, 2026
Best Oracle Java Alternatives in 2026 Comparison of OpenJDK Distributions

A comparison of major OpenJDK distributions (Temurin, Liberica, Zulu, Corretto, Semeru, etc.), covering who maintains them, how updates are delivered, and what lifecycle guarantees they provide. We also explain why upstream OpenJDK isn’t production-ready and how your vendor choice impacts real-world systems. Useful for Spring Boot, containers, and Kubernetes to avoid hidden risks and choose the right runtime.