<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Atharva Unde</title>
    <description>The latest articles on DEV Community by Atharva Unde (@atharvaunde).</description>
    <link>https://dev.to/atharvaunde</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2762428%2F8e921033-436b-4e3a-ae71-db308ce9af74.jpg</url>
      <title>DEV Community: Atharva Unde</title>
      <link>https://dev.to/atharvaunde</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/atharvaunde"/>
    <language>en</language>
    <item>
      <title>Docker: Layers, Caching, Multi-Stage Explained</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 15 Feb 2025 12:55:45 +0000</pubDate>
      <link>https://dev.to/atharvaunde/docker-layers-caching-multi-stage-explained-10b5</link>
      <guid>https://dev.to/atharvaunde/docker-layers-caching-multi-stage-explained-10b5</guid>
      <description>&lt;p&gt;Docker's efficiency is one of its biggest draws. But what makes Docker builds so fast? The secret lies in its layer-based architecture and clever caching mechanism. Let's dive in and see how it all works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dockerfiles: A Layered Cake&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every line in your Dockerfile is an instruction, and Docker treats each of these instructions as a distinct &lt;em&gt;layer&lt;/em&gt;. But what is a layer, exactly?&lt;/p&gt;

&lt;p&gt;Think of it like this: a layer is an intermediate snapshot of your container image during the build process. Each instruction in the Dockerfile creates a new layer, building upon the previous one.&lt;/p&gt;

