Home Explore Blog CI



docker

3rd chunk of `content/get-started/docker-concepts/building-images/using-the-build-cache.md`
428ea387bb565b06322432777a3cbbf59cc4ae5eb44f7fc60000000100000ff3
   The subsequent build was completed in just 1.0 second by leveraging the cached layers. No need to repeat time-consuming steps like installing dependencies.


    <table>
      <tr>
       <td>Steps
       </td>
       <td>Description
       </td>
       <td>Time Taken(1st Run)
       </td>
       <td>Time Taken (2nd Run)
       </td>
      </tr>
      <tr>
       <td>1
       </td>
       <td>Load build definition from Dockerfile
       </td>
       <td>0.0 seconds
       </td>
       <td>0.0 seconds
       </td>
      </tr>
      <tr>
       <td>2
       </td>
       <td>Load metadata for docker.io/library/node:20-alpine
       </td>
       <td>2.7 seconds
       </td>
       <td>0.9 seconds
       </td>
      </tr>
      <tr>
       <td>3
       </td>
       <td>Load .dockerignore
       </td>
       <td>0.0 seconds
       </td>
       <td>0.0 seconds
       </td>
      </tr>
      <tr>
       <td>4
       </td>
       <td>Load build context
    <p>
    (Context size: 4.60MB)
       </td>
       <td>0.1 seconds
       </td>
       <td>0.0 seconds
       </td>
      </tr>
      <tr>
       <td>5
       </td>
       <td>Set the working directory (WORKDIR)
       </td>
       <td>0.1 seconds
       </td>
       <td>0.0 seconds
       </td>
      </tr>
      <tr>
       <td>6
       </td>
       <td>Copy the local code into the container
       </td>
       <td>0.0 seconds
       </td>
       <td>0.0 seconds
       </td>
      </tr>
      <tr>
       <td>7
       </td>
       <td>Run yarn install --production
       </td>
       <td>10.0 seconds
       </td>
       <td>0.0 seconds
       </td>
      </tr>
      <tr>
       <td>8
       </td>
       <td>Exporting layers
       </td>
       <td>2.2 seconds
       </td>
       <td>0.0 seconds
       </td>
      </tr>
      <tr>
       <td>9
       </td>
       <td>Exporting the final image
       </td>
       <td>3.0 seconds
       </td>
       <td>0.0 seconds
       </td>
     </tr>
    </table>


    Going back to the `docker image history` output, you see that each command in the Dockerfile becomes a new layer in the image. You might remember that when you made a change to the image, the `yarn` dependencies had to be reinstalled. Is there a way to fix this? It doesn't make much sense to reinstall the same dependencies every time you build, right?

    To fix this, restructure your Dockerfile so that the dependency cache remains valid unless it really needs to be invalidated. For Node-based applications, dependencies are defined in the `package.json` file. You'll want to reinstall the dependencies if that file changes, but use cached dependencies if the file is unchanged. So, start by copying only that file first, then install the dependencies, and finally copy everything else. Then, you only need to recreate the yarn dependencies if there was a change to the `package.json` file.

6. Update the Dockerfile to copy in the `package.json` file first, install dependencies, and then copy everything else in.

     ```dockerfile
     FROM node:20-alpine
     WORKDIR /app
     COPY package.json yarn.lock ./
     RUN yarn install --production 
     COPY . . 
     EXPOSE 3000
     CMD ["node", "src/index.js"]
     ```

7. Create a file named `.dockerignore` in the same folder as the Dockerfile with the following contents.

     ```plaintext
     node_modules
     ```

8. Build the new image:

    ```console
    $ docker build .
    ```

    You'll then see output similar to the following:

    ```console
    [+] Building 16.1s (10/10) FINISHED
    => [internal] load build definition from Dockerfile                                               0.0s
    => => transferring dockerfile: 175B                                                               0.0s
    => [internal] load .dockerignore                                                                  0.0s
    => => transferring context: 2B                                                                    0.0s
    => [internal] load metadata for docker.io/library/node:21-alpine                                  0.0s

Title: Optimizing Docker Builds with Caching and .dockerignore
Summary
This section explains how to optimize Docker builds by restructuring the Dockerfile to leverage caching for Node.js dependencies. It details how to copy the 'package.json' and 'yarn.lock' files first, install dependencies, and then copy the remaining application code. This approach ensures that dependencies are only reinstalled when the dependency files change. The section also introduces the '.dockerignore' file to exclude 'node_modules' from the build context, further improving build performance. Finally, it guides users through rebuilding the image with these optimizations and shows the resulting output.