Stay organized with collections
Save and categorize content based on your preferences.
This page describes how to build leaner Docker images.
Building leaner containers
When you containerize an application, files that are not needed at runtime, such
as build-time dependencies and intermediate files, can be inadvertently included
in the container image. These unneeded files can increase the size of the
container image and thus add extra time and cost as the image moves between your
Docker registry and your container runtime.
To help reduce the size of your container image, separate the building of the
application, along with the tools used to build it, from the assembly of the
runtime container.
Cloud Build provides a series of Docker
containers with common
developer tools such as Git, Docker, and the Google Cloud CLI. Use
these tools to define a build config file with one step to build the
application, and another step to assemble its final runtime environment.
For example, if you're building a Java application, which requires files such as
the source code, application libraries, build systems, build system
dependencies, and the JDK, you might have a Dockerfile that looks like the
following:
FROM java:8
COPY . workdir/
WORKDIR workdir
RUN GRADLE_USER_HOME=cache ./gradlew buildDeb -x test
RUN dpkg -i ./gate-web/build/distributions/*.deb
CMD ["/opt/gate/bin/gate"]
In the above example, Gradle, which is used to build the package, downloads a
large number of libraries in order to function. These libraries are essential to
the building of the package, but are not needed at runtime. All of the runtime
dependencies are bundled up in the package.
Each command in the Dockerfile creates a new layer in the container. If data
is generated in that layer and is not deleted in the same command, that space
cannot be recovered. In this case Gradle is downloading hundreds of megabytes of
libraries to the cache directory in order to perform the build, but the
libraries are not deleted.
A more efficient way to perform the build is to use Cloud Build to
separate building the application from building its runtime layer.
The following example separates the step for building the Java application from
the step for assembling the runtime container:
YAML
Build the application: In cloudbuild.yaml, add
a step to build the application.
The following code adds a step that builds the java:8 image,
which contains the Java code.
Assemble the runtime container: In cloudbuild.yaml,
add a step to assemble the runtime container.
The following code adds a step named
gcr.io/cloud-builders/docker that assembles the runtime
container. It defines the runtime container in a separate file named
Dockerfile.slim.
The example uses the Alpine Linux base layer
openjdk:8u111-jre-alpine, which is incredibly lean. Also, it
includes the JRE, instead of the bulkier JDK that was necessary to build the
application.
Assemble the runtime container: In cloudbuild.json, add a
step to assemble the runtime container.
The following code adds a step named
gcr.io/cloud-builders/docker that assembles the runtime
container. It defines the runtime container in a separate file named
Dockerfile.slim.
The example uses the Alpine Linux base layer
openjdk:8u111-jre-alpine, which is incredibly lean. Also, it
includes the JRE, instead of the bulkier JDK that was necessary to build the
application.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Hard to understand","hardToUnderstand","thumb-down"],["Incorrect information or sample code","incorrectInformationOrSampleCode","thumb-down"],["Missing the information/samples I need","missingTheInformationSamplesINeed","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-08-07 UTC."],[[["\u003cp\u003eBuilding leaner Docker images involves separating the application build process from the assembly of the runtime environment.\u003c/p\u003e\n"],["\u003cp\u003eUnnecessary files, such as build-time dependencies and intermediate files, can bloat container image sizes, adding extra time and cost.\u003c/p\u003e\n"],["\u003cp\u003eCloud Build can be used to define separate steps for building the application and assembling its runtime environment, leveraging tools like Git, Docker, and the Google Cloud CLI.\u003c/p\u003e\n"],["\u003cp\u003eUsing a separate, lean base layer like Alpine Linux (e.g., \u003ccode\u003eopenjdk:8u111-jre-alpine\u003c/code\u003e) in a \u003ccode\u003eDockerfile.slim\u003c/code\u003e can significantly reduce the final image size by only using the JRE instead of the JDK.\u003c/p\u003e\n"],["\u003cp\u003eThe \u003ccode\u003ecloudbuild.yaml\u003c/code\u003e or \u003ccode\u003ecloudbuild.json\u003c/code\u003e files define the build steps to build and assemble the runtime container, using a separate \u003ccode\u003eDockerfile.slim\u003c/code\u003e and creating the final Docker image.\u003c/p\u003e\n"]]],[],null,["# Building leaner containers\n\nThis page describes how to build leaner Docker images.\n\nBuilding leaner containers\n--------------------------\n\nWhen you containerize an application, files that are not needed at runtime, such\nas build-time dependencies and intermediate files, can be inadvertently included\nin the container image. These unneeded files can increase the size of the\ncontainer image and thus add extra time and cost as the image moves between your\nDocker registry and your container runtime.\n\nTo help reduce the size of your container image, separate the building of the\napplication, along with the tools used to build it, from the assembly of the\nruntime container.\n\nCloud Build provides a [series of Docker\ncontainers](https://github.com/GoogleCloudPlatform/cloud-builders) with common\ndeveloper tools such as Git, Docker, and the Google Cloud CLI. Use\nthese tools to define a build config file with one step to build the\napplication, and another step to assemble its final runtime environment.\n\nFor example, if you're building a Java application, which requires files such as\nthe source code, application libraries, build systems, build system\ndependencies, and the JDK, you might have a Dockerfile that looks like the\nfollowing: \n\n FROM java:8\n\n COPY . workdir/\n\n WORKDIR workdir\n\n RUN GRADLE_USER_HOME=cache ./gradlew buildDeb -x test\n\n RUN dpkg -i ./gate-web/build/distributions/*.deb\n\n CMD [\"/opt/gate/bin/gate\"]\n\nIn the above example, Gradle, which is used to build the package, downloads a\nlarge number of libraries in order to function. These libraries are essential to\nthe building of the package, but are not needed at runtime. All of the runtime\ndependencies are bundled up in the package.\n\nEach command in the `Dockerfile` creates a new layer in the container. If data\nis generated in that layer and is not deleted in the same command, that space\ncannot be recovered. In this case Gradle is downloading hundreds of megabytes of\nlibraries to the `cache` directory in order to perform the build, but the\nlibraries are not deleted.\n\nA more efficient way to perform the build is to use Cloud Build to\nseparate building the application from building its runtime layer.\n\nThe following example separates the step for building the Java application from\nthe step for assembling the runtime container: \n\n### YAML\n\n1. **Build the application** : In `cloudbuild.yaml`, add\n a step to build the application.\n\n The following code adds a step that builds the `java:8` image,\n which contains the Java code. \n\n ```\n steps:\n\n - name: 'java:8'\n env: ['GRADLE_USER_HOME=cache']\n entrypoint: 'bash'\n args: ['-c', './gradlew gate-web:installDist -x test']\n\n ```\n2. **Assemble the runtime container** : In `cloudbuild.yaml`, add a step to assemble the runtime container.\n\n \u003cbr /\u003e\n\n The following code adds a step named\n `gcr.io/cloud-builders/docker` that assembles the runtime\n container. It defines the runtime container in a separate file named\n `Dockerfile.slim`.\n\n The example uses the Alpine Linux base layer\n `openjdk:8u111-jre-alpine`, which is incredibly lean. Also, it\n includes the JRE, instead of the bulkier JDK that was necessary to build the\n application. \n\n ```\n cloudbuild.yaml\n\n steps:\n - name: 'java:8'\n env: ['GRADLE_USER_HOME=cache']\n entrypoint: 'bash'\n args: ['-c',\n './gradlew gate-web:installDist -x test']\n\n - name: 'gcr.io/cloud-builders/docker'\n args: ['build',\n '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA',\n '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:latest',\n '-f', 'Dockerfile.slim',\n '.'\n ]\n\n\n Dockerfile.slim\n\n FROM openjdk:8-jre-alpine\n\n COPY ./gate-web/build/install/gate /opt/gate\n\n CMD [\"/opt/gate/bin/gate\"]\n ```\n3. **Create the Docker images** : In `cloudbuild.yaml`, add a step to create the images. \n\n ```\n steps:\n - name: 'java:8'\n env: ['GRADLE_USER_HOME=cache']\n entrypoint: 'bash'\n args: ['-c', './gradlew gate-web:installDist -x test']\n - name: 'gcr.io/cloud-builders/docker'\n args: ['build',\n '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA',\n '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:latest',\n '-f', 'Dockerfile.slim', '.']\n images:\n - 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA'\n - 'gcr.io/$PROJECT_ID/$REPO_NAME:latest'\n ```\n\n### JSON\n\n1. **Build the application** : In `cloudbuild.json`, add a step to\n build the application.\n\n The following code adds a step named `java:8` for\n building the Java code. \n\n ```\n {\n \"steps\": [\n {\n \"name\": \"java:8\",\n \"env\": [\n \"GRADLE_USER_HOME=cache\"\n ],\n \"entrypoint\": \"bash\",\n \"args\": [\n \"-c\",\n \"./gradlew gate-web:installDist -x test\"\n ]\n },\n }\n ```\n2. **Assemble the runtime container** : In `cloudbuild.json`, add a\n step to assemble the runtime container.\n\n The following code adds a step named\n `gcr.io/cloud-builders/docker` that assembles the runtime\n container. It defines the runtime container in a separate file named\n `Dockerfile.slim`.\n\n The example uses the Alpine Linux base layer\n `openjdk:8u111-jre-alpine`, which is incredibly lean. Also, it\n includes the JRE, instead of the bulkier JDK that was necessary to build the\n application. \n\n ```\n cloudbuild.json:\n\n {\n \"steps\": [\n {\n \"name\": \"java:8\",\n \"env\": [\n \"GRADLE_USER_HOME=cache\"\n ],\n \"entrypoint\": \"bash\",\n \"args\": [\n \"-c\",\n \"./gradlew gate-web:installDist -x test\"\n ]\n },\n {\n \"name\": \"gcr.io/cloud-builders/docker\",\n \"args\": [\n \"build\",\n \"-t\",\n \"gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA\",\n \"-t\",\n \"gcr.io/$PROJECT_ID/$REPO_NAME:latest\",\n \"-f\",\n \"Dockerfile.slim\",\n \".\"\n ]\n }\n ],\n }\n\n Dockerfile.slim:\n\n FROM openjdk:8u111-jre-alpine\n\n COPY ./gate-web/build/install/gate /opt/gate\n\n CMD [\"/opt/gate/bin/gate\"]\n ```\n3. **Create the Docker images** : In `cloudbuild.json`, add a step to create the images. \n\n ```\n {\n \"steps\": [\n {\n \"name\": \"java:8\",\n \"env\": [\n \"GRADLE_USER_HOME=cache\"\n ],\n \"entrypoint\": \"bash\",\n \"args\": [\n \"-c\",\n \"./gradlew gate-web:installDist -x test\"\n ]\n },\n {\n \"name\": \"gcr.io/cloud-builders/docker\",\n \"args\": [\n \"build\",\n \"-t\",\n \"gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA\",\n \"-t\",\n \"gcr.io/$PROJECT_ID/$REPO_NAME:latest\",\n \"-f\",\n \"Dockerfile.slim\",\n \".\"\n ]\n }\n ],\n \"images\": [\n \"gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA\",\n \"gcr.io/$PROJECT_ID/$REPO_NAME:latest\"\n ]\n }\n ```"]]