&lt;p&gt;For instance, consider this common Node.js Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:18&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["node", "server.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple Dockerfile translates into five distinct layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Base Image Layer:&lt;/strong&gt; &lt;code&gt;FROM node:18&lt;/code&gt; (The foundation upon which everything else is built)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Working Directory Layer:&lt;/strong&gt; &lt;code&gt;WORKDIR /app&lt;/code&gt; (Sets the working directory inside the container)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Definition Layer:&lt;/strong&gt; &lt;code&gt;COPY package.json .&lt;/code&gt; (Copies the &lt;code&gt;package.json&lt;/code&gt; file)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Dependency Installation Layer:&lt;/strong&gt; &lt;code&gt;RUN npm install&lt;/code&gt; (Installs the project dependencies)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Application Code Layer:&lt;/strong&gt; &lt;code&gt;COPY . .&lt;/code&gt; (Copies the entire application code)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each of these instructions results in a distinct layer that's stored in the image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker's Caching Superpower&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here's where the magic happens: Docker caches each of these layers during the build process. This means that if a layer hasn't changed, Docker can reuse the cached version instead of rebuilding it from scratch. This dramatically speeds up subsequent builds.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Cache Hit:&lt;/strong&gt; If an instruction and its inputs haven't changed, Docker pulls the existing layer from the cache.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Cache Miss:&lt;/strong&gt; If an instruction or its inputs &lt;em&gt;have&lt;/em&gt; changed, Docker invalidates the cache for that layer &lt;em&gt;and all subsequent layers&lt;/em&gt;. This means it needs to rebuild not only the changed layer but also every layer that comes after it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cache Invalidation: When Things Go Wrong&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The cache invalidation behavior is crucial to understand. Imagine you have a Dockerfile with eight instructions. If instruction #2 changes, Docker invalidates the cache for instruction #2 &lt;em&gt;and all instructions that follow&lt;/em&gt; (3 through 8). They will all need to be rebuilt. This can lead to longer build times if not managed correctly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A Real-World Example (Multi-Stage Build and Labels)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's examine a more complex scenario involving a multi-stage Dockerfile, which is a best practice for creating smaller and more secure images:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.authors="authoremail@example.com"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; "com.example.vendor"="Example LLC"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; version="1.0.0"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; description="This image is used to run hello world backend written in Express Framework"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this Dockerfile, we have two stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;&lt;code&gt;build-env&lt;/code&gt; Stage:&lt;/strong&gt; This stage uses a Node.js Alpine image to install dependencies and prepare the application for production.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Final Stage:&lt;/strong&gt; This stage uses a distroless image (&lt;code&gt;gcr.io/distroless/nodejs20-debian12&lt;/code&gt;), which contains only the necessary runtime dependencies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's how caching works in this multi-stage context:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Independent Caches:&lt;/strong&gt; Each stage has its own separate cache. Changes in one stage don't automatically invalidate the cache of other stages, &lt;em&gt;unless&lt;/em&gt; they affect the &lt;code&gt;COPY --from&lt;/code&gt; instruction (which we'll discuss below).&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;build-env&lt;/code&gt; Stage Changes:&lt;/strong&gt; If you modify &lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;yarn.lock&lt;/code&gt; in the &lt;code&gt;build-env&lt;/code&gt; stage, the &lt;code&gt;RUN yarn install&lt;/code&gt; instruction will be invalidated, and all subsequent instructions in &lt;em&gt;that stage&lt;/em&gt; will need to be rebuilt.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;&lt;code&gt;COPY --from&lt;/code&gt; Interaction:&lt;/strong&gt; The &lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; instruction is crucial. If the &lt;em&gt;contents&lt;/em&gt; of &lt;code&gt;/app&lt;/code&gt; in the &lt;code&gt;build-env&lt;/code&gt; stage change (due to a rebuild triggered by a change in &lt;code&gt;package.json&lt;/code&gt;, for example), the &lt;code&gt;COPY&lt;/code&gt; instruction will also produce a different result in the final stage, invalidating the final stage's cache from that point onward.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Label Invalidation:&lt;/strong&gt; The &lt;code&gt;LABEL&lt;/code&gt; instructions, while important for adding metadata, &lt;em&gt;do not&lt;/em&gt; directly influence the caching mechanism. Changing label values will always cause the layer containing the &lt;code&gt;LABEL&lt;/code&gt; instruction to be rebuilt, but it doesn't impact any previous layers.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Code Changes:&lt;/strong&gt; If you simply modify code in the &lt;code&gt;index.js&lt;/code&gt; file, only the &lt;code&gt;COPY index.js ./&lt;/code&gt; instruction within &lt;code&gt;build-env&lt;/code&gt;, and the subsequent &lt;code&gt;COPY --from&lt;/code&gt; instruction in the final stage will be affected. The dependency installation stage (&lt;code&gt;RUN yarn install&lt;/code&gt;) can still be pulled from the cache, speeding up the build significantly.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Docker Caching and Multi-Stage Builds: Scenario Table&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This table outlines how different changes to your Dockerfile or application code impact the caching mechanism in a multi-stage build.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Changed File/Instruction&lt;/th&gt;
&lt;th&gt;Impact on &lt;code&gt;build-env&lt;/code&gt; Stage Cache&lt;/th&gt;
&lt;th&gt;Impact on Final Stage Cache&lt;/th&gt;
&lt;th&gt;Rebuilt Layers&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dependency Change:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;package.json&lt;/code&gt; or &lt;code&gt;yarn.lock&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;RUN yarn install&lt;/code&gt; and subsequent instructions are invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; and subsequent instructions invalidated.&lt;/td&gt;
&lt;td&gt;All layers from &lt;code&gt;RUN yarn install&lt;/code&gt; in &lt;code&gt;build-env&lt;/code&gt;, and from &lt;code&gt;COPY --from&lt;/code&gt; in the final stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Change Only:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only &lt;code&gt;COPY index.js ./&lt;/code&gt; is invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; and subsequent instructions invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY index.js ./&lt;/code&gt; in &lt;code&gt;build-env&lt;/code&gt;, and from &lt;code&gt;COPY --from&lt;/code&gt; in the final stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dockerfile Change (build-env, Before COPY package.json)`:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(e.g., adding a new &lt;code&gt;ENV&lt;/code&gt; variable before COPY)&lt;/td&gt;
&lt;td&gt;All instructions after and including the changed instruction are invalidated.&lt;/td&gt;
&lt;td&gt;If the content of /app does not change, the final stage stays cached&lt;/td&gt;
&lt;td&gt;All layers from that step to end of &lt;code&gt;build-env&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Dockerfile Change (build-env, After COPY package.json)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(e.g., adding an RUN after copy)&lt;/td&gt;
&lt;td&gt;All instructions after and including the changed instruction are invalidated.&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;COPY --from=build-env /app /app&lt;/code&gt; and subsequent instructions invalidated.&lt;/td&gt;
&lt;td&gt;All layers from changed instruction till end of &lt;code&gt;build-env&lt;/code&gt; and onwards.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Label Value Change:&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;(Change in LABEL instruction in the final stage)&lt;/td&gt;
&lt;td&gt;No impact.&lt;/td&gt;
&lt;td&gt;Only the layer with the modified &lt;code&gt;LABEL&lt;/code&gt; is invalidated.&lt;/td&gt;
&lt;td&gt;Layer containing the &lt;code&gt;LABEL&lt;/code&gt; instruction in the final stage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;No Changes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;N/A&lt;/td&gt;
&lt;td&gt;All layers are pulled from cache.&lt;/td&gt;
&lt;td&gt;All layers are pulled from cache.&lt;/td&gt;
&lt;td&gt;None&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Explanation:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Scenario:&lt;/strong&gt; Describes the type of change made.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Changed File/Instruction:&lt;/strong&gt; Specifies the file or instruction that was modified.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Impact on &lt;code&gt;build-env&lt;/code&gt; Stage Cache:&lt;/strong&gt; Explains which layers in the &lt;code&gt;build-env&lt;/code&gt; stage are invalidated.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Impact on Final Stage Cache:&lt;/strong&gt; Explains which layers in the final stage are invalidated.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Rebuilt Layers:&lt;/strong&gt; Lists the layers that will be rebuilt during the Docker build process.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;The Takeaway: Order and Multi-Stage Considerations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;With multi-stage builds, you need to consider caching within &lt;em&gt;each&lt;/em&gt; stage, as well as how changes in one stage affect subsequent stages through &lt;code&gt;COPY --from&lt;/code&gt; instructions. Strategic placement of instructions and careful management of dependencies are key to maximizing build performance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;In the next section, we will explore best practices to optimize caching and reduce unnecessary rebuilds. Stay tuned!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>jenkins</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Dockerfile Labels: A Comprehensive Guide</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 08 Feb 2025 08:02:10 +0000</pubDate>
      <link>https://dev.to/atharvaunde/dockerfile-labels-a-comprehensive-guide-11fd</link>
      <guid>https://dev.to/atharvaunde/dockerfile-labels-a-comprehensive-guide-11fd</guid>
      <description>&lt;p&gt;In our &lt;a href="https://dev.to/atharvaunde/base-images-the-secret-to-smaller-docker-images-43n3"&gt;previous post&lt;/a&gt;, we explored how to significantly optimize Docker image size. Now, let's dive deeper and enhance our images with valuable metadata using Docker labels.&lt;/p&gt;

&lt;p&gt;Remember this Dockerfile from last time?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If we inspect this image and check its labels using the command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{json .Config.Labels}}'&lt;/span&gt; atharvaunde/dockerexamples:distroless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll see... &lt;code&gt;null&lt;/code&gt;. So, what's the point of Docker labels, and why should we care?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Use Docker Labels?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker labels are essentially metadata tags that you can add to your Docker images. Think of them as key-value pairs that provide extra information about the image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Use Cases for Docker Labels&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Attribution:&lt;/strong&gt; Who built this image? Include the author's name and email.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Organization:&lt;/strong&gt; Which company or team created this image?&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Version Tracking:&lt;/strong&gt; What version of the application is contained within? This is especially useful if you consistently tag images as &lt;code&gt;latest&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Description:&lt;/strong&gt;  Provide a short description of the image's purpose.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Custom Metadata:&lt;/strong&gt; Add any other relevant information, such as dependencies, build dates, or license details.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By embedding this information directly into the image, you provide valuable context to users who consume your images.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Adding Labels to Our Dockerfile&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's enhance our Dockerfile with labels to include the author, company, version, and a description:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; org.opencontainers.image.authors="authoremail@example.com"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; "com.example.vendor"="Example LLC"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; version="1.0.0"&lt;/span&gt;
&lt;span class="k"&gt;LABEL&lt;/span&gt;&lt;span class="s"&gt; description="This image is used to run hello world backend written in Express Framework"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, when we run the same inspection command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker image inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{json .Config.Labels}}'&lt;/span&gt; atharvaunde/dockerexamples:distroless
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll see the labels we added:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"com.example.vendor"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Example LLC"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"This image is used to run hello world backend written in Express Framework"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"org.opencontainers.image.authors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"authoremail@example.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important Placement in Multi-Stage Builds&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When using multi-stage Dockerfiles like ours, it's crucial to place the &lt;code&gt;LABEL&lt;/code&gt; instructions in the &lt;em&gt;final&lt;/em&gt; stage (i.e., the one that creates the actual image you'll be distributing). If we had placed the &lt;code&gt;LABEL&lt;/code&gt; instructions &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;FROM gcr.io/distroless/nodejs20-debian12&lt;/code&gt; line, they would be lost in the final image. The final image is created in a separate stage with separate context, so it wouldn't inherit labels from any previous stage.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Docker Label Inheritance and Overriding&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This table summarizes how labels are handled when a Dockerfile adds a label that either exists or doesn't exist in the base image.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scenario&lt;/th&gt;
&lt;th&gt;Base Image Label&lt;/th&gt;
&lt;th&gt;Dockerfile Label&lt;/th&gt;
&lt;th&gt;Resulting Image Label&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Inheritance:&lt;/strong&gt; Label Exists in Base Image&lt;/td&gt;
&lt;td&gt;Exists&lt;/td&gt;
&lt;td&gt;Absent&lt;/td&gt;
&lt;td&gt;Retained (from base)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Overriding:&lt;/strong&gt; Label Exists in Base Image&lt;/td&gt;
&lt;td&gt;Exists&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;td&gt;Overridden (Dockerfile value)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Addition:&lt;/strong&gt; Label Not in Base Image&lt;/td&gt;
&lt;td&gt;Absent&lt;/td&gt;
&lt;td&gt;Present&lt;/td&gt;
&lt;td&gt;Retained (Dockerfile value)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;In Conclusion:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Docker labels are a simple yet powerful way to add metadata to your Docker images, providing valuable context and improving discoverability. Remember to place your &lt;code&gt;LABEL&lt;/code&gt; instructions in the correct stage of a multi-stage Dockerfile and be aware of how label inheritance works. Happy containerizing!&lt;/p&gt;

</description>
      <category>docker</category>
      <category>devops</category>
      <category>containers</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Base Images: The Secret to Smaller Docker Images</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sun, 02 Feb 2025 12:30:00 +0000</pubDate>
      <link>https://dev.to/atharvaunde/base-images-the-secret-to-smaller-docker-images-43n3</link>
      <guid>https://dev.to/atharvaunde/base-images-the-secret-to-smaller-docker-images-43n3</guid>
      <description>&lt;p&gt;In our &lt;a href="https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24"&gt;previous post&lt;/a&gt;, we walked through creating a basic Dockerfile. However, we noticed a significant issue: the resulting image for our simple "Hello World" app was a hefty 1.62GB (uncompressed)! That’s not ideal for efficient deployment and resource utilization.&lt;/p&gt;

&lt;p&gt;Today, we're diving into how to dramatically reduce your Docker image size by carefully choosing the right base image. You might be surprised by how much impact this single decision can have.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Impact of Your Base Image&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When you build a Docker image, you're essentially layering instructions on top of a foundational image – the &lt;em&gt;base image&lt;/em&gt;. All your commands, dependencies, and application code get added on to this base. Consequently, the size and contents of your base image have a direct impact on the final size of your container image.&lt;/p&gt;

&lt;p&gt;As an example, in our previous attempt, using &lt;code&gt;FROM node:latest&lt;/code&gt; resulted in a 1.62GB (uncompressed) image. That's a lot of bloat for a tiny Node.js application! &lt;/p&gt;

&lt;p&gt;Just by switching our base image to &lt;code&gt;FROM node:alpine&lt;/code&gt;, we saw the size drop to 244MB (uncompressed). That's a huge improvement, but can we do better? Absolutely! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Understanding Different Base Image Types&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's explore the common types of base images and when to consider using them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Standard Images:&lt;/strong&gt; These are the full-fledged OS-based images like Ubuntu, Debian, or others. They come packed with a wide range of libraries and tools. While convenient, these images tend to be large due to all the extra baggage they carry. Unless you're unsure about your application's OS dependencies or are in a real rush, it's best to avoid them for production containers due to their size and resource consumption.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Alpine Images:&lt;/strong&gt; These images are based on the super lightweight Alpine Linux distribution. They are much smaller than standard images because they contain only the bare minimum packages needed to run your application. They are ideal for most use-cases and are one of the best choices when starting out with Docker optimization. However, be sure to test your application thoroughly when first switching to Alpine images, as they might lack OS-level dependencies that your application unexpectedly relies on.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Slim Images:&lt;/strong&gt; While the name might suggest otherwise, slim images can sometimes be larger than Alpine images, but still much smaller than standard ones. They often include only the packages and dependencies required to run specific applications, so it's worth exploring these images if their package set fits your use case. They can be based on various distributions like Alpine, CentOS, or Debian.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Distroless Images:&lt;/strong&gt; These are specially designed for multi-stage Docker builds. They contain &lt;em&gt;only&lt;/em&gt; your application and its runtime dependencies, excluding package managers, shells, and other common Linux utilities. This makes them incredibly small and helps improve the security posture of your containers. Distroless images are perfect for production deployments after you understand the entire application stack and dependencies. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;As Google, the creator of distroless images, puts it, "Distroless images contain only your application and its runtime dependencies." You can read more about them &lt;a href="https://github.com/GoogleContainerTools/distroless" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Choosing the Right Image: A Practical Approach&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deciding which image to use might seem daunting, but a simple approach helps here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Start with Alpine:&lt;/strong&gt; Begin with an Alpine-based image like &lt;code&gt;node:alpine&lt;/code&gt; for Node.js applications.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Inspect and Verify:&lt;/strong&gt; Examine the image's layers and included packages on Docker Hub. This can give you insights into the image's composition. For example, you can check the layers of a specific image tag like &lt;code&gt;node:current-alpine3.20&lt;/code&gt; &lt;a href="https://hub.docker.com/layers/library/node/current-alpine3.20/images/sha256-3a5020f72984cd15b5abc6e285843a19510ee6ec21eb523268811f0606e65ef9" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Add Dependencies Manually:&lt;/strong&gt; If an Alpine image lacks required dependencies, you can install them manually within your Dockerfile. You can even create your own custom base image from scratch if needed.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Advance to Distroless:&lt;/strong&gt; For production, after thorough testing, and when the full application dependencies are well-known, consider using distroless images for maximum size reduction and security.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Let's Compare Image Sizes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To illustrate the point, let's take a look at the image sizes we saw in our example, using different base images. We'll show both uncompressed and compressed sizes for comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Base Image &amp;amp; Build Setup&lt;/th&gt;
&lt;th&gt;Image Tag&lt;/th&gt;
&lt;th&gt;Uncompressed Docker Image&lt;/th&gt;
&lt;th&gt;Compressed Docker Image&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;node:20-alpine&lt;/code&gt; (build) &amp;amp; &lt;code&gt;gcr.io/distroless/nodejs20-debian12&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:distroless&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;191 MB&lt;/td&gt;
&lt;td&gt;49.61 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:slim&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;364 MB&lt;/td&gt;
&lt;td&gt;78.52 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:alpine&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;244 MB&lt;/td&gt;
&lt;td&gt;56.68 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;node:latest&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;mycontainer:default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;1.62 GB&lt;/td&gt;
&lt;td&gt;381.73 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Note: The uncompressed size can be checked by using the command &lt;code&gt;docker image ls | grep mycontainer&lt;/code&gt;. The compressed size can be seen when the image is pushed to a container registry or by using &lt;code&gt;docker save mycontainer:distroless | gzip -c | wc -c&lt;/code&gt; to see the compressed file size in bytes.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/atharvaunde/understanding-the-difference-compressed-vs-uncompressed-docker-image-sizes-1fn0"&gt;Whats the difference in compressed and uncompressed size?&lt;br&gt;
&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Optimization through Distroless (Multi-stage Build)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now, let's see how to optimize it even further using a Distroless image. Here's an updated Dockerfile:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;node:20-alpine&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build-env&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json yarn.lock ./&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; NODE_ENV=production&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;yarn &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--frozen-lockfile&lt;/span&gt; &lt;span class="nt"&gt;--production&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; index.js ./&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; gcr.io/distroless/nodejs20-debian12&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build-env /app /app&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["index.js"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; We use &lt;code&gt;node:20-alpine&lt;/code&gt; as a &lt;em&gt;builder&lt;/em&gt; image to install our dependencies, copy the source, and prepare the application for production.&lt;/li&gt;
&lt;li&gt; We then copy all necessary artifacts (&lt;code&gt;/app&lt;/code&gt;) into a distroless image &lt;code&gt;gcr.io/distroless/nodejs20-debian12&lt;/code&gt; which only contains the absolute runtime requirements.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This method creates a final image with a minimal footprint, as demonstrated in the table.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusion&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Choosing the right base image is a critical step in optimizing your Docker images. Start with Alpine images, manually add needed dependencies, and ultimately aim for distroless images in production. By understanding the trade-offs of each image type, you can greatly reduce your image size, improving efficiency, performance, and resource consumption. Stay tuned for more Docker tips and tricks in future blog posts!&lt;/p&gt;

&lt;p&gt;The table clearly highlights the dramatic size difference when using different base images and build strategies. The multi-stage distroless approach yields the smallest final image, making it ideal for production deployments.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Understanding the Difference: Compressed vs. Uncompressed Docker Image Sizes</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 01 Feb 2025 13:40:36 +0000</pubDate>
      <link>https://dev.to/atharvaunde/understanding-the-difference-compressed-vs-uncompressed-docker-image-sizes-1fn0</link>
      <guid>https://dev.to/atharvaunde/understanding-the-difference-compressed-vs-uncompressed-docker-image-sizes-1fn0</guid>
      <description>&lt;p&gt;When you build a Docker image, you're essentially creating a series of layers, each representing a step or instruction in your Dockerfile. These layers build on top of each other, forming the final image. Let's break down what the compressed and uncompressed sizes mean in this context:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Uncompressed Image Size&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;What it is:&lt;/strong&gt; The uncompressed size represents the &lt;em&gt;total&lt;/em&gt; size of all the layers in your Docker image as they exist on your local machine. This includes all the intermediate layers created during the build process, as well as any duplicate data that may exist across layers. Think of it as the raw, unoptimized size of your image.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Why it's larger:&lt;/strong&gt; This size is usually much larger because:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Intermediate Layers:&lt;/strong&gt; Docker caches intermediate layers during the build process. These layers are kept for efficiency if you rebuild the image later, but they all contribute to the total uncompressed size.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Duplicate Data:&lt;/strong&gt; When you copy files into your image, Docker might create new layers even if some data is repeated from previous layers. For example, if you copy a directory in an early step and copy it again later with minor changes.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Unoptimized Storage:&lt;/strong&gt;  The uncompressed size doesn't take advantage of compression or any optimized storage techniques.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Compressed Image Size&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;What it is:&lt;/strong&gt; The compressed size is the size of your Docker image after it has been processed and optimized by Docker for storage and transfer. This is the size you'll see when the image is pushed to a registry like Docker Hub or when you manually save it using &lt;code&gt;docker save&lt;/code&gt; with compression.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;How it's smaller:&lt;/strong&gt; Docker applies several optimizations during this process:

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Gzip Compression:&lt;/strong&gt; The most significant optimization is that Docker uses gzip to compress the individual layers of your image. This significantly reduces the amount of storage space required.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Layer Deduplication:&lt;/strong&gt; Docker identifies and removes duplicate data across layers. If two layers contain the same file, only one copy of the data will be stored, and layers can reference that single source.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Optimized Storage:&lt;/strong&gt; Docker takes advantage of optimized storage mechanisms which only takes up space required for differences between layers and ensures it is stored efficiently.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Still Confused?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you're packing for an outing!&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Uncompressed:&lt;/strong&gt; This is like throwing all your clothes, toiletries, and shoes into a big suitcase without any organization. It takes up a lot of space.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Compressed:&lt;/strong&gt; This is like carefully rolling up your clothes, using packing cubes to organize items, and removing any unnecessary duplicate items. The whole suitcase is now much smaller.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;TL DR;&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Local vs. Registry:&lt;/strong&gt; The uncompressed size is what you see locally while the compressed size is what's stored and transferred to/from a container registry.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Transfer Efficiency:&lt;/strong&gt; Docker optimizes images during upload to a container registry, and because of compression, downloads will also take less time.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;True Size:&lt;/strong&gt; The compressed size gives you a more accurate idea of the actual space your image will occupy on a registry or when you save it as a compressed archive.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Size Optimization Goal:&lt;/strong&gt; When trying to optimize your docker image size, the goal is to minimize the compressed size, because that affects download and upload times, and the cost of storage in the registry.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;In Summary:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The uncompressed size is a useful metric to observe the effects of your changes during the image building process. However, the compressed size is the true indicator of how large your image is when stored and transferred. It's this compressed size that you should focus on when optimizing your Docker images for better performance and efficiency.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;So how to check the size?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You can check the uncompressed size by running &lt;code&gt;docker image ls | grep &amp;lt;your_image_name&amp;gt;&lt;/code&gt; command. To check the compressed size you can either push it to DockerHub, or use command &lt;code&gt;docker save &amp;lt;your_image_name&amp;gt; | gzip -c | wc -c&lt;/code&gt; to see compressed size in bytes.&lt;/p&gt;

&lt;p&gt;Still confused or having doubts?&lt;br&gt;
Write down in comments section!&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>containers</category>
    </item>
    <item>
      <title>Excited to share my first blog post: "Practical Docker: Step-by-Step Container Creation and Execution"! In this guide, I walk through the essential steps of building and running a simple Docker container, with a focus on practical commands and concepts.</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 01 Feb 2025 11:27:11 +0000</pubDate>
      <link>https://dev.to/atharvaunde/excited-to-share-my-first-blog-post-practical-docker-step-by-step-container-creation-and-jh2</link>
      <guid>https://dev.to/atharvaunde/excited-to-share-my-first-blog-post-practical-docker-step-by-step-container-creation-and-jh2</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/atharvaunde" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2762428%2F8e921033-436b-4e3a-ae71-db308ce9af74.jpg" alt="atharvaunde"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Practical Docker: Step-by-Step Container Creation and Execution&lt;/h2&gt;
      &lt;h3&gt;Atharva Unde ・ Feb 1&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#devops&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dockerforbeginners&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>devops</category>
      <category>docker</category>
      <category>dockerforbeginners</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Practical Docker: Step-by-Step Container Creation and Execution</title>
      <dc:creator>Atharva Unde</dc:creator>
      <pubDate>Sat, 01 Feb 2025 11:18:30 +0000</pubDate>
      <link>https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24</link>
      <guid>https://dev.to/atharvaunde/practical-docker-step-by-step-container-creation-and-execution-a24</guid>
      <description>&lt;p&gt;Running your software in containers, whether it's a website, an app, or even your database, is becoming increasingly important. Docker is a powerful tool for this, and a crucial part of it is the Dockerfile.&lt;/p&gt;

&lt;p&gt;Think of a Dockerfile as a recipe for building a Docker image. It's a simple text file that lists all the steps needed to create a complete, self-contained package (the image) of your application. This includes things like installing necessary software, setting up your environment, and copying over your code. Docker reads this recipe and automatically builds the image, making sure everything is prepared exactly the way you want it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why all this effort?&lt;/strong&gt; &lt;br&gt;
Repeatable results are critical in DevOps. A Dockerfile ensures that every time you build your image, you get the exact same, reliable outcome. &lt;br&gt;
It eliminates the &lt;strong&gt;"it works on my machine"&lt;/strong&gt; problem by providing a consistent environment.&lt;/p&gt;

&lt;p&gt;Let's get started with the technical details of creating a Dockerfile. This will give you the foundation for building your own containerized applications.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM node:latest
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["node", "index.js"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;u&gt;This example showcases a fundamental Dockerfile structure. In future articles, we'll explore more techniques for writing Dockerfiles that improve efficiency, security, and maintainability.&lt;br&gt;
&lt;/u&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Breakdown of the Dockerfile
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;FROM node:latest&lt;/strong&gt;&lt;br&gt;
This specifies the base image to use. node:latest pulls the latest official Node.js image from Docker Hub. This image already has Node.js and npm pre-installed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WORKDIR /app&lt;/strong&gt;&lt;br&gt;
This sets the working directory inside the container to /app. This is crucial for organization and makes the Dockerfile more readable. Subsequent instructions will operate within this directory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;COPY package*.json ./&lt;/strong&gt;&lt;br&gt;
This copies the package.json and package-lock.json files from the build context (your local project directory) into the /app directory inside the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;RUN yarn&lt;/strong&gt;&lt;br&gt;
This crucial step runs the yarn command inside the container. It installs all the dependencies listed in package.json. This ensures the container has all the necessary packages.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;COPY . .&lt;/strong&gt;&lt;br&gt;
This copies all the remaining files and directories from the build context to the /app directory. This effectively copies the entire application code to the container.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;EXPOSE 3000&lt;/strong&gt;&lt;br&gt;
This declares that the container will listen on port 3000. While it's important for visibility, it does not automatically map this port to your host.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CMD ["node", "index.js"]&lt;/strong&gt;&lt;br&gt;
This is the command that runs when the container starts. It executes the Node.js file index.js. This is how your application is launched within the container. &lt;u&gt;There can be only be one CMD instruction in a Dockerfile.&lt;br&gt;
&lt;/u&gt;&lt;/p&gt;

&lt;p&gt;Now that we've created the Dockerfile, let's build the actual container image. This involves using the docker build command.&lt;/p&gt;

&lt;p&gt;To build your image, open your terminal and navigate to the directory containing your Dockerfile and the application code. Then, run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker build -t mycontainer:latest .
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This builds a docker image with the name as &lt;code&gt;myContainer&lt;/code&gt; and gets tagged as &lt;code&gt;latest&lt;/code&gt;. &lt;code&gt;.&lt;/code&gt; denotes that &lt;code&gt;Dockerfile&lt;/code&gt; is supposed to take the current directory from which the command is being run as the build context for source code and other files.&lt;/p&gt;

&lt;p&gt;If the build process is successful, you'll see output showing the image ID. You can verify the image was built by running&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker image ls
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
mycontainer   latest    e50c98928825   7 seconds ago   1.62GB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's worry about the size of the image in the next chapter of the blog, where we will discuss how to write an optimized Dockerfile&lt;/p&gt;

&lt;p&gt;Lets run the image and see if the container is responding to our requests on &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -d -p 3000:3000 --name myapp mycontainer:latest 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command creates a detached container named &lt;code&gt;myapp&lt;/code&gt; from the &lt;code&gt;mycontainer&lt;/code&gt; image using the &lt;code&gt;latest&lt;/code&gt; tag, maps port 3000 on the host to port 3000 inside the container, and starts the application defined in your CMD instruction inside the container. &lt;/p&gt;

&lt;p&gt;After running this command, you can access your application by navigating to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt; in your browser on the host machine!&lt;/p&gt;

&lt;p&gt;Source Code: &lt;a href="https://github.com/atharvaunde/dockerExamples.git" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do let me know in comments in case you face any issues,&lt;/p&gt;

</description>
      <category>devops</category>
      <category>docker</category>
      <category>dockerforbeginners</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
