# Table of Contents - [Get Docker Desktop | Docker Docs](#get-docker-desktop-docker-docs) - [Publishing and exposing ports | Docker Docs](#publishing-and-exposing-ports-docker-docs) - [Overview | Docker Docs](#overview-docker-docs) - [Understanding the image layers | Docker Docs](#understanding-the-image-layers-docker-docs) - [Develop with containers | Docker Docs](#develop-with-containers-docker-docs) - [Overriding container defaults | Docker Docs](#overriding-container-defaults-docker-docs) - [Build and push your first image | Docker Docs](#build-and-push-your-first-image-docker-docs) - [Writing a Dockerfile | Docker Docs](#writing-a-dockerfile-docker-docs) - [Containerize your app using a multi-stage build | Docker Docs](#containerize-your-app-using-a-multi-stage-build-docker-docs) - [Build images | Docker Docs](#build-images-docker-docs) - [Build images | Docker Docs](#build-images-docker-docs) - [Persisting container data | Docker Docs](#persisting-container-data-docker-docs) - [Build and run a C++ application using Docker Compose | Docker Docs](#build-and-run-a-c-application-using-docker-compose-docker-docs) - [Build, tag, and publish an image | Docker Docs](#build-tag-and-publish-an-image-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Sharing local files with containers | Docker Docs](#sharing-local-files-with-containers-docker-docs) - [Communication and information gathering | Docker Docs](#communication-and-information-gathering-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Copy files | Docker Docs](#copy-files-docker-docs) - [Using the build cache | Docker Docs](#using-the-build-cache-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [What's next | Docker Docs](#what-s-next-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Build workflow | Docker Docs](#build-workflow-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Multi-container applications | Docker Docs](#multi-container-applications-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Containerize | Docker Docs](#containerize-docker-docs) - [Multi-stage builds | Docker Docs](#multi-stage-builds-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Containerize | Docker Docs](#containerize-docker-docs) - [Create the project | Docker Docs](#create-the-project-docker-docs) - [Containerize | Docker Docs](#containerize-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Run ROS 2 | Docker Docs](#run-ros-2-docker-docs) - [Containerize | Docker Docs](#containerize-docker-docs) - [Containerize | Docker Docs](#containerize-docker-docs) - [The H2 problem | Docker Docs](#the-h2-problem-docker-docs) - [Prerequisites for Setting Up Laravel with Docker Compose | Docker Docs](#prerequisites-for-setting-up-laravel-with-docker-compose-docker-docs) - [Why Testcontainers Cloud? | Docker Docs](#why-testcontainers-cloud-docker-docs) - [Why Docker Build Cloud? | Docker Docs](#why-docker-build-cloud-docker-docs) - [Set Up ROS 2 workspace | Docker Docs](#set-up-ros-2-workspace-docker-docs) - [Immediate setup & data persistence | Docker Docs](#immediate-setup-data-persistence-docker-docs) - [Setting up roles and permissions in Docker | Docker Docs](#setting-up-roles-and-permissions-in-docker-docker-docs) - [Run containers | Docker Docs](#run-containers-docker-docs) - [Run containers | Docker Docs](#run-containers-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Customize workflow | Docker Docs](#customize-workflow-docker-docs) - [Automate your builds with GitHub Actions | Docker Docs](#automate-your-builds-with-github-actions-docker-docs) - [Demo: set up and use Docker Build Cloud in development | Docker Docs](#demo-set-up-and-use-docker-build-cloud-in-development-docker-docs) - [JDBC URL approach | Docker Docs](#jdbc-url-approach-docker-docs) - [Execute commands | Docker Docs](#execute-commands-docker-docs) - [Why Docker Scout? | Docker Docs](#why-docker-scout-docker-docs) - [Why Docker Compose? | Docker Docs](#why-docker-compose-docker-docs) - [Advanced Configuration and Initialization | Docker Docs](#advanced-configuration-and-initialization-docker-docs) - [Understand the application | Docker Docs](#understand-the-application-docker-docs) - [Lifecycle callbacks | Docker Docs](#lifecycle-callbacks-docker-docs) - [Setting up Testcontainers Cloud by Docker | Docker Docs](#setting-up-testcontainers-cloud-by-docker-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Demo: set up and use Docker Compose | Docker Docs](#demo-set-up-and-use-docker-compose-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Demo | Docker Docs](#demo-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Configuring Testcontainers Cloud in the CI Pipeline | Docker Docs](#configuring-testcontainers-cloud-in-the-ci-pipeline-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Linting and typing | Docker Docs](#linting-and-typing-docker-docs) - [Containerize your app | Docker Docs](#containerize-your-app-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Extension annotations | Docker Docs](#extension-annotations-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Develop your app | Docker Docs](#develop-your-app-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Run tests | Docker Docs](#run-tests-docker-docs) - [Write tests | Docker Docs](#write-tests-docker-docs) - [Connecting services with Docker Compose | Docker Docs](#connecting-services-with-docker-compose-docker-docs) - [Common challenges and questions | Docker Docs](#common-challenges-and-questions-docker-docs) - [Run tests | Docker Docs](#run-tests-docker-docs) - [Run tests | Docker Docs](#run-tests-docker-docs) - [Run tests | Docker Docs](#run-tests-docker-docs) - [Run tests | Docker Docs](#run-tests-docker-docs) - [Unknown](#unknown) - [docker secret inspect | Docker Docs](#docker-secret-inspect-docker-docs) - [docker secret rm | Docker Docs](#docker-secret-rm-docker-docs) --- # Get Docker Desktop | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Get Docker Desktop ================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/introduction/get-docker-desktop/#explanation) ------------------------------------------------------------------------------------------------ Docker Desktop is the all-in-one package to build images, run containers, and so much more. This guide will walk you through the installation process, enabling you to experience Docker Desktop firsthand. > **Docker Desktop terms** > > Commercial use of Docker Desktop in larger enterprises (more than 250 employees OR more than $10 million USD in annual revenue) requires a [paid subscription](https://www.docker.com/pricing?ref=Docs&refAction=DocsGetDockerDesktop) > . ### Docker Desktop for Mac [Download (Apple Silicon)](https://desktop.docker.com/mac/main/arm64/Docker.dmg?utm_source=docker&utm_medium=webreferral&utm_campaign=docs-driven-download-mac-arm64) | [Download (Intel)](https://desktop.docker.com/mac/main/amd64/Docker.dmg?utm_source=docker&utm_medium=webreferral&utm_campaign=docs-driven-download-mac-amd64) | [Install instructions](https://docs.docker.com/desktop/setup/install/mac-install) ### Docker Desktop for Windows [Download](https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe?utm_source=docker&utm_medium=webreferral&utm_campaign=docs-driven-download-windows) | [Install instructions](https://docs.docker.com/desktop/setup/install/windows-install) ### Docker Desktop for Linux [Install instructions](https://docs.docker.com/desktop/setup/install/linux/) Once it's installed, complete the setup process and you're all set to run a Docker container. [Try it out](https://docs.docker.com/get-started/introduction/get-docker-desktop/#try-it-out) ---------------------------------------------------------------------------------------------- In this hands-on guide, you will see how to run a Docker container using Docker Desktop. Follow the instructions to run a container using the CLI. [Run your first container](https://docs.docker.com/get-started/introduction/get-docker-desktop/#run-your-first-container) -------------------------------------------------------------------------------------------------------------------------- Open your CLI terminal and start a container by running the `docker run` command: $ docker run -d -p 8080:80 docker/welcome-to-docker [Access the frontend](https://docs.docker.com/get-started/introduction/get-docker-desktop/#access-the-frontend) ---------------------------------------------------------------------------------------------------------------- For this container, the frontend is accessible on port `8080`. To open the website, visit [http://localhost:8080](http://localhost:8080/) in your browser. ![Screenshot of the landing page of the Nginx web server, coming from the running container](https://docs.docker.com/get-started/docker-concepts/the-basics/images/access-the-frontend.webp) [Manage containers using Docker Desktop](https://docs.docker.com/get-started/introduction/get-docker-desktop/#manage-containers-using-docker-desktop) ------------------------------------------------------------------------------------------------------------------------------------------------------ 1. Open Docker Desktop and select the **Containers** field on the left sidebar. 2. You can view information about your container including logs, and files, and even access the shell by selecting the **Exec** tab. ![Screenshot of exec into the running container in Docker Desktop](https://docs.docker.com/get-started/introduction/images/exec-into-docker-container.webp) 3. Select the **Inspect** field to obtain detailed information about the container. You can perform various actions such as pause, resume, start or stop containers, or explore the **Logs**, **Bind mounts**, **Exec**, **Files**, and **Stats** tabs. ![Screenshot of inspecting the running container in Docker Desktop](https://docs.docker.com/get-started/introduction/images/inspecting-container.webp) Docker Desktop simplifies container management for developers by streamlining the setup, configuration, and compatibility of applications across different environments, thereby addressing the pain points of environment inconsistencies and deployment challenges. [What's next?](https://docs.docker.com/get-started/introduction/get-docker-desktop/#whats-next) ------------------------------------------------------------------------------------------------ Now that you have Docker Desktop installed and ran your first container, it's time to start developing with containers. [Develop with containers](https://docs.docker.com/get-started/introduction/develop-with-containers/) --- # Publishing and exposing ports | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Publishing and exposing ports ============================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#explanation) -------------------------------------------------------------------------------------------------------------------- If you've been following the guides so far, you understand that containers provide isolated processes for each component of your application. Each component - a React frontend, a Python API, and a Postgres database - runs in its own sandbox environment, completely isolated from everything else on your host machine. This isolation is great for security and managing dependencies, but it also means you can’t access them directly. For example, you can’t access the web app in your browser. That’s where port publishing comes in. ### [Publishing ports](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#publishing-ports) Publishing a port provides the ability to break through a little bit of networking isolation by setting up a forwarding rule. As an example, you can indicate that requests on your host’s port `8080` should be forwarded to the container’s port `80`. Publishing ports happens during container creation using the `-p` (or `--publish`) flag with `docker run`. The syntax is: $ docker run -d -p HOST_PORT:CONTAINER_PORT nginx * `HOST_PORT`: The port number on your host machine where you want to receive traffic * `CONTAINER_PORT`: The port number within the container that's listening for connections For example, to publish the container's port `80` to host port `8080`: $ docker run -d -p 8080:80 nginx Now, any traffic sent to port `8080` on your host machine will be forwarded to port `80` within the container. > Important > > When a port is published, it's published to all network interfaces by default. This means any traffic that reaches your machine can access the published application. Be mindful of publishing databases or any sensitive information. [Learn more about published ports here](https://docs.docker.com/engine/network/#published-ports) > . ### [Publishing to ephemeral ports](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#publishing-to-ephemeral-ports) At times, you may want to simply publish the port but don’t care which host port is used. In these cases, you can let Docker pick the port for you. To do so, simply omit the `HOST_PORT` configuration. For example, the following command will publish the container’s port `80` onto an ephemeral port on the host: $ docker run -p 80 nginx Once the container is running, using `docker ps` will show you the port that was chosen: docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a527355c9c53 nginx "/docker-entrypoint.…" 4 seconds ago Up 3 seconds 0.0.0.0:54772->80/tcp romantic_williamson In this example, the app is exposed on the host at port `54772`. ### [Publishing all ports](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#publishing-all-ports) When creating a container image, the `EXPOSE` instruction is used to indicate the packaged application will use the specified port. These ports aren't published by default. With the `-P` or `--publish-all` flag, you can automatically publish all exposed ports to ephemeral ports. This is quite useful when you’re trying to avoid port conflicts in development or testing environments. For example, the following command will publish all of the exposed ports configured by the image: $ docker run -P nginx [Try it out](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#try-it-out) ------------------------------------------------------------------------------------------------------------------ In this hands-on guide, you'll learn how to publish container ports using both the CLI and Docker Compose for deploying a web application. ### [Use the Docker CLI](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#use-the-docker-cli) In this step, you will run a container and publish its port using the Docker CLI. 1. [Download and install](https://docs.docker.com/get-started/get-docker/) Docker Desktop. 2. In a terminal, run the following command to start a new container: $ docker run -d -p 8080:80 docker/welcome-to-docker The first `8080` refers to the host port. This is the port on your local machine that will be used to access the application running inside the container. The second `80` refers to the container port. This is the port that the application inside the container listens on for incoming connections. Hence, the command binds to port `8080` of the host to port `80` on the container system. 3. Verify the published port by going to the **Containers** view of the Docker Desktop Dashboard. ![A screenshot of Docker Desktop Dashboard showing the published port](https://docs.docker.com/get-started/docker-concepts/running-containers/images/published-ports.webp) 4. Open the website by either selecting the link in the **Port(s)** column of your container or visiting [http://localhost:8080](http://localhost:8080/) in your browser. ![A screenshot of the landing page of the Nginx web server running in a container](https://docs.docker.com/get-started/docker-concepts/the-basics/images/access-the-frontend.webp?border=true) ### [Use Docker Compose](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#use-docker-compose) This example will launch the same application using Docker Compose: 1. Create a new directory and inside that directory, create a `compose.yaml` file with the following contents: services: app: image: docker/welcome-to-docker ports: - 8080:80 The `ports` configuration accepts a few different forms of syntax for the port definition. In this case, you’re using the same `HOST_PORT:CONTAINER_PORT` used in the `docker run` command. 2. Open a terminal and navigate to the directory you created in the previous step. 3. Use the `docker compose up` command to start the application. 4. Open your browser to [http://localhost:8080](http://localhost:8080/) . [Additional resources](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#additional-resources) -------------------------------------------------------------------------------------------------------------------------------------- If you’d like to dive in deeper on this topic, be sure to check out the following resources: * [`docker container port` CLI reference](https://docs.docker.com/reference/cli/docker/container/port/) * [Published ports](https://docs.docker.com/engine/network/#published-ports) [Next steps](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/#next-steps) ------------------------------------------------------------------------------------------------------------------ Now that you understand how to publish and expose ports, you're ready to learn how to override the container defaults using the `docker run` command. [Overriding container defaults](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/) --- # Overview | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Manuals](https://docs.docker.com/manuals/) * [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Reference](https://docs.docker.com/reference/) Docker AI overview ================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Docker provides tools for working with AI across your development workflow. Each tool serves a different purpose. [Which tool do I need?](https://docs.docker.com/ai-overview/#which-tool-do-i-need) ----------------------------------------------------------------------------------- | I want to... | Use | CLI command | | --- | --- | --- | | Get AI help with Docker tasks (containers, images, Dockerfiles) | [Gordon](https://docs.docker.com/ai/gordon/) | `docker ai` | | Run AI models locally with an OpenAI-compatible API | [Model Runner](https://docs.docker.com/ai/model-runner/) | `docker model` | | Connect AI tools to external services via MCP | [MCP Catalog and Toolkit](https://docs.docker.com/ai/mcp-catalog-and-toolkit/) | `docker mcp` | | Build and orchestrate custom multi-agent teams | [Docker Agent](https://docs.docker.com/ai/docker-agent/) | `docker agent` | | Run coding agents in isolated environments | [Docker Sandboxes](https://docs.docker.com/ai/sandboxes/) | `docker sandbox` | [How these tools relate](https://docs.docker.com/ai-overview/#how-these-tools-relate) -------------------------------------------------------------------------------------- **Gordon** is Docker's built-in AI assistant. It helps with Docker-specific tasks like debugging containers, writing Dockerfiles, and managing images. You interact with it through Docker Desktop or the `docker ai` command. **Docker Agent** is an open-source framework for defining teams of AI agents in YAML. You configure agents with specific roles, models, and tools, then run them from your terminal. Docker Agent is a general-purpose agent runtime, not specific to Docker tasks. **Docker Sandboxes** provides isolated microVM environments for running coding agents. It supports multiple agents including Claude Code, Codex, Copilot, Gemini, and Docker Agent. Sandboxes is the isolation layer — the agents themselves are separate tools. **Model Runner** lets you run LLMs locally. Other tools like Docker Agent can use Model Runner as a model provider. **MCP Catalog and Toolkit** manages connections between AI tools and external services using the Model Context Protocol. Gordon, Docker Agent, and third-party tools can all use MCP servers configured through the Toolkit. --- # Understanding the image layers | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Understanding the image layers ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#explanation) --------------------------------------------------------------------------------------------------------------------------- As you learned in [What is an image?](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/) , container images are composed of layers. And each of these layers, once created, are immutable. But, what does that actually mean? And how are those layers used to create the filesystem a container can use? ### [Image layers](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#image-layers) Each layer in an image contains a set of filesystem changes - additions, deletions, or modifications. Let’s look at a theoretical image: 1. The first layer adds basic commands and a package manager, such as apt. 2. The second layer installs a Python runtime and pip for dependency management. 3. The third layer copies in an application’s specific requirements.txt file. 4. The fourth layer installs that application’s specific dependencies. 5. The fifth layer copies in the actual source code of the application. This example might look like: ![screenshot of the flowchart showing the concept of the image layers](https://docs.docker.com/get-started/docker-concepts/building-images/images/container_image_layers.webp) This is beneficial because it allows layers to be reused between images. For example, imagine you wanted to create another Python application. Due to layering, you can leverage the same Python base. This will make builds faster and reduce the amount of storage and bandwidth required to distribute the images. The image layering might look similar to the following: ![screenshot of the flowchart showing the benefits of the image layering](https://docs.docker.com/get-started/docker-concepts/building-images/images/container_image_layer_reuse.webp) Layers let you extend images of others by reusing their base layers, allowing you to add only the data that your application needs. ### [Stacking the layers](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#stacking-the-layers) Layering is made possible by content-addressable storage and union filesystems. While this will get technical, here’s how it works: 1. After each layer is downloaded, it is extracted into its own directory on the host filesystem. 2. When you run a container from an image, a union filesystem is created where layers are stacked on top of each other, creating a new and unified view. 3. When the container starts, its root directory is set to the location of this unified directory, using `chroot`. When the union filesystem is created, in addition to the image layers, a directory is created specifically for the running container. This allows the container to make filesystem changes while allowing the original image layers to remain untouched. This enables you to run multiple containers from the same underlying image. [Try it out](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#try-it-out) ------------------------------------------------------------------------------------------------------------------------- In this hands-on guide, you will create new image layers manually using the [`docker container commit`](https://docs.docker.com/reference/cli/docker/container/commit/) command. Note that you’ll rarely create images this way, as you’ll normally [use a Dockerfile](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/) . But, it makes it easier to understand how it’s all working. ### [Create a base image](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#create-a-base-image) In this first step, you will create your own base image that you will then use for the following steps. 1. [Download and install](https://www.docker.com/products/docker-desktop/) Docker Desktop. 2. In a terminal, run the following command to start a new container: $ docker run --name=base-container -ti ubuntu Once the image has been downloaded and the container has started, you should see a new shell prompt. This is running inside your container. It will look similar to the following (the container ID will vary): root@d8c5ca119fcd:/# 3. Inside the container, run the following command to install Node.js: $ apt update && apt install -y nodejs When this command runs, it downloads and installs Node inside the container. In the context of the union filesystem, these filesystem changes occur within the directory unique to this container. 4. Validate if Node is installed by running the following command: $ node -e 'console.log("Hello world!")' You should then see a “Hello world!” appear in the console. 5. Now that you have Node installed, you’re ready to save the changes you’ve made as a new image layer, from which you can start new containers or build new images. To do so, you will use the [`docker container commit`](https://docs.docker.com/reference/cli/docker/container/commit/) command. Run the following command in a new terminal: $ docker container commit -m "Add node" base-container node-base 6. View the layers of your image using the `docker image history` command: $ docker image history node-base You will see output similar to the following: IMAGE CREATED CREATED BY SIZE COMMENT 9e274734bb25 10 seconds ago /bin/bash 157MB Add node cd1dba651b30 7 days ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B 7 days ago /bin/sh -c #(nop) ADD file:6089c6bede9eca8ec… 110MB 7 days ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B 7 days ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B 7 days ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B 7 days ago /bin/sh -c #(nop) ARG RELEASE 0B Note the “Add node” comment on the top line. This layer contains the Node.js install you just made. 7. To prove your image has Node installed, you can start a new container using this new image: $ docker run node-base node -e "console.log('Hello again')" With that, you should get a “Hello again” output in the terminal, showing Node was installed and working. 8. Now that you’re done creating your base image, you can remove that container: $ docker rm -f base-container > **Base image definition** > > A base image is a foundation for building other images. It's possible to use any images as a base image. However, some images are intentionally created as building blocks, providing a foundation or starting point for an application. > > In this example, you probably won’t deploy this `node-base` image, as it doesn’t actually do anything yet. But it’s a base you can use for other builds. ### [Build an app image](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#build-an-app-image) Now that you have a base image, you can extend that image to build additional images. 1. Start a new container using the newly created node-base image: $ docker run --name=app-container -ti node-base 2. Inside of this container, run the following command to create a Node program: $ echo 'console.log("Hello from an app")' > app.js To run this Node program, you can use the following command and see the message printed on the screen: $ node app.js 3. In another terminal, run the following command to save this container’s changes as a new image: $ docker container commit -c "CMD node app.js" -m "Add app" app-container sample-app This command not only creates a new image named `sample-app`, but also adds additional configuration to the image to set the default command when starting a container. In this case, you are setting it to automatically run `node app.js`. 4. In a terminal outside of the container, run the following command to view the updated layers: $ docker image history sample-app You’ll then see output that looks like the following. Note the top layer comment has “Add app” and the next layer has “Add node”: IMAGE CREATED CREATED BY SIZE COMMENT c1502e2ec875 About a minute ago /bin/bash 33B Add app 5310da79c50a 4 minutes ago /bin/bash 126MB Add node 2b7cc08dcdbb 5 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B 5 weeks ago /bin/sh -c #(nop) ADD file:07cdbabf782942af0… 69.2MB 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B 5 weeks ago /bin/sh -c #(nop) LABEL org.opencontainers.… 0B 5 weeks ago /bin/sh -c #(nop) ARG LAUNCHPAD_BUILD_ARCH 0B 5 weeks ago /bin/sh -c #(nop) ARG RELEASE 0B 5. Finally, start a new container using the brand new image. Since you specified the default command, you can use the following command: $ docker run sample-app You should see your greeting appear in the terminal, coming from your Node program. 6. Now that you’re done with your containers, you can remove them using the following command: $ docker rm -f app-container [Additional resources](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#additional-resources) --------------------------------------------------------------------------------------------------------------------------------------------- If you’d like to dive deeper into the things you learned, check out the following resources: * [`docker image history`](https://docs.docker.com/reference/cli/docker/image/history/) * [`docker container commit`](https://docs.docker.com/reference/cli/docker/container/commit/) [Next steps](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/#next-steps) ------------------------------------------------------------------------------------------------------------------------- As hinted earlier, most image builds don’t use `docker container commit`. Instead, you’ll use a Dockerfile which automates these steps for you. [Writing a Dockerfile](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/) --- # Develop with containers | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Develop with containers ======================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/introduction/develop-with-containers/#explanation) ----------------------------------------------------------------------------------------------------- Now that you have Docker Desktop installed, you are ready to do some application development. Specifically, you will do the following: 1. Clone and start a development project 2. Make changes to the backend and frontend 3. See the changes immediately [Try it out](https://docs.docker.com/get-started/introduction/develop-with-containers/#try-it-out) --------------------------------------------------------------------------------------------------- In this hands-on guide, you'll learn how to develop with containers. [Start the project](https://docs.docker.com/get-started/introduction/develop-with-containers/#start-the-project) ----------------------------------------------------------------------------------------------------------------- 1. To get started, either clone or [download the project as a ZIP file](https://github.com/docker/getting-started-todo-app/archive/refs/heads/main.zip) to your local machine. $ git clone https://github.com/docker/getting-started-todo-app And after the project is cloned, navigate into the new directory created by the clone: $ cd getting-started-todo-app 2. Once you have the project, start the development environment using Docker Compose. To start the project using the CLI, run the following command: $ docker compose watch You will see an output that shows container images being pulled down, containers starting, and more. Don't worry if you don't understand it all at this point. But, within a moment or two, things should stabilize and finish. 3. Open your browser to [http://localhost](http://localhost/) to see the application up and running. It may take a few minutes for the app to run. The app is a simple to-do application, so feel free to add an item or two, mark some as done, or even delete an item. ![Screenshot of the getting started to-do app after its first launch](https://docs.docker.com/get-started/introduction/images/develop-getting-started-app-first-launch.webp) ### [What's in the environment?](https://docs.docker.com/get-started/introduction/develop-with-containers/#whats-in-the-environment) Now that the environment is up and running, what's actually in it? At a high-level, there are several containers (or processes) that each serve a specific need for the application: * React frontend - a Node container that's running the React dev server, using [Vite](https://vitejs.dev/) . * Node backend - the backend provides an API that provides the ability to retrieve, create, and delete to-do items. * MySQL database - a database to store the list of the items. * phpMyAdmin - a web-based interface to interact with the database that is accessible at [http://db.localhost](http://db.localhost/) . * Traefik proxy - [Traefik](https://traefik.io/traefik/) is an application proxy that routes requests to the right service. It sends all requests for `localhost/api/*` to the backend, requests for `localhost/*` to the frontend, and then requests for `db.localhost` to phpMyAdmin. This provides the ability to access all applications using port 80 (instead of different ports for each service). With this environment, you as the developer don’t need to install or configure any services, populate a database schema, configure database credentials, or anything. You only need Docker Desktop. The rest just works. [Make changes to the app](https://docs.docker.com/get-started/introduction/develop-with-containers/#make-changes-to-the-app) ----------------------------------------------------------------------------------------------------------------------------- With this environment up and running, you’re ready to make a few changes to the application and see how Docker helps provide a fast feedback loop. ### [Change the greeting](https://docs.docker.com/get-started/introduction/develop-with-containers/#change-the-greeting) The greeting at the top of the page is populated by an API call at `/api/greeting`. Currently, it always returns "Hello world!". You’ll now modify it to return one of three randomized messages (that you'll get to choose). 1. Open the `backend/src/routes/getGreeting.js` file in a text editor on your local machine (in the cloned project directory). This file provides the handler for the API endpoint. Your changes will automatically sync to the running container. 2. Modify the variable at the top to an array of greetings. Feel free to use the following modifications or customize it to your own liking. Also, update the endpoint to send a random greeting from this list. | | | | --- | --- | | 1
2
3
4
5
6
7
8
9
10
11 | const GREETINGS = [
"Whalecome!",
"All hands on deck!",
"Charting the course ahead!",
];

module.exports = async (req, res) => {
res.send({
greeting: GREETINGS[Math.floor(Math.random() * GREETINGS.length)],
});
}; | 3. If you haven't done so yet, save the file. If you refresh your browser, you should see a new greeting. If you keep refreshing, you should see all of the messages appear. ![Screenshot of the to-do app with a new greeting](https://docs.docker.com/get-started/introduction/images/develop-app-with-greetings.webp) ### [Change the placeholder text](https://docs.docker.com/get-started/introduction/develop-with-containers/#change-the-placeholder-text) When you look at the app, you'll see the placeholder text is simply "New Item". You’ll now make that a little more descriptive and fun. You’ll also make a few changes to the styling of the app too. 1. Open the `client/src/components/AddNewItemForm.jsx` file in your local project directory. This provides the component to add a new item to the to-do list. 2. Modify the `placeholder` attribute of the `Form.Control` element to whatever you'd like to display. | | | | --- | --- | | 33
34
35
36
37
38
39 | value={newItem}
onChange={(e) => setNewItem(e.target.value)}
type="text"
placeholder="What do you need to do?"
aria-label="New item"
/> | 3. Save the file and go back to your browser. You should see the change already hot-reloaded into your browser. If you don't like it, feel free to tweak it until it looks just right. ![Screenshot of the to-do app with an updated placeholder in the add item text field"](https://docs.docker.com/get-started/introduction/images/develop-app-with-updated-placeholder.webp) ### [Change the background color](https://docs.docker.com/get-started/introduction/develop-with-containers/#change-the-background-color) Before you consider the application finalized, you need to make the colors better. 1. Open the `client/src/index.scss` file in your local project directory. 2. Adjust the `background-color` attribute to any color you'd like. The provided snippet is a soft blue to go along with Docker's nautical theme. If you're using an IDE, you can pick a color using the integrated color pickers. Otherwise, feel free to use an online [Color Picker](https://www.w3schools.com/colors/colors_picker.asp) . | | | | --- | --- | | 3
4
5
6
7 | body {
background-color: #99bbff;
margin-top: 50px;
font-family: "Lato";
} | Each save should let you see the change immediately in the browser. Keep adjusting it until it's the perfect setup for you. ![Screenshot of the to-do app with a new placeholder and background color"](https://docs.docker.com/get-started/introduction/images/develop-app-with-updated-client.webp) And with that, you're done. Congrats on updating your website. [Recap](https://docs.docker.com/get-started/introduction/develop-with-containers/#recap) ----------------------------------------------------------------------------------------- Before you move on, take a moment and reflect on what happened here. Within a few moments, you were able to: * Start a complete development project with zero installation effort. The containerized environment provided the development environment, ensuring you have everything you need. You didn't have to install Node, MySQL, or any of the other dependencies directly on your machine. All you needed was Docker Desktop and a code editor. * Make changes and see them immediately. This was made possible because 1. the processes running in each container are watching and responding to file changes and 2) the files in your local project directory are shared with the containerized environment, so edits you make locally are automatically synced to the containers. Docker Desktop enables all of this and so much more. Once you start thinking with containers, you can create almost any environment and easily share it with your team. [Next steps](https://docs.docker.com/get-started/introduction/develop-with-containers/#next-steps) --------------------------------------------------------------------------------------------------- Now that the application has been updated, you’re ready to learn about packaging it as a container image and pushing it to a registry, specifically Docker Hub. [Build and push your first image](https://docs.docker.com/get-started/introduction/build-and-push-first-image/) --- # Overriding container defaults | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Overriding container defaults ============================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#explanation) --------------------------------------------------------------------------------------------------------------------------------- When a Docker container starts, it executes an application or command. The container gets this executable (script or file) from its image’s configuration. Containers come with default settings that usually work well, but you can change them if needed. These adjustments help the container's program run exactly how you want it to. For example, if you have an existing database container that listens on the standard port and you want to run a new instance of the same database container, then you might want to change the port settings the new container listens on so that it doesn’t conflict with the existing container. Sometimes you might want to increase the memory available to the container if the program needs more resources to handle a heavy workload or set the environment variables to provide specific configuration details the program needs to function properly. The `docker run` command offers a powerful way to override these defaults and tailor the container's behavior to your liking. The command offers several flags that let you to customize container behavior on the fly. Here's a few ways you can achieve this. ### [Overriding the network ports](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#overriding-the-network-ports) Sometimes you might want to use separate database instances for development and testing purposes. Running these database instances on the same port might conflict. You can use the `-p` option in `docker run` to map container ports to host ports, allowing you to run the multiple instances of the container without any conflict. $ docker run -d -p HOST_PORT:CONTAINER_PORT postgres ### [Setting environment variables](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#setting-environment-variables) This option sets an environment variable `foo` inside the container with the value `bar`. $ docker run -e foo=bar postgres env You will see output like the following: HOSTNAME=2042f2e6ebe4 foo=bar > Tip > > The `.env` file acts as a convenient way to set environment variables for your Docker containers without cluttering your command line with numerous `-e` flags. To use a `.env` file, you can pass `--env-file` option with the `docker run` command. > > $ docker run --env-file .env postgres env > ### [Restricting the container to consume the resources](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#restricting-the-container-to-consume-the-resources) You can use the `--memory` and `--cpus` flags with the `docker run` command to restrict how much CPU and memory a container can use. For example, you can set a memory limit for the Python API container, preventing it from consuming excessive resources on your host. Here's the command: $ docker run -e POSTGRES_PASSWORD=secret --memory="512m" --cpus="0.5" postgres This command limits container memory usage to 512 MB and defines the CPU quota of 0.5 for half a core. > **Monitor the real-time resource usage** > > You can use the `docker stats` command to monitor the real-time resource usage of running containers. This helps you understand whether the allocated resources are sufficient or need adjustment. By effectively using these `docker run` flags, you can tailor your containerized application's behavior to fit your specific requirements. [Try it out](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#try-it-out) ------------------------------------------------------------------------------------------------------------------------------- In this hands-on guide, you'll see how to use the `docker run` command to override the container defaults. 1. [Download and install](https://docs.docker.com/get-started/get-docker/) Docker Desktop. ### [Run multiple instances of the Postgres database](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#run-multiple-instances-of-the-postgres-database) 1. Start a container using the [Postgres image](https://hub.docker.com/_/postgres) with the following command: $ docker run -d -e POSTGRES_PASSWORD=secret -p 5432:5432 postgres This will start the Postgres database in the background, listening on the standard container port `5432` and mapped to port `5432` on the host machine. 2. Start a second Postgres container mapped to a different port. $ docker run -d -e POSTGRES_PASSWORD=secret -p 5433:5432 postgres This will start another Postgres container in the background, listening on the standard postgres port `5432` in the container, but mapped to port `5433` on the host machine. You override the host port just to ensure that this new container doesn't conflict with the existing running container. 3. Verify that both containers are running by going to the **Containers** view in the Docker Desktop Dashboard. ![A screenshot of the Docker Desktop Dashboard showing the running instances of Postgres containers](https://docs.docker.com/get-started/docker-concepts/running-containers/images/running-postgres-containers.webp) ### [Run Postgres container in a controlled network](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#run-postgres-container-in-a-controlled-network) By default, containers automatically connect to a special network called a bridge network when you run them. This bridge network acts like a virtual bridge, allowing containers on the same host to communicate with each other while keeping them isolated from the outside world and other hosts. It's a convenient starting point for most container interactions. However, for specific scenarios, you might want more control over the network configuration. Here's where the custom network comes in. You create a custom network by passing `--network` flag with the `docker run` command. All containers without a `--network` flag are attached to the default bridge network. Follow the steps to see how to connect a Postgres container to a custom network. 1. Create a new custom network by using the following command: $ docker network create mynetwork 2. Verify the network by running the following command: $ docker network ls This command lists all networks, including the newly created "mynetwork". 3. Connect Postgres to the custom network by using the following command: $ docker run -d -e POSTGRES_PASSWORD=secret -p 5434:5432 --network mynetwork postgres This will start Postgres container in the background, mapped to the host port 5434 and attached to the `mynetwork` network. You passed the `--network` parameter to override the container default by connecting the container to custom Docker network for better isolation and communication with other containers. You can use `docker network inspect` command to see if the container is tied to this new bridge network. > **Key difference between default bridge and custom networks** > > 1. DNS resolution: By default, containers connected to the default bridge network can communicate with each other, but only by IP address. (unless you use `--link` option which is considered legacy). It is not recommended for production use due to the various [technical shortcomings](https://docs.docker.com/engine/network/drivers/bridge/#differences-between-user-defined-bridges-and-the-default-bridge) > . On a custom network, containers can resolve each other by name or alias. > 2. Isolation: All containers without a `--network` specified are attached to the default bridge network, hence can be a risk, as unrelated containers are then able to communicate. Using a custom network provides a scoped network in which only containers attached to that network are able to communicate, hence providing better isolation. ### [Manage the resources](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#manage-the-resources) By default, containers are not limited in their resource usage. However, on shared systems, it's crucial to manage resources effectively. It's important not to let a running container consume too much of the host machine's memory. This is where the `docker run` command shines again. It offers flags like `--memory` and `--cpus` to restrict how much CPU and memory a container can use. $ docker run -d -e POSTGRES_PASSWORD=secret --memory="512m" --cpus=".5" postgres The `--cpus` flag specifies the CPU quota for the container. Here, it's set to half a CPU core (0.5) whereas the `--memory` flag specifies the memory limit for the container. In this case, it's set to 512 MB. ### [Override the default CMD and ENTRYPOINT in Docker Compose](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#override-the-default-cmd-and-entrypoint-in-docker-compose) Sometimes, you might need to override the default commands (`CMD`) or entry points (`ENTRYPOINT`) defined in a Docker image, especially when using Docker Compose. 1. Create a `compose.yml` file with the following content: services: postgres: image: postgres:18 entrypoint: ["docker-entrypoint.sh", "postgres"] command: ["-h", "localhost", "-p", "5432"] environment: POSTGRES_PASSWORD: secret The Compose file defines a service named `postgres` that uses the official Postgres image, sets an entrypoint script, and starts the container with password authentication. 2. Bring up the service by running the following command: $ docker compose up -d This command starts the Postgres service defined in the Docker Compose file. 3. Verify the authentication with Docker Desktop Dashboard. Open the Docker Desktop Dashboard, select the **Postgres** container and select **Exec** to enter into the container shell. You can type the following command to connect to the Postgres database: # psql -U postgres ![A screenshot of the Docker Desktop Dashboard selecting the Postgres container and entering into its shell using EXEC button](https://docs.docker.com/get-started/docker-concepts/running-containers/images/exec-into-postgres-container.webp) > Note > > The PostgreSQL image sets up trust authentication locally so you may notice a password isn't required when connecting from localhost (inside the same container). However, a password will be required if connecting from a different host/container. ### [Override the default CMD and ENTRYPOINT with `docker run`](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#override-the-default-cmd-and-entrypoint-with-docker-run) You can also override defaults directly using the `docker run` command with the following command: $ docker run -e POSTGRES_PASSWORD=secret postgres docker-entrypoint.sh -h localhost -p 5432 This command runs a Postgres container, sets an environment variable for password authentication, overrides the default startup commands and configures hostname and port mapping. [Additional resources](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#additional-resources) --------------------------------------------------------------------------------------------------------------------------------------------------- * [Ways to set environment variables with Compose](https://docs.docker.com/compose/how-tos/environment-variables/set-environment-variables/) * [What is a container](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/) [Next steps](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/#next-steps) ------------------------------------------------------------------------------------------------------------------------------- Now that you have learned about overriding container defaults, it's time to learn how to persist container data. [Persisting container data](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/) --- # Build and push your first image | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Build and push your first image =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#explanation) -------------------------------------------------------------------------------------------------------- Now that you've updated the [to-do list app](https://docs.docker.com/get-started/introduction/develop-with-containers/) , you’re ready to create a container image for the application and share it on Docker Hub. To do so, you will need to do the following: 1. Sign in with your Docker account 2. Create an image repository on Docker Hub 3. Build the container image 4. Push the image to Docker Hub Before you dive into the hands-on guide, the following are a few core concepts that you should be aware of. ### [Container images](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#container-images) If you’re new to container images, think of them as a standardized package that contains everything needed to run an application, including its files, configuration, and dependencies. These packages can then be distributed and shared with others. ### [Docker Hub](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#docker-hub) To share your Docker images, you need a place to store them. This is where registries come in. While there are many registries, Docker Hub is the default and go-to registry for images. Docker Hub provides both a place for you to store your own images and to find images from others to either run or use as the bases for your own images. When choosing base images, Docker Hub offers two categories of trusted, Docker-maintained images: * [Docker Official Images (DOI)](https://docs.docker.com/docker-hub/image-library/trusted-content/#docker-official-images) – Curated images for popular software, following best practices and regularly updated. * [Docker Hardened Images (DHI)](https://docs.docker.com/dhi/) – Minimal, secure, production-ready images with near-zero CVEs, designed to reduce attack surface and simplify compliance. DHI images are free and open source under Apache 2.0. In [Develop with containers](https://docs.docker.com/get-started/introduction/develop-with-containers/) , you used the following images that came from Docker Hub, each of which are [Docker Official Images](https://docs.docker.com/docker-hub/image-library/trusted-content/#docker-official-images) : * [node](https://hub.docker.com/_/node) - provides a Node environment and is used as the base of your development efforts. This image is also used as the base for the final application image. * [mysql](https://hub.docker.com/_/mysql) - provides a MySQL database to store the to-do list items * [phpmyadmin](https://hub.docker.com/_/phpmyadmin) - provides phpMyAdmin, a web-based interface to the MySQL database * [traefik](https://hub.docker.com/_/traefik) - provides Traefik, a modern HTTP reverse proxy and load balancer that routes requests to the appropriate container based on routing rules Explore the full catalog of trusted content on Docker Hub: * [Docker Official Images](https://hub.docker.com/search?badges=official) – Curated images for popular software * [Docker Hardened Images](https://hub.docker.com/hardened-images/catalog) – Security-hardened, minimal production images (also available at [dhi.io](https://dhi.io/) ) * [Docker Verified Publishers](https://hub.docker.com/search?badges=verified_publisher) – Images from verified software vendors * [Docker Sponsored Open Source](https://hub.docker.com/search?badges=open_source) – Images from sponsored OSS projects [Try it out](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#try-it-out) ------------------------------------------------------------------------------------------------------ In this hands-on guide, you'll learn how to sign in to Docker Hub and push images to Docker Hub repository. [Sign in with your Docker account](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#sign-in-with-your-docker-account) -------------------------------------------------------------------------------------------------------------------------------------------------- To push images to Docker Hub, you will need to sign in with a Docker account. 1. Open the Docker Dashboard. 2. Select **Sign in** at the top-right corner. 3. If needed, create an account and then complete the sign-in flow. Once you're done, you should see the **Sign in** button turn into a profile picture. [Create an image repository](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#create-an-image-repository) -------------------------------------------------------------------------------------------------------------------------------------- Now that you have an account, you can create an image repository. Just as a Git repository holds source code, an image repository stores container images. 1. Go to [Docker Hub](https://hub.docker.com/) . 2. Select **Create repository**. 3. On the **Create repository** page, enter the following information: * **Repository name** - `getting-started-todo-app` * **Short description** - feel free to enter a description if you'd like * **Visibility** - select **Public** to allow others to pull your customized to-do app 4. Select **Create** to create the repository. [Build and push the image](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#build-and-push-the-image) ---------------------------------------------------------------------------------------------------------------------------------- Now that you have a repository, you are ready to build and push your image. An important note is that the image you are building extends the Node image, meaning you don't need to install or configure Node, yarn, etc. You can simply focus on what makes your application unique. > **What is an image/Dockerfile?** > > Without going too deep yet, think of a container image as a single package that contains everything needed to run a process. In this case, it will contain a Node environment, the backend code, and the compiled React code. > > Any machine that runs a container using the image, will then be able to run the application as it was built without needing anything else pre-installed on the machine. > > A `Dockerfile` is a text-based script that provides the instruction set on how to build the image. For this quick start, the repository already contains the Dockerfile. CLI VS Code 1. To get started, either clone or [download the project as a ZIP file](https://github.com/docker/getting-started-todo-app/archive/refs/heads/main.zip) to your local machine. $ git clone https://github.com/docker/getting-started-todo-app And after the project is cloned, navigate into the new directory created by the clone: $ cd getting-started-todo-app 2. Build the project by running the following command, swapping out `DOCKER_USERNAME` with your username. $ docker build -t /getting-started-todo-app . For example, if your Docker username was `mobydock`, you would run the following: $ docker build -t mobydock/getting-started-todo-app . 3. To verify the image exists locally, you can use the `docker image ls` command: $ docker image ls You will see output similar to the following: REPOSITORY TAG IMAGE ID CREATED SIZE mobydock/getting-started-todo-app latest 1543656c9290 2 minutes ago 1.12GB ... 4. To push the image, use the `docker push` command. Be sure to replace `DOCKER_USERNAME` with your username: $ docker push /getting-started-todo-app Depending on your upload speeds, this may take a moment to push. 1. Open Visual Studio Code. Ensure you have the **Docker extension for VS Code** installed from [Extension Marketplace](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) . ![Screenshot of VS code extension marketplace](https://docs.docker.com/get-started/introduction/images/install-docker-extension.webp) 2. In the **File** menu, select **Open Folder**. Choose **Clone Git Repository** and paste this URL: [https://github.com/docker/getting-started-todo-app](https://github.com/docker/getting-started-todo-app) ![Screenshot of VS code showing how to clone a repository](https://docs.docker.com/get-started/introduction/images/clone-the-repo.webp) 3. Right-click the `Dockerfile` and select the **Build Image...** menu item. ![Screenshot of VS Code showing the right-click menu and "Build Image" menu item](https://docs.docker.com/get-started/introduction/images/build-vscode-menu-item.webp) 4. In the dialog that appears, enter a name of `DOCKER_USERNAME/getting-started-todo-app`, replacing `DOCKER_USERNAME` with your Docker username. 5. After pressing **Enter**, you'll see a terminal appear where the build will occur. Once it's completed, feel free to close the terminal. 6. Open the Docker Extension for VS Code by selecting the Docker logo in the left nav menu. 7. Find the image you created. It'll have a name of `docker.io/DOCKER_USERNAME/getting-started-todo-app`. 8. Expand the image to view the tags (or different versions) of the image. You should see a tag named `latest`, which is the default tag given to an image. 9. Right-click on the **latest** item and select the **Push...** option. ![Screenshot of the Docker Extension and the right-click menu to push an image](https://docs.docker.com/get-started/introduction/images/build-vscode-push-image.webp) 10. Press **Enter** to confirm and then watch as your image is pushed to Docker Hub. Depending on your upload speeds, it might take a moment to push the image. Once the upload is finished, feel free to close the terminal. [Recap](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#recap) -------------------------------------------------------------------------------------------- Before you move on, take a moment and reflect on what happened here. Within a few moments, you were able to build a container image that packages your application and push it to Docker Hub. Going forward, you’ll want to remember that: * Docker Hub is the go-to registry for finding trusted content. Docker provides a collection of trusted content, composed of Docker Official Images, Docker Verified Publishers, and Docker Sponsored Open Source Software, to use directly or as bases for your own images. * Docker Hub provides a marketplace to distribute your own applications. Anyone can create an account and distribute images. While you are publicly distributing the image you created, private repositories can ensure your images are accessible to only authorized users. > **Usage of other registries** > > While Docker Hub is the default registry, registries are standardized and made interoperable through the [Open Container Initiative](https://opencontainers.org/) > . This allows companies and organizations to run their own private registries. Quite often, trusted content is mirrored (or copied) from Docker Hub into these private registries. [Next steps](https://docs.docker.com/get-started/introduction/build-and-push-first-image/#next-steps) ------------------------------------------------------------------------------------------------------ Now that you’ve built an image, it's time to discuss why you as a developer should learn more about Docker and how it will help you in your day-to-day tasks. [What's Next](https://docs.docker.com/get-started/introduction/whats-next/) --- # Writing a Dockerfile | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Writing a Dockerfile ==================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/#explanation) --------------------------------------------------------------------------------------------------------------------- A Dockerfile is a text-based document that's used to create a container image. It provides instructions to the image builder on the commands to run, files to copy, startup command, and more. As an example, the following Dockerfile would produce a ready-to-run Python application: FROM python:3.13 WORKDIR /usr/local/app # Install the application dependencies COPY requirements.txt ./ RUN pip install --no-cache-dir -r requirements.txt # Copy in the source code COPY src ./src EXPOSE 8080 # Setup an app user so the container doesn't run as the root user RUN useradd app USER app CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8080"] ### [Common instructions](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/#common-instructions) Some of the most common instructions in a `Dockerfile` include: * `FROM ` - this specifies the base image that the build will extend. * `WORKDIR ` - this instruction specifies the "working directory" or the path in the image where files will be copied and commands will be executed. * `COPY ` - this instruction tells the builder to copy files from the host and put them into the container image. * `RUN ` - this instruction tells the builder to run the specified command. * `ENV ` - this instruction sets an environment variable that a running container will use. * `EXPOSE ` - this instruction sets configuration on the image that indicates a port the image would like to expose. * `USER ` - this instruction sets the default user for all subsequent instructions. * `CMD ["", ""]` - this instruction sets the default command a container using this image will run. To read through all of the instructions or go into greater detail, check out the [Dockerfile reference](https://docs.docker.com/engine/reference/builder/) . [Try it out](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/#try-it-out) ------------------------------------------------------------------------------------------------------------------- Just as you saw with the previous example, a Dockerfile typically follows these steps: 1. Determine your base image 2. Install application dependencies 3. Copy in any relevant source code and/or binaries 4. Configure the final image In this quick hands-on guide, you'll write a Dockerfile that builds a simple Node.js application. If you're not familiar with JavaScript-based applications, don't worry. It isn't necessary for following along with this guide. ### [Set up](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/#set-up) [Download this ZIP file](https://github.com/docker/getting-started-todo-app/archive/refs/heads/build-image-from-scratch.zip) and extract the contents into a directory on your machine. If you'd rather not download a ZIP file, clone the [https://github.com/docker/getting-started-todo-app](https://github.com/docker/getting-started-todo-app) project and checkout the `build-image-from-scratch` branch. ### [Creating the Dockerfile](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/#creating-the-dockerfile) Now that you have the project, you’re ready to create the `Dockerfile`. 1. [Download and install](https://www.docker.com/products/docker-desktop/) Docker Desktop. 2. Examine the project. Explore the contents of `getting-started-todo-app/app/`. You'll notice that a `Dockerfile` already exists. It is a simple text file that you can open in any text or code editor. 3. Delete the existing `Dockerfile`. For this exercise, you'll pretend you're starting from scratch and will create a new `Dockerfile`. 4. Create a file named `Dockerfile` in the `getting-started-todo-app/app/` folder. > **Dockerfile file extensions** > > It's important to note that the `Dockerfile` has _no_ file extension. Some editors will automatically add an extension to the file (or complain it doesn't have one). 5. In the `Dockerfile`, define your base image by adding the following line: FROM node:22-alpine 6. Now, define the working directory by using the `WORKDIR` instruction. This will specify where future commands will run and the directory files will be copied inside the container image. WORKDIR /app 7. Copy all of the files from your project on your machine into the container image by using the `COPY` instruction: COPY . . 8. Install the app's dependencies by using the `yarn` CLI and package manager. To do so, run a command using the `RUN` instruction: RUN yarn install --production 9. Finally, specify the default command to run by using the `CMD` instruction: CMD ["node", "./src/index.js"] And with that, you should have the following Dockerfile: FROM node:22-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "./src/index.js"] > **This Dockerfile isn't production-ready yet** > > It's important to note that this Dockerfile is _not_ following all of the best practices yet (by design). It will build the app, but the builds won't be as fast, or the images as secure, as they could be. > > Keep reading to learn more about how to make the image maximize the build cache, run as a non-root user, and multi-stage builds. > **Containerize new projects quickly with `docker init`** > > The `docker init` command will analyze your project and quickly create a Dockerfile, a `compose.yaml`, and a `.dockerignore`, helping you get up and going. Since you're learning about Dockerfiles specifically here, you won't use it now. But, [learn more about it here](https://docs.docker.com/reference/cli/docker/init/) > . [Additional resources](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/#additional-resources) --------------------------------------------------------------------------------------------------------------------------------------- To learn more about writing a Dockerfile, visit the following resources: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [Dockerfile best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) * [Base images](https://docs.docker.com/build/building/base-images/) * [Getting started with Docker Init](https://docs.docker.com/reference/cli/docker/init/) [Next steps](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/#next-steps) ------------------------------------------------------------------------------------------------------------------- Now that you have created a Dockerfile and learned the basics, it's time to learn about building, tagging, and pushing the images. [Build, tag and publish the Image](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/) --- # Containerize your app using a multi-stage build | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [C++ language-specific guide](https://docs.docker.com/guides/cpp/) This guide explains how to containerize C++ applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/cplusplus/cplusplus-original.svg "C++") C++ 20 minutes [1](https://docs.docker.com/guides/cpp/multistage/) [Containerize your app using a multi-stage build](https://docs.docker.com/guides/cpp/multistage/) [2](https://docs.docker.com/guides/cpp/containerize/) [Build and run a C++ application using Docker Compose](https://docs.docker.com/guides/cpp/containerize/) [3](https://docs.docker.com/guides/cpp/develop/) [Develop your app](https://docs.docker.com/guides/cpp/develop/) [4](https://docs.docker.com/guides/cpp/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/cpp/configure-ci-cd/) [5](https://docs.docker.com/guides/cpp/deploy/) [Test your deployment](https://docs.docker.com/guides/cpp/deploy/) [6](https://docs.docker.com/guides/cpp/security/) [Supply-chain security](https://docs.docker.com/guides/cpp/security/) [« Back to all guides](https://docs.docker.com/guides/) Create a multi-stage build for your C++ application =================================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/cpp/multistage/#prerequisites) ------------------------------------------------------------------------------ * You have a [Git client](https://git-scm.com/downloads) . The examples in this section use a command-line based Git client, but you can use any client. [Overview](https://docs.docker.com/guides/cpp/multistage/#overview) -------------------------------------------------------------------- This section walks you through creating a multi-stage Docker build for a C++ application. A multi-stage build is a Docker feature that allows you to use different base images for different stages of the build process, so you can optimize the size of your final image and separate build dependencies from runtime dependencies. The standard practice for compiled languages like C++ is to have a build stage that compiles the code and a runtime stage that runs the compiled binary, because the build dependencies are not needed at runtime. [Get the sample application](https://docs.docker.com/guides/cpp/multistage/#get-the-sample-application) -------------------------------------------------------------------------------------------------------- Let's use a simple C++ application that prints `Hello, World!` to the terminal. To do so, clone the sample repository to use with this guide: $ git clone https://github.com/dockersamples/c-plus-plus-docker.git The example for this section is under the `hello` directory in the repository. Get inside it and take a look at the files: $ cd c-plus-plus-docker/hello $ ls You should see the following files: Dockerfile hello.cpp [Check the Dockerfile](https://docs.docker.com/guides/cpp/multistage/#check-the-dockerfile) -------------------------------------------------------------------------------------------- Open the `Dockerfile` in an IDE or text editor. The `Dockerfile` contains the instructions for building the Docker image. # Stage 1: Build stage FROM ubuntu:latest AS build # Install build-essential for compiling C++ code RUN apt-get update && apt-get install -y build-essential # Set the working directory WORKDIR /app # Copy the source code into the container COPY hello.cpp . # Compile the C++ code statically to ensure it doesn't depend on runtime libraries RUN g++ -o hello hello.cpp -static # Stage 2: Runtime stage FROM scratch # Copy the static binary from the build stage COPY --from=build /app/hello /hello # Command to run the binary CMD ["/hello"] The `Dockerfile` has two stages: 1. **Build stage**: This stage uses the `ubuntu:latest` image to compile the C++ code and create a static binary. 2. **Runtime stage**: This stage uses the `scratch` image, which is an empty image, to copy the static binary from the build stage and run it. [Build the Docker image](https://docs.docker.com/guides/cpp/multistage/#build-the-docker-image) ------------------------------------------------------------------------------------------------ To build the Docker image, run the following command in the `hello` directory: $ docker build -t hello . The `-t` flag tags the image with the name `hello`. [Run the Docker container](https://docs.docker.com/guides/cpp/multistage/#run-the-docker-container) ---------------------------------------------------------------------------------------------------- To run the Docker container, use the following command: $ docker run hello You should see the output `Hello, World!` in the terminal. [Summary](https://docs.docker.com/guides/cpp/multistage/#summary) ------------------------------------------------------------------ In this section, you learned how to create a multi-stage build for a C++ application. Multi-stage builds help you optimize the size of your final image and separate build dependencies from runtime dependencies. In this example, the final image only contains the static binary and doesn't include any build dependencies. As the image has an empty base, the usual OS tools are also absent. So, for example, you can't run a simple `ls` command in the container: $ docker run hello ls This makes the image very lightweight and secure. [Containerize a C++ application »](https://docs.docker.com/guides/cpp/containerize/) --- # Build images | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Go language-specific guide](https://docs.docker.com/guides/golang/) This guide teaches you how to containerize Go applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg "Go") Go 30 minutes [1](https://docs.docker.com/guides/golang/build-images/) [Build images](https://docs.docker.com/guides/golang/build-images/) [2](https://docs.docker.com/guides/golang/run-containers/) [Run containers](https://docs.docker.com/guides/golang/run-containers/) [3](https://docs.docker.com/guides/golang/develop/) [Develop your app](https://docs.docker.com/guides/golang/develop/) [4](https://docs.docker.com/guides/golang/run-tests/) [Run your tests](https://docs.docker.com/guides/golang/run-tests/) [5](https://docs.docker.com/guides/golang/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/golang/configure-ci-cd/) [6](https://docs.docker.com/guides/golang/deploy/) [Test your deployment](https://docs.docker.com/guides/golang/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Build your Go image =================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Overview](https://docs.docker.com/guides/golang/build-images/#overview) ------------------------------------------------------------------------- In this section you're going to build a container image. The image includes everything you need to run your application – the compiled application binary file, the runtime, the libraries, and all other resources required by your application. [Required software](https://docs.docker.com/guides/golang/build-images/#required-software) ------------------------------------------------------------------------------------------- To complete this tutorial, you need the following: * Docker running locally. Follow the [instructions to download and install Docker](https://docs.docker.com/desktop/) . * An IDE or a text editor to edit files. [Visual Studio Code](https://code.visualstudio.com/) is a free and popular choice but you can use anything you feel comfortable with. * A Git client. This guide uses a command-line based `git` client, but you are free to use whatever works for you. * A command-line terminal application. The examples shown in this module are from the Linux shell, but they should work in PowerShell, Windows Command Prompt, or OS X Terminal with minimal, if any, modifications. [Meet the example application](https://docs.docker.com/guides/golang/build-images/#meet-the-example-application) ----------------------------------------------------------------------------------------------------------------- The example application is a caricature of a microservice. It is purposefully trivial to keep focus on learning the basics of containerization for Go applications. The application offers two HTTP endpoints: * It responds with a string containing a heart symbol (`<3`) to requests to `/`. * It responds with `{"Status" : "OK"}` JSON to a request to `/health`. It responds with HTTP error 404 to any other request. The application listens on a TCP port defined by the value of environment variable `PORT`. The default value is `8080`. The application is stateless. The complete source code for the application is on GitHub: [github.com/docker/docker-gs-ping](https://github.com/docker/docker-gs-ping) . You are encouraged to fork it and experiment with it as much as you like. To continue, clone the application repository to your local machine: $ git clone https://github.com/docker/docker-gs-ping The application's `main.go` file is straightforward, if you are familiar with Go: package main import ( "net/http" "os" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" ) func main() { e := echo.New() e.Use(middleware.Logger()) e.Use(middleware.Recover()) e.GET("/", func(c echo.Context) error { return c.HTML(http.StatusOK, "Hello, Docker! <3") }) e.GET("/health", func(c echo.Context) error { return c.JSON(http.StatusOK, struct{ Status string }{Status: "OK"}) }) httpPort := os.Getenv("PORT") if httpPort == "" { httpPort = "8080" } e.Logger.Fatal(e.Start(":" + httpPort)) } // Simple implementation of an integer minimum // Adapted from: https://gobyexample.com/testing-and-benchmarking func IntMin(a, b int) int { if a < b { return a } return b } [Create a Dockerfile for the application](https://docs.docker.com/guides/golang/build-images/#create-a-dockerfile-for-the-application) --------------------------------------------------------------------------------------------------------------------------------------- To build a container image with Docker, a `Dockerfile` with build instructions is required. Begin your `Dockerfile` with the (optional) parser directive line that instructs BuildKit to interpret your file according to the grammar rules for the specified version of the syntax. You then tell Docker what base image you would like to use for your application: # syntax=docker/dockerfile:1 FROM golang:1.19 Docker images can be inherited from other images. Therefore, instead of creating your own base image from scratch, you can use the official Go image that already has all necessary tools and libraries to compile and run a Go application. > Note > > If you are curious about creating your own base images, you can check out the following section of this guide: [creating base images](https://docs.docker.com/build/building/base-images/#create-a-base-image) > . Note, however, that this isn't necessary to continue with your task at hand. Now that you have defined the base image for your upcoming container image, you can begin building on top of it. To make things easier when running the rest of your commands, create a directory inside the image that you're building. This also instructs Docker to use this directory as the default destination for all subsequent commands. This way you don't have to type out full file paths in the `Dockerfile`, the relative paths will be based on this directory. WORKDIR /app Usually the very first thing you do once you’ve downloaded a project written in Go is to install the modules necessary to compile it. Note, that the base image has the toolchain already, but your source code isn't in it yet. So before you can run `go mod download` inside your image, you need to get your `go.mod` and `go.sum` files copied into it. Use the `COPY` command to do this. In its simplest form, the `COPY` command takes two parameters. The first parameter tells Docker what files you want to copy into the image. The last parameter tells Docker where you want that file to be copied to. Copy the `go.mod` and `go.sum` file into your project directory `/app` which, owing to your use of `WORKDIR`, is the current directory (`./`) inside the image. Unlike some modern shells that appear to be indifferent to the use of trailing slash (`/`), and can figure out what the user meant (most of the time), Docker's `COPY` command is quite sensitive in its interpretation of the trailing slash. COPY go.mod go.sum ./ > Note > > If you'd like to familiarize yourself with the trailing slash treatment by the `COPY` command, see [Dockerfile reference](https://docs.docker.com/reference/dockerfile/#copy) > . This trailing slash can cause issues in more ways than you can imagine. Now that you have the module files inside the Docker image that you are building, you can use the `RUN` command to run the command `go mod download` there as well. This works exactly the same as if you were running `go` locally on your machine, but this time these Go modules will be installed into a directory inside the image. RUN go mod download At this point, you have a Go toolchain version 1.19.x and all your Go dependencies installed inside the image. The next thing you need to do is to copy your source code into the image. You’ll use the `COPY` command just like you did with your module files before. COPY *.go ./ This `COPY` command uses a wildcard to copy all files with `.go` extension located in the current directory on the host (the directory where the `Dockerfile` is located) into the current directory inside the image. Now, to compile your application, use the familiar `RUN` command: RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping This should be familiar. The result of that command will be a static application binary named `docker-gs-ping` and located in the root of the filesystem of the image that you are building. You could have put the binary into any other place you desire inside that image, the root directory has no special meaning in this regard. It's just convenient to use it to keep the file paths short for improved readability. Now, all that is left to do is to tell Docker what command to run when your image is used to start a container. You do this with the `CMD` command: CMD ["/docker-gs-ping"] Here's the complete `Dockerfile`: # syntax=docker/dockerfile:1 FROM golang:1.19 # Set destination for COPY WORKDIR /app # Download Go modules COPY go.mod go.sum ./ RUN go mod download # Copy the source code. Note the slash at the end, as explained in # https://docs.docker.com/reference/dockerfile/#copy COPY *.go ./ # Build RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping # Optional: # To bind to a TCP port, runtime parameters must be supplied to the docker command. # But we can document in the Dockerfile what ports # the application is going to listen on by default. # https://docs.docker.com/reference/dockerfile/#expose EXPOSE 8080 # Run CMD ["/docker-gs-ping"] The `Dockerfile` may also contain comments. They always begin with a `#` symbol, and must be at the beginning of a line. Comments are there for your convenience to allow documenting your `Dockerfile`. There is also a concept of Dockerfile directives, such as the `syntax` directive you added. The directives must always be at the very top of the `Dockerfile`, so when adding comments, make sure that the comments follow after any directives that you may have used: # syntax=docker/dockerfile:1 # A sample microservice in Go packaged into a container image. FROM golang:1.19 # ... [Build the image](https://docs.docker.com/guides/golang/build-images/#build-the-image) --------------------------------------------------------------------------------------- Now that you've created your `Dockerfile`, build an image from it. The `docker build` command creates Docker images from the `Dockerfile` and a context. A build context is the set of files located in the specified path or URL. The Docker build process can access any of the files located in the context. The build command optionally takes a `--tag` flag. This flag is used to label the image with a string value, which is easy for humans to read and recognize. If you don't pass a `--tag`, Docker will use `latest` as the default value. Build your first Docker image. $ docker build --tag docker-gs-ping . The build process will print some diagnostic messages as it goes through the build steps. The following is just an example of what these messages may look like. [+] Building 2.2s (15/15) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 701B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => resolve image config for docker.io/docker/dockerfile:1 1.1s => CACHED docker-image://docker.io/docker/dockerfile:1@sha256:39b85bbfa7536a5feceb7372a0817649ecb2724562a38360f4d6a7782a409b14 0.0s => [internal] load build definition from Dockerfile 0.0s => [internal] load .dockerignore 0.0s => [internal] load metadata for docker.io/library/golang:1.19 0.7s => [1/6] FROM docker.io/library/golang:1.19@sha256:5d947843dde82ba1df5ac1b2ebb70b203d106f0423bf5183df3dc96f6bc5a705 0.0s => [internal] load build context 0.0s => => transferring context: 6.08kB 0.0s => CACHED [2/6] WORKDIR /app 0.0s => CACHED [3/6] COPY go.mod go.sum ./ 0.0s => CACHED [4/6] RUN go mod download 0.0s => CACHED [5/6] COPY *.go ./ 0.0s => CACHED [6/6] RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:ede8ff889a0d9bc33f7a8da0673763c887a258eb53837dd52445cdca7b7df7e3 0.0s => => naming to docker.io/library/docker-gs-ping 0.0s Your exact output will vary, but provided there aren't any errors, you should see the word `FINISHED` in the first line of output. This means Docker has successfully built your image named `docker-gs-ping`. [View local images](https://docs.docker.com/guides/golang/build-images/#view-local-images) ------------------------------------------------------------------------------------------- To see the list of images you have on your local machine, you have two options. One is to use the CLI and the other is to use [Docker Desktop](https://docs.docker.com/desktop/) . Since you're currently working in the terminal, take a look at listing images with the CLI. To list images, run the `docker image ls`command (or the `docker images` shorthand): $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE docker-gs-ping latest 7f153fbcc0a8 2 minutes ago 1.11GB ... Your exact output may vary, but you should see the `docker-gs-ping` image with the `latest` tag. Because you didn't specify a custom tag when you built your image, Docker assumed that the tag would be `latest`, which is a special value. [Tag images](https://docs.docker.com/guides/golang/build-images/#tag-images) ----------------------------------------------------------------------------- An image name is made up of slash-separated name components. Name components may contain lowercase letters, digits, and separators. A separator is defined as a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator. An image is made up of a manifest and a list of layers. In simple terms, a tag points to a combination of these artifacts. You can have multiple tags for the image and, in fact, most images have multiple tags. Create a second tag for the image you built and take a look at its layers. Use the `docker image tag` (or `docker tag` shorthand) command to create a new tag for your image. This command takes two arguments; the first argument is the source image, and the second is the new tag to create. The following command creates a new `docker-gs-ping:v1.0` tag for the `docker-gs-ping:latest` you built: $ docker image tag docker-gs-ping:latest docker-gs-ping:v1.0 The Docker `tag` command creates a new tag for the image. It doesn't create a new image. The tag points to the same image and is just another way to reference the image. Now run the `docker image ls` command again to see the updated list of local images: $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE docker-gs-ping latest 7f153fbcc0a8 6 minutes ago 1.11GB docker-gs-ping v1.0 7f153fbcc0a8 6 minutes ago 1.11GB ... You can see that you have two images that start with `docker-gs-ping`. You know they're the same image because if you look at the `IMAGE ID` column, you can see that the values are the same for the two images. This value is a unique identifier Docker uses internally to identify the image. Remove the tag that you just created. To do this, you’ll use the `docker image rm` command, or the shorthand `docker rmi` (which stands for "remove image"): $ docker image rm docker-gs-ping:v1.0 Untagged: docker-gs-ping:v1.0 Notice that the response from Docker tells you that the image hasn't been removed but only untagged. Verify this by running the following command: $ docker image ls You will see that the tag `v1.0` is no longer in the list of images kept by your Docker instance. REPOSITORY TAG IMAGE ID CREATED SIZE docker-gs-ping latest 7f153fbcc0a8 7 minutes ago 1.11GB ... The tag `v1.0` has been removed but you still have the `docker-gs-ping:latest` tag available on your machine, so the image is there. [Multi-stage builds](https://docs.docker.com/guides/golang/build-images/#multi-stage-builds) --------------------------------------------------------------------------------------------- You may have noticed that your `docker-gs-ping` image weighs in at over a gigabyte, which is a lot for a tiny compiled Go application. You may also be wondering what happened to the full suite of Go tools, including the compiler, after you had built your image. The answer is that the full toolchain is still there, in the container image. Not only this is inconvenient because of the large file size, but it may also present a security risk when the container is deployed. These two issues can be solved by using [multi-stage builds](https://docs.docker.com/build/building/multi-stage/) . In a nutshell, a multi-stage build can carry over the artifacts from one build stage into another, and every build stage can be instantiated from a different base image. Thus, in the following example, you are going to use a full-scale official Go image to build your application. Then you'll copy the application binary into another image whose base is very lean and doesn't include the Go toolchain or other optional components. The `Dockerfile.multistage` in the sample application's repository has the following content: # syntax=docker/dockerfile:1 # Build the application from source FROM golang:1.19 AS build-stage WORKDIR /app COPY go.mod go.sum ./ RUN go mod download COPY *.go ./ RUN CGO_ENABLED=0 GOOS=linux go build -o /docker-gs-ping # Run the tests in the container FROM build-stage AS run-test-stage RUN go test -v ./... # Deploy the application binary into a lean image FROM gcr.io/distroless/base-debian11 AS build-release-stage WORKDIR / COPY --from=build-stage /docker-gs-ping /docker-gs-ping EXPOSE 8080 USER nonroot:nonroot ENTRYPOINT ["/docker-gs-ping"] Since you have two Dockerfiles now, you have to tell Docker what Dockerfile you'd like to use to build the image. Tag the new image with `multistage`. This tag (like any other, apart from `latest`) has no special meaning for Docker, it's just something you chose. $ docker build -t docker-gs-ping:multistage -f Dockerfile.multistage . Comparing the sizes of `docker-gs-ping:multistage` and `docker-gs-ping:latest` you see a few orders-of-magnitude difference. $ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE docker-gs-ping multistage e3fdde09f172 About a minute ago 28.1MB docker-gs-ping latest 336a3f164d0f About an hour ago 1.11GB This is so because the ["distroless"](https://github.com/GoogleContainerTools/distroless) base image that you have used in the second stage of the build is very barebones and is designed for lean deployments of static binaries. There's much more to multi-stage builds, including the possibility of multi-architecture builds, so feel free to check out [multi-stage builds](https://docs.docker.com/build/building/multi-stage/) . This is, however, not essential for your progress here. [Next steps](https://docs.docker.com/guides/golang/build-images/#next-steps) ----------------------------------------------------------------------------- In this module, you met your example application and built and container image for it. In the next module, you’ll take a look at how to run your image as a container. [Run your Go image as a container »](https://docs.docker.com/guides/golang/run-containers/) --- # Build images | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Rust language-specific guide](https://docs.docker.com/guides/rust/) This guide covers how to containerize Rust applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/rust/rust-original.svg "Rust") Rust Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/rust/build-images/) [Build images](https://docs.docker.com/guides/rust/build-images/) [2](https://docs.docker.com/guides/rust/run-containers/) [Run containers](https://docs.docker.com/guides/rust/run-containers/) [3](https://docs.docker.com/guides/rust/develop/) [Develop your app](https://docs.docker.com/guides/rust/develop/) [4](https://docs.docker.com/guides/rust/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/rust/configure-ci-cd/) [5](https://docs.docker.com/guides/rust/deploy/) [Test your deployment](https://docs.docker.com/guides/rust/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Build your Rust image ===================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/rust/build-images/#prerequisites) --------------------------------------------------------------------------------- * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. [Overview](https://docs.docker.com/guides/rust/build-images/#overview) ----------------------------------------------------------------------- This guide walks you through building your first Rust image. An image includes everything needed to run an application - the code or binary, runtime, dependencies, and any other file system objects required. [Get the sample application](https://docs.docker.com/guides/rust/build-images/#get-the-sample-application) ----------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/docker/docker-rust-hello && cd docker-rust-hello [Create a Dockerfile for Rust](https://docs.docker.com/guides/rust/build-images/#create-a-dockerfile-for-rust) --------------------------------------------------------------------------------------------------------------- Now that you have an application, you can use `docker init` to create a Dockerfile for it. Inside the `docker-rust-hello` directory, run the `docker init` command. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. Refer to the following example to answer the prompts from `docker init` and use the same answers for your prompts. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? Rust ? What version of Rust do you want to use? 1.92.0 ? What port does your server listen on? 8000 You should now have the following new files in your `docker-rust-hello` directory: * Dockerfile * .dockerignore * compose.yaml * README.Docker.md [Choose a base image](https://docs.docker.com/guides/rust/build-images/#choose-a-base-image) --------------------------------------------------------------------------------------------- Before editing your Dockerfile, you need to choose a base image. You can use the [Rust Docker Official Image](https://hub.docker.com/_/rust) , or a [Docker Hardened Image (DHI)](https://hub.docker.com/hardened-images/catalog/dhi/rust) . Docker Hardened Images (DHIs) are minimal, secure, and production-ready base images maintained by Docker. They help reduce vulnerabilities and simplify compliance. For more details, see [Docker Hardened Images](https://docs.docker.com/dhi/) . Using Docker Hardened Images Using the Docker Official Images Docker Hardened Images (DHIs) are publicly available and can be used directly as base images. To pull Docker Hardened Images, authenticate once with Docker: docker login dhi.io Use DHIs from the dhi.io registry, for example: FROM dhi.io/rust:${RUST_VERSION}-alpine3.22-dev AS build The following Dockerfile is equivalent to the one generated by `docker init`, but it uses a Rust DHI as the build base image: Dockerfile # Make sure RUST_VERSION matches the Rust version ARG RUST_VERSION=1.92 ARG APP_NAME=docker-rust-hello ################################################################################ # Create a stage for building the application. ################################################################################ FROM dhi.io/rust:${RUST_VERSION}-alpine3.22-dev AS build ARG APP_NAME WORKDIR /app # Install host build dependencies. RUN apk add --no-cache clang lld musl-dev git # Build the application. RUN --mount=type=bind,source=src,target=src \ --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ --mount=type=cache,target=/app/target/ \ --mount=type=cache,target=/usr/local/cargo/git/db \ --mount=type=cache,target=/usr/local/cargo/registry/ \ cargo build --locked --release && \ cp ./target/release/$APP_NAME /bin/server ################################################################################ # Create a new stage for running the application that contains the minimal # We use dhi.io/static for the final stage because it’s a minimal Docker Hardened Image runtime (basically “just # enough OS to run the binary”), which helps keep the image small and with a lower attack surface compared to a # # full Alpine/Debian runtime. ################################################################################ FROM dhi.io/static:20250419 AS final # Copy the executable from the "build" stage. COPY --from=build /bin/server /bin/ # Configure rocket to listen on all interfaces. ENV ROCKET_ADDRESS=0.0.0.0 # Expose the port that the application listens on. EXPOSE 8000 # What the container should run when it is started. CMD ["/bin/server"] Dockerfile # Pin the Rust toolchain version used in the build stage. ARG RUST_VERSION=1.92 # Name of the compiled binary produced by Cargo (must match Cargo.toml package name). ARG APP_NAME=docker-rust-hello ################################################################################ # Build stage (DOI Rust image) # This stage compiles the application. ################################################################################ FROM docker.io/library/rust:${RUST_VERSION}-alpine AS build # Re-declare args inside the stage if you want to use them here. ARG APP_NAME # All build steps happen inside /app. WORKDIR /app # Install build dependencies needed to compile Rust crates on Alpine RUN apk add --no-cache clang lld musl-dev git # Build the application RUN --mount=type=bind,source=src,target=src \ --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ --mount=type=cache,target=/app/target/ \ --mount=type=cache,target=/usr/local/cargo/git/db \ --mount=type=cache,target=/usr/local/cargo/registry/ \ cargo build --locked --release && \ cp ./target/release/$APP_NAME /bin/server ################################################################################ # Runtime stage (DOI Alpine image) # This stage runs the already-compiled binary with minimal dependencies. ################################################################################ FROM docker.io/library/alpine:3.18 AS final # Create a non-privileged user (recommended best practice) ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser # Drop privileges for runtime. USER appuser # Copy only the compiled binary from the build stage. COPY --from=build /bin/server /bin/ # Rocket: listen on all interfaces inside the container. ENV ROCKET_ADDRESS=0.0.0.0 # Document the port your app listens on. EXPOSE 8000 # Start the application. CMD ["/bin/server"] For building an image, only the Dockerfile is necessary. Open the Dockerfile in your favorite IDE or text editor and see what it contains. To learn more about Dockerfiles, see the [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) . [.dockerignore file](https://docs.docker.com/guides/rust/build-images/#dockerignore-file) ------------------------------------------------------------------------------------------ When you run `docker init`, it also creates a [`.dockerignore`](https://docs.docker.com/reference/dockerfile/#dockerignore-file) file. Use the `.dockerignore` file to specify patterns and paths that you don't want copied into the image in order to keep the image as small as possible. Open up the `.dockerignore` file in your favorite IDE or text editor and see what's inside already. [Build an image](https://docs.docker.com/guides/rust/build-images/#build-an-image) ----------------------------------------------------------------------------------- Now that you’ve created the Dockerfile, you can build the image. To do this, use the `docker build` command. The `docker build` command builds Docker images from a Dockerfile and a context. A build's context is the set of files located in the specified PATH or URL. The Docker build process can access any of the files located in this context. The build command optionally takes a `--tag` flag. The tag sets the name of the image and an optional tag in the format `name:tag`. If you don't pass a tag, Docker uses "latest" as its default tag. Build the Docker image. $ docker build --tag docker-rust-image-dhi . You should see output like the following. [+] Building 1.4s (13/13) FINISHED docker:desktop-linux => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 1.67kB 0.0s => [internal] load metadata for dhi.io/static:20250419 1.1s => [internal] load metadata for dhi.io/rust:1.92-alpine3.22-dev 1.2s => [auth] static:pull token for dhi.io 0.0s => [auth] rust:pull token for dhi.io 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 646B 0.0s => [build 1/3] FROM dhi.io/rust:1.92-alpine3.22-dev@sha256:49eb72825a9e15fe48f2c4875a63c7e7f52a5b430bb52b8254b91d132aa5bf38 0.0s => => resolve dhi.io/rust:1.92-alpine3.22-dev@sha256:49eb72825a9e15fe48f2c4875a63c7e7f52a5b430bb52b8254b91d132aa5bf38 0.0s => [final 1/2] FROM dhi.io/static:20250419@sha256:74fc43fa240887b8159970e434244039aab0c6efaaa9cf044004cdc22aa2a34d 0.0s => => resolve dhi.io/static:20250419@sha256:74fc43fa240887b8159970e434244039aab0c6efaaa9cf044004cdc22aa2a34d 0.0s => [internal] load build context 0.0s => => transferring context: 117B 0.0s => CACHED [build 2/3] WORKDIR /build 0.0s => CACHED [build 3/3] RUN --mount=type=bind,source=src,target=src --mount=type=bind,source=Cargo.toml,target=Cargo.toml --mount=type=bind,source=Cargo.lock,target=Cargo 0.0s => CACHED [final 2/2] COPY --from=build /build/target/release/docker-rust-hello /server 0.0s => exporting to image 0.1s => => exporting layers 0.0s => => exporting manifest sha256:cc937bbdd712ef6e5445501f77e02ef8455ef64c567598786d46b7b21a4d4fa8 0.0s => => exporting config sha256:077507b483af4b5e1a928e527e4bb3a4aaf0557e1eea81cd39465f564c187669 0.0s => => exporting attestation manifest sha256:11b60e7608170493da1fdd88c120e2d2957f2a72a22edbc9cfbdd0dd37d21f89 0.0s => => exporting manifest list sha256:99a1b925a8d6ebf80e376b8a1e50cd806ec42d194479a3375e1cd9d2911b4db9 0.0s => => naming to docker.io/library/docker-rust-image-dhi:latest 0.0s => => unpacking to docker.io/library/docker-rust-image-dhi:latest 0.0s View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/yczk0ijw8kc5g20e8nbc8r6lj [View local images](https://docs.docker.com/guides/rust/build-images/#view-local-images) ----------------------------------------------------------------------------------------- To see a list of images you have on your local machine, you have two options. One is to use the Docker CLI and the other is to use [Docker Desktop](https://docs.docker.com/desktop/use-desktop/images/) . As you are working in the terminal already, take a look at listing images using the CLI. To list images, run the `docker images` command. $ docker images IMAGE ID DISK USAGE CONTENT SIZE EXTRA docker-rust-image-dhi:latest 99a1b925a8d6 11.6MB 2.45MB U You should see at least one image listed, including the image you just built `docker-rust-image-dhi:latest`. [Tag images](https://docs.docker.com/guides/rust/build-images/#tag-images) --------------------------------------------------------------------------- As mentioned earlier, an image name is made up of slash-separated name components. Name components may contain lowercase letters, digits, and separators. A separator can include a period, one or two underscores, or one or more dashes. A name component may not start or end with a separator. An image is made up of a manifest and a list of layers. Don't worry too much about manifests and layers at this point other than a "tag" points to a combination of these artifacts. You can have multiple tags for an image. Create a second tag for the image you built and take a look at its layers. To create a new tag for the image you built, run the following command. $ docker tag docker-rust-image-dhi:latest docker-rust-image-dhi:v1.0.0 The `docker tag` command creates a new tag for an image. It doesn't create a new image. The tag points to the same image and is just another way to reference the image. Now, run the `docker images` command to see a list of the local images. $ docker images IMAGE ID DISK USAGE CONTENT SIZE EXTRA docker-rust-image-dhi:latest 99a1b925a8d6 11.6MB 2.45MB U docker-rust-image-dhi:v1.0.0 99a1b925a8d6 11.6MB 2.45MB U You can see that two images start with `docker-rust-image-dhi`. You know they're the same image because if you take a look at the `IMAGE ID` column, you can see that the values are the same for the two images. Remove the tag you just created. To do this, use the `rmi` command. The `rmi` command stands for remove image. $ docker rmi docker-rust-image-dhi:v1.0.0 Untagged: docker-rust-image-dhi:v1.0.0 Note that the response from Docker tells you that Docker didn't remove the image, but only "untagged" it. You can check this by running the `docker images` command. $ docker images IMAGE ID DISK USAGE CONTENT SIZE EXTRA docker-rust-image-dhi:latest 99a1b925a8d6 11.6MB 2.45MB U Docker removed the image tagged with `:v1.0.0`, but the `docker-rust-image-dhi:latest` tag is available on your machine. [Summary](https://docs.docker.com/guides/rust/build-images/#summary) --------------------------------------------------------------------- This section showed how you can use `docker init` to create a Dockerfile and .dockerignore file for a Rust application. It then showed you how to build an image. And finally, it showed you how to tag an image and list all images. Related information: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [.dockerignore file](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [docker init CLI reference](https://docs.docker.com/reference/cli/docker/init/) * [docker build CLI reference](https://docs.docker.com/reference/cli/docker/buildx/build/) * [Docker Hardened Images](https://docs.docker.com/dhi/) [Next steps](https://docs.docker.com/guides/rust/build-images/#next-steps) --------------------------------------------------------------------------- In the next section learn how to run your image as a container. [Run your Rust image as a container »](https://docs.docker.com/guides/rust/run-containers/) --- # Persisting container data | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Persisting container data ========================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#explanation) ----------------------------------------------------------------------------------------------------------------------------- When a container starts, it uses the files and configuration provided by the image. Each container is able to create, modify, and delete files and does so without affecting any other containers. When the container is deleted, these file changes are also deleted. While this ephemeral nature of containers is great, it poses a challenge when you want to persist the data. For example, if you restart a database container, you might not want to start with an empty database. So, how do you persist files? ### [Container volumes](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#container-volumes) Volumes are a storage mechanism that provide the ability to persist data beyond the lifecycle of an individual container. Think of it like providing a shortcut or symlink from inside the container to outside the container. As an example, imagine you create a volume named `log-data`. $ docker volume create log-data When starting a container with the following command, the volume will be mounted (or attached) into the container at `/logs`: $ docker run -d -p 80:80 -v log-data:/logs docker/welcome-to-docker If the volume `log-data` doesn't exist, Docker will automatically create it for you. When the container runs, all files it writes into the `/logs` folder will be saved in this volume, outside of the container. If you delete the container and start a new container using the same volume, the files will still be there. > **Sharing files using volumes** > > You can attach the same volume to multiple containers to share files between containers. This might be helpful in scenarios such as log aggregation, data pipelines, or other event-driven applications. ### [Managing volumes](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#managing-volumes) Volumes have their own lifecycle beyond that of containers and can grow quite large depending on the type of data and applications you’re using. The following commands will be helpful to manage volumes: * `docker volume ls` - list all volumes * `docker volume rm ` - remove a volume (only works when the volume is not attached to any containers) * `docker volume prune` - remove all unused (unattached) volumes [Try it out](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#try-it-out) --------------------------------------------------------------------------------------------------------------------------- In this guide, you'll practice creating and using volumes to persist data created by a Postgres container. When the database runs, it stores files into the `/var/lib/postgresql` directory. By attaching the volume here, you will be able to restart the container multiple times while keeping the data. ### [Use volumes](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#use-volumes) 1. [Download and install](https://docs.docker.com/get-started/get-docker/) Docker Desktop. 2. Start a container using the [Postgres image](https://hub.docker.com/_/postgres) with the following command: $ docker run --name=db -e POSTGRES_PASSWORD=secret -d -v postgres_data:/var/lib/postgresql postgres:18 This will start the database in the background, configure it with a password, and attach a volume to the directory PostgreSQL will persist the database files. 3. Connect to the database by using the following command: $ docker exec -ti db psql -U postgres 4. In the PostgreSQL command line, run the following to create a database table and insert two records: CREATE TABLE tasks ( id SERIAL PRIMARY KEY, description VARCHAR(100) ); INSERT INTO tasks (description) VALUES ('Finish work'), ('Have fun'); 5. Verify the data is in the database by running the following in the PostgreSQL command line: SELECT * FROM tasks; You should get output that looks like the following: id | description ----+------------- 1 | Finish work 2 | Have fun (2 rows) 6. Exit out of the PostgreSQL shell by running the following command: \q 7. Stop and remove the database container. Remember that, even though the container has been deleted, the data is persisted in the `postgres_data` volume. $ docker stop db $ docker rm db 8. Start a new container by running the following command, attaching the same volume with the persisted data: $ docker run --name=new-db -d -v postgres_data:/var/lib/postgresql postgres:18 You might have noticed that the `POSTGRES_PASSWORD` environment variable has been omitted. That’s because that variable is only used when bootstrapping a new database. 9. Verify the database still has the records by running the following command: $ docker exec -ti new-db psql -U postgres -c "SELECT * FROM tasks" ### [View volume contents](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#view-volume-contents) The Docker Desktop Dashboard provides the ability to view the contents of any volume, as well as the ability to export, import, empty, delete and clone volumes. 1. Open the Docker Desktop Dashboard and navigate to the **Volumes** view. In this view, you should see the **postgres\_data** volume. 2. Select the **postgres\_data** volume’s name. 3. The **Stored Data** tab shows the contents of the volume and provides the ability to navigate the files. The **Container in-use** tab displays the name of the container using the volume, the image name, the port number used by the container, and the target. A target is a path inside a container that gives access to the files in the volume. The **Exports** tab lets you export the volume. Double-clicking on a file will let you see the contents and make changes. 4. Right-click on any file to save it or delete it. ### [Remove volumes](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#remove-volumes) Before removing a volume, it must not be attached to any containers. If you haven’t removed the previous container, do so with the following command (the `-f` will stop the container first and then remove it): $ docker rm -f new-db There are a few methods to remove volumes, including the following: * Select the **Delete Volume** option on a volume in the Docker Desktop Dashboard. * Use the `docker volume rm` command: $ docker volume rm postgres_data * Use the `docker volume prune` command to remove all unused volumes: $ docker volume prune [Additional resources](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#additional-resources) ----------------------------------------------------------------------------------------------------------------------------------------------- The following resources will help you learn more about volumes: * [Manage data in Docker](https://docs.docker.com/engine/storage) * [Volumes](https://docs.docker.com/engine/storage/volumes) * [Volume mounts](https://docs.docker.com/engine/containers/run/#volume-mounts) [Next steps](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/#next-steps) --------------------------------------------------------------------------------------------------------------------------- Now that you have learned about persisting container data, it’s time to learn about sharing local files with containers. [Sharing local files with containers](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/) --- # Build and run a C++ application using Docker Compose | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [C++ language-specific guide](https://docs.docker.com/guides/cpp/) This guide explains how to containerize C++ applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/cplusplus/cplusplus-original.svg "C++") C++ 20 minutes [1](https://docs.docker.com/guides/cpp/multistage/) [Containerize your app using a multi-stage build](https://docs.docker.com/guides/cpp/multistage/) [2](https://docs.docker.com/guides/cpp/containerize/) [Build and run a C++ application using Docker Compose](https://docs.docker.com/guides/cpp/containerize/) [3](https://docs.docker.com/guides/cpp/develop/) [Develop your app](https://docs.docker.com/guides/cpp/develop/) [4](https://docs.docker.com/guides/cpp/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/cpp/configure-ci-cd/) [5](https://docs.docker.com/guides/cpp/deploy/) [Test your deployment](https://docs.docker.com/guides/cpp/deploy/) [6](https://docs.docker.com/guides/cpp/security/) [Supply-chain security](https://docs.docker.com/guides/cpp/security/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a C++ application ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/cpp/containerize/#prerequisites) -------------------------------------------------------------------------------- * You have a [Git client](https://git-scm.com/downloads) . The examples in this section use a command-line based Git client, but you can use any client. [Overview](https://docs.docker.com/guides/cpp/containerize/#overview) ---------------------------------------------------------------------- This section walks you through containerizing and running a C++ application, using Docker Compose. [Get the sample application](https://docs.docker.com/guides/cpp/containerize/#get-the-sample-application) ---------------------------------------------------------------------------------------------------------- We're using the same sample repository that you used in the previous sections of this guide. If you haven't already cloned the repository, clone it now: $ git clone https://github.com/dockersamples/c-plus-plus-docker.git You should now have the following contents in your `c-plus-plus-docker` (root) directory. ├── c-plus-plus-docker/ │ ├── compose.yml │ ├── Dockerfile │ ├── LICENSE │ ├── ok_api.cpp │ └── README.md To learn more about the files in the repository, see the following: * [Dockerfile](https://docs.docker.com/reference/dockerfile/) * [.dockerignore](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [compose.yml](https://docs.docker.com/reference/compose-file/) [Run the application](https://docs.docker.com/guides/cpp/containerize/#run-the-application) -------------------------------------------------------------------------------------------- Inside the `c-plus-plus-docker` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You will see a message `{"Status" : "OK"}` in the browser. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/cpp/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `c-plus-plus-docker` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . In the terminal, run the following command to stop the application. $ docker compose down For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) . [Summary](https://docs.docker.com/guides/cpp/containerize/#summary) -------------------------------------------------------------------- In this section, you learned how you can containerize and run your C++ application using Docker. Related information: * [Docker Compose overview](https://docs.docker.com/compose/) [Next steps](https://docs.docker.com/guides/cpp/containerize/#next-steps) -------------------------------------------------------------------------- In the next section, you'll learn how you can develop your application using containers. [Use containers for C++ development »](https://docs.docker.com/guides/cpp/develop/) --- # Build, tag, and publish an image | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Build, tag, and publish an image ================================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#explanation) ------------------------------------------------------------------------------------------------------------------------------- In this guide, you will learn the following: * Building images - the process of building an image based on a `Dockerfile` * Tagging images - the process of giving an image a name, which also determines where the image can be distributed * Publishing images - the process to distribute or share the newly created image using a container registry ### [Building images](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#building-images) Most often, images are built using a Dockerfile. The most basic `docker build` command might look like the following: docker build . The final `.` in the command provides the path or URL to the [build context](https://docs.docker.com/build/concepts/context/#what-is-a-build-context) . At this location, the builder will find the `Dockerfile` and other referenced files. When you run a build, the builder pulls the base image, if needed, and then runs the instructions specified in the Dockerfile. With the previous command, the image will have no name, but the output will provide the ID of the image. As an example, the previous command might produce the following output: $ docker build . [+] Building 3.5s (11/11) FINISHED docker:desktop-linux => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 308B 0.0s => [internal] load metadata for docker.io/library/python:3.12 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [1/6] FROM docker.io/library/python:3.12 0.0s => [internal] load build context 0.0s => => transferring context: 123B 0.0s => [2/6] WORKDIR /usr/local/app 0.0s => [3/6] RUN useradd app 0.1s => [4/6] COPY ./requirements.txt ./requirements.txt 0.0s => [5/6] RUN pip install --no-cache-dir --upgrade -r requirements.txt 3.2s => [6/6] COPY ./app ./app 0.0s => exporting to image 0.1s => => exporting layers 0.1s => => writing image sha256:9924dfd9350407b3df01d1a0e1033b1e543523ce7d5d5e2c83a724480ebe8f00 0.0s With the previous output, you could start a container by using the referenced image: docker run sha256:9924dfd9350407b3df01d1a0e1033b1e543523ce7d5d5e2c83a724480ebe8f00 That name certainly isn't memorable, which is where tagging becomes useful. ### [Tagging images](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#tagging-images) Tagging images is the method to provide an image with a memorable name. However, there is a structure to the name of an image. A full image name has the following structure: [HOST[:PORT_NUMBER]/]PATH[:TAG] * `HOST`: The optional registry hostname where the image is located. If no host is specified, Docker's public registry at `docker.io` is used by default. * `PORT_NUMBER`: The registry port number if a hostname is provided * `PATH`: The path of the image, consisting of slash-separated components. For Docker Hub, the format follows `[NAMESPACE/]REPOSITORY`, where namespace is either a user's or organization's name. If no namespace is specified, `library` is used, which is the namespace for Docker Official Images. * `TAG`: A custom, human-readable identifier that's typically used to identify different versions or variants of an image. If no tag is specified, `latest` is used by default. Some examples of image names include: * `nginx`, equivalent to `docker.io/library/nginx:latest`: this pulls an image from the `docker.io` registry, the `library` namespace, the `nginx` image repository, and the `latest` tag. * `docker/welcome-to-docker`, equivalent to `docker.io/docker/welcome-to-docker:latest`: this pulls an image from the `docker.io` registry, the `docker` namespace, the `welcome-to-docker` image repository, and the `latest` tag * `ghcr.io/dockersamples/example-voting-app-vote:pr-311`: this pulls an image from the GitHub Container Registry, the `dockersamples` namespace, the `example-voting-app-vote` image repository, and the `pr-311` tag To tag an image during a build, add the `-t` or `--tag` flag: docker build -t my-username/my-image . If you've already built an image, you can add another tag to the image by using the [`docker image tag`](https://docs.docker.com/engine/reference/commandline/image_tag/) command: docker image tag my-username/my-image another-username/another-image:v1 ### [Publishing images](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#publishing-images) Once you have an image built and tagged, you're ready to push it to a registry. To do so, use the [`docker push`](https://docs.docker.com/engine/reference/commandline/image_push/) command: docker push my-username/my-image Within a few seconds, all of the layers for your image will be pushed to the registry. > **Requiring authentication** > > Before you're able to push an image to a repository, you will need to be authenticated. To do so, simply use the [docker login](https://docs.docker.com/engine/reference/commandline/login/) > command. [Try it out](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#try-it-out) ----------------------------------------------------------------------------------------------------------------------------- In this hands-on guide, you will build a simple image using a provided Dockerfile and push it to Docker Hub. ### [Set up](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#set-up) 1. Get the sample application. If you have Git, you can clone the repository for the sample application. Otherwise, you can download the sample application. Choose one of the following options. Clone with git Download Use the following command in a terminal to clone the sample application repository. $ git clone https://github.com/docker/getting-started-todo-app Download the source and extract it. [Download the source](https://github.com/docker/getting-started-todo-app/raw/cd61f824da7a614a8298db503eed6630eeee33a3/app.zip) 2. [Download and install](https://www.docker.com/products/docker-desktop/) Docker Desktop. 3. If you don't have a Docker account yet, [create one now](https://hub.docker.com/) . Once you've done that, sign in to Docker Desktop using that account. ### [Build an image](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#build-an-image) Now that you have a repository on Docker Hub, it's time for you to build an image and push it to the repository. 1. Using a terminal in the root of the sample app repository, run the following command. Replace `YOUR_DOCKER_USERNAME` with your Docker Hub username: $ docker build -t /concepts-build-image-demo . As an example, if your username is `mobywhale`, you would run the command: $ docker build -t mobywhale/concepts-build-image-demo . 2. Once the build has completed, you can view the image by using the following command: $ docker image ls The command will produce output similar to the following: REPOSITORY TAG IMAGE ID CREATED SIZE mobywhale/concepts-build-image-demo latest 746c7e06537f 24 seconds ago 354MB 3. You can actually view the history (or how the image was created) by using the [docker image history](https://docs.docker.com/reference/cli/docker/image/history/) command: $ docker image history mobywhale/concepts-build-image-demo You'll then see output similar to the following: IMAGE CREATED CREATED BY SIZE COMMENT f279389d5f01 8 seconds ago CMD ["node" "./src/index.js"] 0B buildkit.dockerfile.v0 8 seconds ago EXPOSE map[3000/tcp:{}] 0B buildkit.dockerfile.v0 8 seconds ago WORKDIR /app 8.19kB buildkit.dockerfile.v0 4 days ago /bin/sh -c #(nop) CMD ["node"] 0B 4 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B\ 4 days ago /bin/sh -c #(nop) COPY file:4d192565a7220e13… 20.5kB\ 4 days ago /bin/sh -c apk add --no-cache --virtual .bui… 7.92MB\ 4 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.22.19 0B\ 4 days ago /bin/sh -c addgroup -g 1000 node && addu… 126MB\ 4 days ago /bin/sh -c #(nop) ENV NODE_VERSION=20.12.0 0B\ 2 months ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B\ 2 months ago /bin/sh -c #(nop) ADD file:d0764a717d1e9d0af… 8.42MB\ \ This output shows the layers of the image, highlighting the layers you added and those that were inherited from your base image.\ \ \ ### [Push the image](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#push-the-image)\ \ Now that you have an image built, it's time to push the image to a registry.\ \ 1. Push the image using the [docker push](https://docs.docker.com/reference/cli/docker/image/push/)\ command:\ \ $ docker push /concepts-build-image-demo\ \ \ If you receive a `requested access to the resource is denied`, make sure you are both logged in and that your Docker username is correct in the image tag.\ \ After a moment, your image should be pushed to Docker Hub.\ \ \ [Additional resources](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#additional-resources)\ \ -------------------------------------------------------------------------------------------------------------------------------------------------\ \ To learn more about building, tagging, and publishing images, visit the following resources:\ \ * [What is a build context?](https://docs.docker.com/build/concepts/context/#what-is-a-build-context)\ \ * [docker build reference](https://docs.docker.com/reference/cli/docker/buildx/build/)\ \ * [docker image tag reference](https://docs.docker.com/reference/cli/docker/image/tag/)\ \ * [docker push reference](https://docs.docker.com/reference/cli/docker/image/push/)\ \ * [What is a registry?](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-registry/)\ \ \ [Next steps](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/#next-steps)\ \ -----------------------------------------------------------------------------------------------------------------------------\ \ Now that you have learned about building and publishing images, it's time to learn how to speed up the build process using the Docker build cache.\ \ [Using the build cache](https://docs.docker.com/get-started/docker-concepts/building-images/using-the-build-cache/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Build a RAG application using Ollama and Docker](https://docs.docker.com/guides/rag-ollama/) This guide demonstrates how to use Docker to deploy Retrieval-Augmented Generation (RAG) models with Ollama. AI 20 minutes [1](https://docs.docker.com/guides/rag-ollama/containerize/) [Containerize your app](https://docs.docker.com/guides/rag-ollama/containerize/) [2](https://docs.docker.com/guides/rag-ollama/develop/) [Develop your app](https://docs.docker.com/guides/rag-ollama/develop/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a RAG application ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Overview](https://docs.docker.com/guides/rag-ollama/containerize/#overview) ----------------------------------------------------------------------------- This section walks you through containerizing a RAG application using Docker. > Note > > You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) > demo applications. [Get the sample application](https://docs.docker.com/guides/rag-ollama/containerize/#get-the-sample-application) ----------------------------------------------------------------------------------------------------------------- The sample application used in this guide is an example of RAG application, made by three main components, which are the building blocks for every RAG application. A Large Language Model hosted somewhere, in this case it is hosted in a container and served via [Ollama](https://ollama.ai/) . A vector database, [Qdrant](https://qdrant.tech/) , to store the embeddings of local data, and a web application, using [Streamlit](https://streamlit.io/) to offer the best user experience to the user. Clone the sample application. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/mfranzon/winy.git You should now have the following files in your `winy` directory. ├── winy/ │ ├── .gitignore │ ├── app/ │ │ ├── main.py │ │ ├── Dockerfile | | └── requirements.txt │ ├── tools/ │ │ ├── create_db.py │ │ ├── create_embeddings.py │ │ ├── requirements.txt │ │ ├── test.py | | └── download_model.sh │ ├── docker-compose.yaml │ ├── wine_database.db │ ├── LICENSE │ └── README.md [Containerizing your application: Essentials](https://docs.docker.com/guides/rag-ollama/containerize/#containerizing-your-application-essentials) -------------------------------------------------------------------------------------------------------------------------------------------------- Containerizing an application involves packaging it along with its dependencies into a container, which ensures consistency across different environments. Here’s what you need to containerize an app like Winy : 1. Dockerfile: A Dockerfile that contains instructions on how to build a Docker image for your application. It specifies the base image, dependencies, configuration files, and the command to run your application. 2. Docker Compose File: Docker Compose is a tool for defining and running multi-container Docker applications. A Compose file allows you to configure your application's services, networks, and volumes in a single file. [Run the application](https://docs.docker.com/guides/rag-ollama/containerize/#run-the-application) --------------------------------------------------------------------------------------------------- Inside the `winy` directory, run the following command in a terminal. $ docker compose up --build Docker builds and runs your application. Depending on your network connection, it may take several minutes to download all the dependencies. You'll see a message like the following in the terminal when the application is running. server-1 | You can now view your Streamlit app in your browser. server-1 | server-1 | URL: http://0.0.0.0:8501 server-1 | Open a browser and view the application at [http://localhost:8501](http://localhost:8501/) . You should see a simple Streamlit application. The application requires a Qdrant database service and an LLM service to work properly. If you have access to services that you ran outside of Docker, specify the connection information in the `docker-compose.yaml`. winy: build: context: ./app dockerfile: Dockerfile environment: - QDRANT_CLIENT=http://qdrant:6333 # Specifies the url for the qdrant database - OLLAMA=http://ollama:11434 # Specifies the url for the ollama service container_name: winy ports: - "8501:8501" depends_on: - qdrant - ollama If you don't have the services running, continue with this guide to learn how you can run some or all of these services with Docker. Remember that the `ollama` service is empty; it doesn't have any model. For this reason you need to pull a model before starting to use the RAG application. All the instructions are in the following page. In the terminal, press `ctrl`+`c` to stop the application. [Summary](https://docs.docker.com/guides/rag-ollama/containerize/#summary) --------------------------------------------------------------------------- In this section, you learned how you can containerize and run your RAG application using Docker. [Next steps](https://docs.docker.com/guides/rag-ollama/containerize/#next-steps) --------------------------------------------------------------------------------- In the next section, you'll learn how to properly configure the application with your preferred LLM model, completely locally, using Docker. [Use containers for RAG development »](https://docs.docker.com/guides/rag-ollama/develop/) --- # Sharing local files with containers | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Sharing local files with containers =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#explanation) ----------------------------------------------------------------------------------------------------------------------- Each container has everything it needs to function with no reliance on any pre-installed dependencies on the host machine. Since containers run in isolation, they have minimal influence on the host and other containers. This isolation has a major benefit: containers minimize conflicts with the host system and other containers. However, this isolation also means containers can't directly access data on the host machine by default. Consider a scenario where you have a web application container that requires access to configuration settings stored in a file on your host system. This file may contain sensitive data such as database credentials or API keys. Storing such sensitive information directly within the container image poses security risks, especially during image sharing. To address this challenge, Docker offers storage options that bridge the gap between container isolation and your host machine's data. Docker offers two primary storage options for persisting data and sharing files between the host machine and containers: volumes and bind mounts. ### [Volume versus bind mounts](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#volume-versus-bind-mounts) If you want to ensure that data generated or modified inside the container persists even after the container stops running, you would opt for a volume. See [Persisting container data](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/) to learn more about volumes and their use cases. If you have specific files or directories on your host system that you want to directly share with your container, like configuration files or development code, then you would use a bind mount. It's like opening a direct portal between your host and container for sharing. Bind mounts are ideal for development environments where real-time file access and sharing between the host and container are crucial. ### [Sharing files between a host and container](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#sharing-files-between-a-host-and-container) Both `-v` (or `--volume`) and `--mount` flags used with the `docker run` command let you share files or directories between your local machine (host) and a Docker container. However, there are some key differences in their behavior and usage. The `-v` flag is simpler and more convenient for basic volume or bind mount operations. If the host location doesn’t exist when using `-v` or `--volume`, a directory will be automatically created. Imagine you're a developer working on a project. You have a source directory on your development machine where your code resides. When you compile or build your code, the generated artifacts (compiled code, executables, images, etc.) are saved in a separate subdirectory within your source directory. In the following examples, this subdirectory is `/HOST/PATH`. Now you want these build artifacts to be accessible within a Docker container running your application. Additionally, you want the container to automatically access the latest build artifacts whenever you rebuild your code. Here's a way to use `docker run` to start a container using a bind mount and map it to the container file location. $ docker run -v /HOST/PATH:/CONTAINER/PATH -it nginx The `--mount` flag offers more advanced features and granular control, making it suitable for complex mount scenarios or production deployments. By default, if you use `--mount` to bind-mount a file or directory that doesn't yet exist on the Docker host, the `docker run` command doesn't automatically create it for you but generates an error. $ docker run --mount type=bind,source=/HOST/PATH,target=/CONTAINER/PATH,readonly nginx > Note > > Docker recommends using the `--mount` syntax instead of `-v`. It provides better control over the mounting process and avoids potential issues with missing directories. ### [File permissions for Docker access to host files](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#file-permissions-for-docker-access-to-host-files) When using bind mounts, it's crucial to ensure that Docker has the necessary permissions to access the host directory. To grant read/write access, you can use the `:ro` flag (read-only) or `:rw` (read-write) with the `-v` or `--mount` flag during container creation. For example, the following command grants read-write access permission. $ docker run -v HOST-DIRECTORY:/CONTAINER-DIRECTORY:rw nginx Read-only bind mounts let the container access the mounted files on the host for reading, but it can't change or delete the files. With read-write bind mounts, containers can modify or delete mounted files, and these changes or deletions will also be reflected on the host system. Read-only bind mounts ensures that files on the host can't be accidentally modified or deleted by a container. > **Synchronized File Share** > > As your codebase grows larger, traditional methods of file sharing like bind mounts may become inefficient or slow, especially in development environments where frequent access to files is necessary. [Synchronized file shares](https://docs.docker.com/desktop/features/synchronized-file-sharing/) > improve bind mount performance by leveraging synchronized filesystem caches. This optimization ensures that file access between the host and virtual machine (VM) is fast and efficient. [Try it out](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#try-it-out) --------------------------------------------------------------------------------------------------------------------- In this hands-on guide, you’ll practice how to create and use a bind mount to share files between a host and a container. ### [Run a container](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#run-a-container) 1. [Download and install](https://docs.docker.com/get-started/get-docker/) Docker Desktop. 2. Start a container using the [httpd](https://hub.docker.com/_/httpd) image with the following command: $ docker run -d -p 8080:80 --name my_site httpd:2.4 This will start the `httpd` service in the background, and publish the webpage to port `8080` on the host. 3. Open the browser and access [http://localhost:8080](http://localhost:8080/) or use the curl command to verify if it's working fine or not. $ curl localhost:8080 ### [Use a bind mount](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#use-a-bind-mount) Using a bind mount, you can map the configuration file on your host computer to a specific location within the container. In this example, you’ll see how to change the look and feel of the webpage by using bind mount: 1. Delete the existing container by using the Docker Desktop Dashboard: ![A screenshot of Docker Desktop Dashboard showing how to delete the httpd container](https://docs.docker.com/get-started/docker-concepts/running-containers/images/delete-httpd-container.webp) 2. Create a new directory called `public_html` on your host system. $ mkdir public_html 3. Navigate into the newly created directory `public_html` and create a file called `index.html` with the following content. This is a basic HTML document that creates a simple webpage that welcomes you with a friendly whale. My Website with a Whale & Docker!

Whalecome!!

Look! There's a friendly whale greeting you!

           ##         .
          ## ## ##        ==
         ## ## ## ## ##    ===
         /"""""""""""""""""\___/ ===
        {                       /  ===-
        \______ O           __/
        \    \         __/
         \____\_______/
        
        Hello from Docker!
        
4. It's time to run the container. The `--mount` and `-v` examples produce the same result. You can't run them both unless you remove the `my_site` container after running the first one. `-v` `--mount` $ docker run -d --name my_site -p 8080:80 -v .:/usr/local/apache2/htdocs/ httpd:2.4 $ docker run -d --name my_site -p 8080:80 --mount type=bind,source=./,target=/usr/local/apache2/htdocs/ httpd:2.4 > Tip > > When using the `-v` or `--mount` flag in Windows PowerShell, you need to provide the absolute path to your directory instead of just `./`. This is because PowerShell handles relative paths differently from bash (commonly used in Mac and Linux environments). With everything now up and running, you should be able to access the site via [http://localhost:8080](http://localhost:8080/) and find a new webpage that welcomes you with a friendly whale. ### [Access the file on the Docker Desktop Dashboard](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#access-the-file-on-the-docker-desktop-dashboard) 1. You can view the mounted files inside a container by selecting the container's **Files** tab and then selecting a file inside the `/usr/local/apache2/htdocs/` directory. Then, select **Open file editor**. ![A screenshot of Docker Desktop Dashboard showing the mounted files inside the a container](https://docs.docker.com/get-started/docker-concepts/running-containers/images/mounted-files.webp) 2. Delete the file on the host and verify the file is also deleted in the container. You will find that the files no longer exist under **Files** in the Docker Desktop Dashboard. ![A screenshot of Docker Desktop Dashboard showing the deleted files inside the a container](https://docs.docker.com/get-started/docker-concepts/running-containers/images/deleted-files.webp) 3. Recreate the HTML file on the host system and see that file re-appears under the **Files** tab under **Containers** on the Docker Desktop Dashboard. By now, you will be able to access the site too. ### [Stop your container](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#stop-your-container) The container continues to run until you stop it. 1. Go to the **Containers** view in the Docker Desktop Dashboard. 2. Locate the container you'd like to stop. 3. Select the **Stop** action in the Actions column. [Additional resources](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#additional-resources) ----------------------------------------------------------------------------------------------------------------------------------------- The following resources will help you learn more about bind mounts: * [Manage data in Docker](https://docs.docker.com/storage/) * [Volumes](https://docs.docker.com/storage/volumes/) * [Bind mounts](https://docs.docker.com/storage/bind-mounts/) * [Running containers](https://docs.docker.com/reference/run/) * [Troubleshoot storage errors](https://docs.docker.com/storage/troubleshooting_volume_errors/) * [Persisting container data](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/) [Next steps](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/#next-steps) --------------------------------------------------------------------------------------------------------------------- Now that you have learned about sharing local files with containers, it’s time to learn about multi-container applications. [Multi-container applications](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/) --- # Communication and information gathering | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Set up your company for success with Docker](https://docs.docker.com/guides/admin-set-up/) Get the most out of Docker by streamlining workflows, standardizing development environments, and ensuring smooth deployments across your company. Administration 20 minutes [1](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/) [Communication and information gathering](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/) [2](https://docs.docker.com/guides/admin-set-up/finalize-plans-and-setup/) [Finalize plans and begin setup](https://docs.docker.com/guides/admin-set-up/finalize-plans-and-setup/) [3](https://docs.docker.com/guides/admin-set-up/testing/) [Testing](https://docs.docker.com/guides/admin-set-up/testing/) [4](https://docs.docker.com/guides/admin-set-up/deploy/) [Deploy your Docker setup](https://docs.docker.com/guides/admin-set-up/deploy/) Resources: * [Overview of Administration in Docker](https://docs.docker.com/admin/) * [Single sign-on](https://docs.docker.com/security/for-admins/single-sign-on/) * [Enforce sign-in](https://docs.docker.com/security/for-admins/enforce-sign-in/) * [Roles and permissions](https://docs.docker.com/security/for-admins/roles-and-permissions/) * [Settings Management](https://docs.docker.com/security/for-admins/hardened-desktop/settings-management/) * [Registry Access Management](https://docs.docker.com/security/for-admins/hardened-desktop/registry-access-management/) * [Image Access Management](https://docs.docker.com/security/for-admins/hardened-desktop/image-access-management/) * [Docker subscription information](https://www.docker.com/pricing?ref=Docs&refAction=DocsGuidesAdminSetup) [« Back to all guides](https://docs.docker.com/guides/) Communication and information gathering ======================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Communicate with your developers and IT teams](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/#communicate-with-your-developers-and-it-teams) --------------------------------------------------------------------------------------------------------------------------------------------------------------------- Before rolling out Docker Desktop across your organization, coordinate with key stakeholders to ensure a smooth transition. ### [Notify Docker Desktop users](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/#notify-docker-desktop-users) You may already have Docker Desktop users within your company. Some steps in this onboarding process may affect how they interact with the platform. Communicate early with users to inform them that: * They'll be upgraded to a supported version of Docker Desktop as part of the subscription onboarding * Settings will be reviewed and optimized for productivity * They'll need to sign in to the company's Docker organization using their business email to access subscription benefits ### [Engage with your MDM team](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/#engage-with-your-mdm-team) Device management solutions, such as Intune and Jamf, are commonly used for software distribution across enterprises. These tools are typically managed by a dedicated MDM team. Engage with this team early in the process to: * Understand their requirements and lead time for deploying changes * Coordinate the distribution of configuration files Several setup steps in this guide require JSON files, registry keys, or .plist files to be distributed to developer machines. Use MDM tools to deploy these configuration files and ensure their integrity. [Identify Docker organizations](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/#identify-docker-organizations) ------------------------------------------------------------------------------------------------------------------------------------- Some companies may have more than one [Docker organization](https://docs.docker.com/admin/organization/) created. These organizations may have been created for specific purposes, or may not be needed anymore. If you suspect your company has multiple Docker organizations: * Survey your teams to see if they have their own organizations * Contact your Docker Support to get a list of organizations with users whose emails match your domain name [Gather requirements](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/#gather-requirements) ----------------------------------------------------------------------------------------------------------------- [Settings Management](https://docs.docker.com/enterprise/security/hardened-desktop/settings-management/) lets you preset numerous configuration parameters for Docker Desktop. Work with the following stakeholders to establish your company's baseline configuration: * Docker organization owner * Development lead * Information security representative Review these areas together: * Security features and [enforcing sign-in](https://docs.docker.com/enterprise/security/enforce-sign-in/) for Docker Desktop users * Additional Docker products included in your subscriptions To view the parameters that can be preset, see [Configure Settings Management](https://docs.docker.com/enterprise/security/hardened-desktop/settings-management/configure-json-file/#step-two-configure-the-settings-you-want-to-lock-in) . [Optional: Meet with the Docker Implementation team](https://docs.docker.com/guides/admin-set-up/comms-and-info-gathering/#optional-meet-with-the-docker-implementation-team) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ The Docker Implementation team can help you set up your organization, configure SSO, enforce sign-in, and configure Docker Desktop. To schedule a meeting, email [successteam@docker.com](mailto:successteam@docker.com) . [Finalize plans and begin setup »](https://docs.docker.com/guides/admin-set-up/finalize-plans-and-setup/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Bun language-specific guide](https://docs.docker.com/guides/bun/) Learn how to containerize JavaScript applications with the Bun runtime. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Docker Hardened Images 10 minutes [1](https://docs.docker.com/guides/bun/containerize/) [Containerize your app](https://docs.docker.com/guides/bun/containerize/) [2](https://docs.docker.com/guides/bun/develop/) [Develop your app](https://docs.docker.com/guides/bun/develop/) [3](https://docs.docker.com/guides/bun/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/bun/configure-ci-cd/) [4](https://docs.docker.com/guides/bun/deploy/) [Test your deployment](https://docs.docker.com/guides/bun/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Bun application ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/bun/containerize/#prerequisites) -------------------------------------------------------------------------------- * You have a [Git client](https://git-scm.com/downloads) . The examples in this section use a command-line based Git client, but you can use any client. [Overview](https://docs.docker.com/guides/bun/containerize/#overview) ---------------------------------------------------------------------- For a long time, Node.js has been the de-facto runtime for server-side JavaScript applications. Recent years have seen a rise in new alternative runtimes in the ecosystem, including [Bun website](https://bun.sh/) . Like Node.js, Bun is a JavaScript runtime. Bun is a comparatively lightweight runtime that is designed to be fast and efficient. Why develop Bun applications with Docker? Having multiple runtimes to choose from is great. But as the number of runtimes increases, it becomes challenging to manage the different runtimes and their dependencies consistently across environments. This is where Docker comes in. Creating and destroying containers on demand is a great way to manage the different runtimes and their dependencies. Also, as it's fairly a new runtime, getting a consistent development environment for Bun can be challenging. Docker can help you set up a consistent development environment for Bun. [Get the sample application](https://docs.docker.com/guides/bun/containerize/#get-the-sample-application) ---------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/dockersamples/bun-docker.git && cd bun-docker You should now have the following contents in your `bun-docker` directory. ├── bun-docker/ │ ├── compose.yml │ ├── Dockerfile │ ├── LICENSE │ ├── server.js │ └── README.md [Create a Dockerfile](https://docs.docker.com/guides/bun/containerize/#create-a-dockerfile) -------------------------------------------------------------------------------------------- Before creating a Dockerfile, you need to choose a base image. You can either use the [Bun Docker Official Image](https://hub.docker.com/r/oven/bun) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog) . Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/) . Using Docker Hardened Images Using the official image Docker Hardened Images (DHIs) are available for Bun in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/bun) . You can pull DHIs directly from the `dhi.io` registry. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Bun DHI as `dhi.io/bun:1`. The tag (`1`) in this example refers to the version to the latest 1.x version of Bun. $ docker pull dhi.io/bun:1 For other available versions, refer to the [catalog](https://hub.docker.com/hardened-images/catalog/dhi/bun) . # Use the DHI Bun image as the base image FROM dhi.io/bun:1 # Set the working directory in the container WORKDIR /app # Copy the current directory contents into the container at /app COPY . . # Expose the port on which the API will listen EXPOSE 3000 # Run the server when the container launches CMD ["bun", "server.js"] Using the Docker Official Image is straightforward. In the following Dockerfile, you'll notice that the `FROM` instruction uses `oven/bun` as the base image. You can find the image on [Docker Hub](https://hub.docker.com/r/oven/bun) . This is the Docker Official Image for Bun created by Oven, the company behind Bun, and it's available on Docker Hub. # Use the official Bun image FROM oven/bun:latest # Set the working directory in the container WORKDIR /app # Copy the current directory contents into the container at /app COPY . . # Expose the port on which the API will listen EXPOSE 3000 # Run the server when the container launches CMD ["bun", "server.js"] In addition to specifying the base image, the Dockerfile also: * Sets the working directory in the container to `/app`. * Copies the content of the current directory to the `/app` directory in the container. * Exposes port 3000, where the API is listening for requests. * And finally, starts the server when the container launches with the command `bun server.js`. [Run the application](https://docs.docker.com/guides/bun/containerize/#run-the-application) -------------------------------------------------------------------------------------------- Inside the `bun-docker` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:3000](http://localhost:3000/) . You will see a message `{"Status" : "OK"}` in the browser. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/bun/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `bun-docker` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:3000](http://localhost:3000/) . In the terminal, run the following command to stop the application. $ docker compose down [Summary](https://docs.docker.com/guides/bun/containerize/#summary) -------------------------------------------------------------------- In this section, you learned how you can containerize and run your Bun application using Docker. Related information: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [.dockerignore file](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [Docker Compose overview](https://docs.docker.com/compose/) * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Docker Hardened Images](https://docs.docker.com/dhi/) [Next steps](https://docs.docker.com/guides/bun/containerize/#next-steps) -------------------------------------------------------------------------- In the next section, you'll learn how you can develop your application using containers. [Use containers for Bun development »](https://docs.docker.com/guides/bun/develop/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [PDF analysis and chat](https://docs.docker.com/guides/genai-pdf-bot/) Learn how to build a PDF bot for parsing PDF documents and generating responses using Docker and generative AI. AI 20 minutes [1](https://docs.docker.com/guides/genai-pdf-bot/containerize/) [Containerize your app](https://docs.docker.com/guides/genai-pdf-bot/containerize/) [2](https://docs.docker.com/guides/genai-pdf-bot/develop/) [Develop your app](https://docs.docker.com/guides/genai-pdf-bot/develop/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a generative AI application ======================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/genai-pdf-bot/containerize/#prerequisites) ------------------------------------------------------------------------------------------ > Note > > GenAI applications can often benefit from GPU acceleration. Currently Docker Desktop supports GPU acceleration only on [Windows with the WSL2 backend](https://docs.docker.com/desktop/features/gpu/#using-nvidia-gpus-with-wsl2) > . Linux users can also access GPU acceleration using a native installation of the [Docker Engine](https://docs.docker.com/engine/install/) > . * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) or, if you are a Linux user and are planning to use GPU acceleration, [Docker Engine](https://docs.docker.com/engine/install/) . Docker adds new features regularly and some parts of this guide may work only with the latest version of Docker Desktop. * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. [Overview](https://docs.docker.com/guides/genai-pdf-bot/containerize/#overview) -------------------------------------------------------------------------------- This section walks you through containerizing a generative AI (GenAI) application using Docker Desktop. > Note > > You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) > demo applications. [Get the sample application](https://docs.docker.com/guides/genai-pdf-bot/containerize/#get-the-sample-application) -------------------------------------------------------------------------------------------------------------------- The sample application used in this guide is a modified version of the PDF Reader application from the [GenAI Stack](https://github.com/docker/genai-stack) demo applications. The application is a full stack Python application that lets you ask questions about a PDF file. The application uses [LangChain](https://www.langchain.com/) for orchestration, [Streamlit](https://streamlit.io/) for the UI, [Ollama](https://ollama.ai/) to run the LLM, and [Neo4j](https://neo4j.com/) to store vectors. Clone the sample application. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/craig-osterhout/docker-genai-sample You should now have the following files in your `docker-genai-sample` directory. ├── docker-genai-sample/ │ ├── .gitignore │ ├── app.py │ ├── chains.py │ ├── env.example │ ├── requirements.txt │ ├── util.py │ ├── LICENSE │ └── README.md [Initialize Docker assets](https://docs.docker.com/guides/genai-pdf-bot/containerize/#initialize-docker-assets) ---------------------------------------------------------------------------------------------------------------- Now that you have an application, you can use `docker init` to create the necessary Docker assets to containerize your application. Inside the `docker-genai-sample` directory, run the `docker init` command. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. For example, this application uses Streamlit to run. Refer to the following `docker init` example and use the same answers for your prompts. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? Python ? What version of Python do you want to use? 3.11.4 ? What port do you want your app to listen on? 8000 ? What is the command to run your app? streamlit run app.py --server.address=0.0.0.0 --server.port=8000 You should now have the following contents in your `docker-genai-sample` directory. ├── docker-genai-sample/ │ ├── .dockerignore │ ├── .gitignore │ ├── app.py │ ├── chains.py │ ├── compose.yaml │ ├── env.example │ ├── requirements.txt │ ├── util.py │ ├── Dockerfile │ ├── LICENSE │ ├── README.Docker.md │ └── README.md To learn more about the files that `docker init` added, see the following: * [Dockerfile](https://docs.docker.com/reference/dockerfile/) * [.dockerignore](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [compose.yaml](https://docs.docker.com/reference/compose-file/) [Run the application](https://docs.docker.com/guides/genai-pdf-bot/containerize/#run-the-application) ------------------------------------------------------------------------------------------------------ Inside the `docker-genai-sample` directory, run the following command in a terminal. $ docker compose up --build Docker builds and runs your application. Depending on your network connection, it may take several minutes to download all the dependencies. You'll see a message like the following in the terminal when the application is running. server-1 | You can now view your Streamlit app in your browser. server-1 | server-1 | URL: http://0.0.0.0:8000 server-1 | Open a browser and view the application at [http://localhost:8000](http://localhost:8000/) . You should see a simple Streamlit application. The application may take a few minutes to download the embedding model. While the download is in progress, **Running** appears in the top-right corner. The application requires a Neo4j database service and an LLM service to function. If you have access to services that you ran outside of Docker, specify the connection information and try it out. If you don't have the services running, continue with this guide to learn how you can run some or all of these services with Docker. In the terminal, press `ctrl`+`c` to stop the application. [Summary](https://docs.docker.com/guides/genai-pdf-bot/containerize/#summary) ------------------------------------------------------------------------------ In this section, you learned how you can containerize and run your GenAI application using Docker. Related information: * [docker init CLI reference](https://docs.docker.com/reference/cli/docker/init/) [Next steps](https://docs.docker.com/guides/genai-pdf-bot/containerize/#next-steps) ------------------------------------------------------------------------------------ In the next section, you'll learn how you can run your application, database, and LLM service all locally using Docker. [Use containers for generative AI development »](https://docs.docker.com/guides/genai-pdf-bot/develop/) --- # Copy files | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Configuration of services running in a container](https://docs.docker.com/guides/testcontainers-java-service-configuration/) Learn how to initialize and configure Docker containers for testing by copying files into containers and executing commands inside them. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-java-service-configuration/copy-files/) [Copy files](https://docs.docker.com/guides/testcontainers-java-service-configuration/copy-files/) [2](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/) [Execute commands](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/) [« Back to all guides](https://docs.docker.com/guides/) Copy files into containers ========================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Sometimes you need to initialize a container by placing files in a specific location. For example, PostgreSQL runs SQL scripts from `/docker-entrypoint-initdb.d/` when the container starts. [Create the initialization script](https://docs.docker.com/guides/testcontainers-java-service-configuration/copy-files/#create-the-initialization-script) ---------------------------------------------------------------------------------------------------------------------------------------------------------- Create `src/test/resources/init-db.sql`: create table customers ( id bigint not null, name varchar not null, primary key (id) ); [Copy the file into the container](https://docs.docker.com/guides/testcontainers-java-service-configuration/copy-files/#copy-the-file-into-the-container) ---------------------------------------------------------------------------------------------------------------------------------------------------------- Use `withCopyFileToContainer()` to copy the SQL script into the container's init directory: package com.testcontainers.demo; import static org.junit.jupiter.api.Assertions.assertFalse; import java.util.List; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.postgresql.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.MountableFile; @Testcontainers class CustomerServiceTest { @Container static PostgreSQLContainer postgres = new PostgreSQLContainer( "postgres:16-alpine" ) .withCopyFileToContainer( MountableFile.forClasspathResource("init-db.sql"), "/docker-entrypoint-initdb.d/" ); CustomerService customerService; @BeforeEach void setUp() { customerService = new CustomerService( postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword() ); } @Test void shouldGetCustomers() { customerService.createCustomer(new Customer(1L, "George")); customerService.createCustomer(new Customer(2L, "John")); List customers = customerService.getAllCustomers(); assertFalse(customers.isEmpty()); } } The `withCopyFileToContainer(MountableFile, String)` method copies `init-db.sql` from the classpath into `/docker-entrypoint-initdb.d/` inside the container. PostgreSQL executes scripts in that directory automatically at startup. You can also copy files from any path on the host: .withCopyFileToContainer( MountableFile.forHostPath("/host/path/to/init-db.sql"), "/docker-entrypoint-initdb.d/" ); [Execute commands inside containers »](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/) --- # Using the build cache | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Using the build cache ===================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/building-images/using-the-build-cache/#explanation) ---------------------------------------------------------------------------------------------------------------------- Consider the following Dockerfile that you created for the [getting-started](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/) app. FROM node:22-alpine WORKDIR /app COPY . . RUN yarn install --production CMD ["node", "./src/index.js"] When you run the `docker build` command to create a new image, Docker executes each instruction in your Dockerfile, creating a layer for each command and in the order specified. For each instruction, Docker checks whether it can reuse the instruction from a previous build. If it finds that you've already executed a similar instruction before, Docker doesn't need to redo it. Instead, it’ll use the cached result. This way, your build process becomes faster and more efficient, saving you valuable time and resources. Using the build cache effectively lets you achieve faster builds by reusing results from previous builds and skipping unnecessary work. In order to maximize cache usage and avoid resource-intensive and time-consuming rebuilds, it's important to understand how cache invalidation works. Here are a few examples of situations that can cause cache to be invalidated: * Any changes to the command of a `RUN` instruction invalidates that layer. Docker detects the change and invalidates the build cache if there's any modification to a `RUN` command in your Dockerfile. * Any changes to files copied into the image with the `COPY` or `ADD` instructions. Docker keeps an eye on any alterations to files within your project directory. Whether it's a change in content or properties like permissions, Docker considers these modifications as triggers to invalidate the cache. * Once one layer is invalidated, all following layers are also invalidated. If any previous layer, including the base image or intermediary layers, has been invalidated due to changes, Docker ensures that subsequent layers relying on it are also invalidated. This keeps the build process synchronized and prevents inconsistencies. When you're writing or editing a Dockerfile, keep an eye out for unnecessary cache misses to ensure that builds run as fast and efficiently as possible. [Try it out](https://docs.docker.com/get-started/docker-concepts/building-images/using-the-build-cache/#try-it-out) -------------------------------------------------------------------------------------------------------------------- In this hands-on guide, you will learn how to use the Docker build cache effectively for a Node.js application. ### [Build the application](https://docs.docker.com/get-started/docker-concepts/building-images/using-the-build-cache/#build-the-application) 1. [Download and install](https://www.docker.com/products/docker-desktop/) Docker Desktop. 2. Open a terminal and [clone this sample application](https://github.com/dockersamples/todo-list-app) . $ git clone https://github.com/dockersamples/todo-list-app 3. Navigate into the `todo-list-app` directory: $ cd todo-list-app Inside this directory, you'll find a file named `Dockerfile` with the following content: FROM node:22-alpine WORKDIR /app COPY . . RUN yarn install --production EXPOSE 3000 CMD ["node", "./src/index.js"] 4. Execute the following command to build the Docker image: $ docker build . Here’s the result of the build process: [+] Building 20.0s (10/10) FINISHED The first line indicates that the entire build process took _20.0 seconds_. The first build may take some time as it installs dependencies. 5. Rebuild without making changes. Now, re-run the `docker build` command without making any change in the source code or Dockerfile as shown: $ docker build . Subsequent builds after the initial are faster due to the caching mechanism, as long as the commands and context remain unchanged. Docker caches the intermediate layers generated during the build process. When you rebuild the image without making any changes to the Dockerfile or the source code, Docker can reuse the cached layers, significantly speeding up the build process. [+] Building 1.0s (9/9) FINISHED docker:desktop-linux => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 187B 0.0s ... => [internal] load build context 0.0s => => transferring context: 8.16kB 0.0s => CACHED [2/4] WORKDIR /app 0.0s => CACHED [3/4] COPY . . 0.0s => CACHED [4/4] RUN yarn install --production 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => exporting manifest 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. | Steps | Description | Time Taken (1st Run) | Time Taken (2nd Run) | | --- | --- | --- | --- | | 1 | `Load build definition from Dockerfile` | 0.0 seconds | 0.0 seconds | | 2 | `Load metadata for docker.io/library/node:22-alpine` | 2.7 seconds | 0.9 seconds | | 3 | `Load .dockerignore` | 0.0 seconds | 0.0 seconds | | 4 | `Load build context`

(Context size: 4.60MB) | 0.1 seconds | 0.0 seconds | | 5 | `Set the working directory (WORKDIR)` | 0.1 seconds | 0.0 seconds | | 6 | `Copy the local code into the container` | 0.0 seconds | 0.0 seconds | | 7 | `Run yarn install --production` | 10.0 seconds | 0.0 seconds | | 8 | `Exporting layers` | 2.2 seconds | 0.0 seconds | | 9 | `Exporting the final image` | 3.0 seconds | 0.0 seconds | 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. FROM node:22-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. node_modules 8. Build the new image: $ docker build . You'll then see output similar to the following: [+] 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:22-alpine 0.0s => [internal] load build context 0.8s => => transferring context: 53.37MB 0.8s => [1/5] FROM docker.io/library/node:22-alpine 0.0s => CACHED [2/5] WORKDIR /app 0.0s => [3/5] COPY package.json yarn.lock ./ 0.2s => [4/5] RUN yarn install --production 14.0s => [5/5] COPY . . 0.5s => exporting to image 0.6s => => exporting layers 0.6s => => writing image sha256:d6f819013566c54c50124ed94d5e66c452325327217f4f04399b45f94e37d25 0.0s => => naming to docker.io/library/node-app:2.0 0.0s You'll see that all layers were rebuilt. Perfectly fine since you changed the Dockerfile quite a bit. 9. Now, make a change to the `src/static/index.html` file (like change the title to say "The Awesome Todo App"). 10. Build the Docker image. This time, your output should look a little different. $ docker build -t node-app:3.0 . You'll then see output similar to the following: [+] Building 1.2s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 37B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/node:22-alpine 0.0s => [internal] load build context 0.2s => => transferring context: 450.43kB 0.2s => [1/5] FROM docker.io/library/node:22-alpine 0.0s => CACHED [2/5] WORKDIR /app 0.0s => CACHED [3/5] COPY package.json yarn.lock ./ 0.0s => CACHED [4/5] RUN yarn install --production 0.0s => [5/5] COPY . . 0.5s => exporting to image 0.3s => => exporting layers 0.3s => => writing image sha256:91790c87bcb096a83c2bd4eb512bc8b134c757cda0bdee4038187f98148e2eda 0.0s => => naming to docker.io/library/node-app:3.0 0.0s First off, you should notice that the build was much faster. You'll see that several steps are using previously cached layers. That's good news; you're using the build cache. Pushing and pulling this image and updates to it will be much faster as well. By following these optimization techniques, you can make your Docker builds faster and more efficient, leading to quicker iteration cycles and improved development productivity. [Additional resources](https://docs.docker.com/get-started/docker-concepts/building-images/using-the-build-cache/#additional-resources) ---------------------------------------------------------------------------------------------------------------------------------------- * [Optimizing builds with cache management](https://docs.docker.com/build/cache/) * [Cache Storage Backend](https://docs.docker.com/build/cache/backends/) * [Build cache invalidation](https://docs.docker.com/build/cache/invalidation/) [Next steps](https://docs.docker.com/get-started/docker-concepts/building-images/using-the-build-cache/#next-steps) -------------------------------------------------------------------------------------------------------------------- Now that you understand how to use the Docker build cache effectively, you're ready to learn about Multi-stage builds. [Multi-stage builds](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [R language-specific guide](https://docs.docker.com/guides/r/) This guide details how to containerize R applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/r/r-original.svg "R") R 10 minutes [1](https://docs.docker.com/guides/r/containerize/) [Containerize your app](https://docs.docker.com/guides/r/containerize/) [2](https://docs.docker.com/guides/r/develop/) [Develop your app](https://docs.docker.com/guides/r/develop/) [3](https://docs.docker.com/guides/r/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/r/configure-ci-cd/) [4](https://docs.docker.com/guides/r/deploy/) [Test your deployment](https://docs.docker.com/guides/r/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a R application ============================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/r/containerize/#prerequisites) ------------------------------------------------------------------------------ * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. [Overview](https://docs.docker.com/guides/r/containerize/#overview) -------------------------------------------------------------------- This section walks you through containerizing and running a R application. [Get the sample application](https://docs.docker.com/guides/r/containerize/#get-the-sample-application) -------------------------------------------------------------------------------------------------------- The sample application uses the popular [Shiny](https://shiny.posit.co/) framework. Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/mfranzon/r-docker-dev.git && cd r-docker-dev You should now have the following contents in your `r-docker-dev` directory. ├── r-docker-dev/ │ ├── src/ │ │ └── app.R │ ├── src_db/ │ │ └── app_db.R │ ├── compose.yaml │ ├── Dockerfile │ └── README.md To learn more about the files in the repository, see the following: * [Dockerfile](https://docs.docker.com/reference/dockerfile/) * [.dockerignore](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [compose.yaml](https://docs.docker.com/reference/compose-file/) [Run the application](https://docs.docker.com/guides/r/containerize/#run-the-application) ------------------------------------------------------------------------------------------ Inside the `r-docker-dev` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:3838](http://localhost:3838/) . You should see a simple Shiny application. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/r/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `r-docker-dev` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:3838](http://localhost:3838/) . You should see a simple Shiny application. In the terminal, run the following command to stop the application. $ docker compose down For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) . [Summary](https://docs.docker.com/guides/r/containerize/#summary) ------------------------------------------------------------------ In this section, you learned how you can containerize and run your R application using Docker. Related information: * [Docker Compose overview](https://docs.docker.com/compose/) [Next steps](https://docs.docker.com/guides/r/containerize/#next-steps) ------------------------------------------------------------------------ In the next section, you'll learn how you can develop your application using containers. [Use containers for R development »](https://docs.docker.com/guides/r/develop/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Deno language-specific guide](https://docs.docker.com/guides/deno/) Learn how to containerize JavaScript applications with the Deno runtime using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Docker Hardened Images 10 minutes [1](https://docs.docker.com/guides/deno/containerize/) [Containerize your app](https://docs.docker.com/guides/deno/containerize/) [2](https://docs.docker.com/guides/deno/develop/) [Develop your app](https://docs.docker.com/guides/deno/develop/) [3](https://docs.docker.com/guides/deno/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/deno/configure-ci-cd/) [4](https://docs.docker.com/guides/deno/deploy/) [Test your deployment](https://docs.docker.com/guides/deno/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Deno application =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/deno/containerize/#prerequisites) --------------------------------------------------------------------------------- * You have a [Git client](https://git-scm.com/downloads) . The examples in this section use a command-line based Git client, but you can use any client. [Overview](https://docs.docker.com/guides/deno/containerize/#overview) ----------------------------------------------------------------------- For a long time, Node.js has been the go-to runtime for server-side JavaScript applications. However, recent years have introduced new alternative runtimes, including [Deno](https://deno.land/) . Like Node.js, Deno is a JavaScript and TypeScript runtime, but it takes a fresh approach with modern security features, a built-in standard library, and native support for TypeScript. Why develop Deno applications with Docker? Having a choice of runtimes is exciting, but managing multiple runtimes and their dependencies consistently across environments can be tricky. This is where Docker proves invaluable. Using containers to create and destroy environments on demand simplifies runtime management and ensures consistency. Additionally, as Deno continues to grow and evolve, Docker helps establish a reliable and reproducible development environment, minimizing setup challenges and streamlining the workflow. [Get the sample application](https://docs.docker.com/guides/deno/containerize/#get-the-sample-application) ----------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/dockersamples/docker-deno.git && cd docker-deno You should now have the following contents in your `deno-docker` directory. ├── deno-docker/ │ ├── compose.yml │ ├── Dockerfile │ ├── LICENSE │ ├── server.ts │ └── README.md [Understand the sample application](https://docs.docker.com/guides/deno/containerize/#understand-the-sample-application) ------------------------------------------------------------------------------------------------------------------------- The sample application is a simple Deno application that uses the Oak framework to create a simple API that returns a JSON response. The application listens on port 8000 and returns a message `{"Status" : "OK"}` when you access the application in a browser. // server.ts import { Application, Router } from "https://deno.land/x/oak@v12.0.0/mod.ts"; const app = new Application(); const router = new Router(); // Define a route that returns JSON router.get("/", (context) => { context.response.body = { Status: "OK" }; context.response.type = "application/json"; }); app.use(router.routes()); app.use(router.allowedMethods()); console.log("Server running on http://localhost:8000"); await app.listen({ port: 8000 }); [Create a Dockerfile](https://docs.docker.com/guides/deno/containerize/#create-a-dockerfile) --------------------------------------------------------------------------------------------- Before creating a Dockerfile, you need to choose a base image. You can either use the [Deno Docker Official Image](https://hub.docker.com/r/denoland/deno) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog) . Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/) . Using Docker Hardened Images Using the official image Docker Hardened Images (DHIs) are available for Deno in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/deno) . You can pull DHIs directly from the `dhi.io` registry. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Deno DHI as `dhi.io/deno:2`. The tag (`2`) in this example refers to the version to the latest 2.x version of Deno. $ docker pull dhi.io/deno:2 For other available versions, refer to the [catalog](https://hub.docker.com/hardened-images/catalog/dhi/deno) . # Use the DHI Deno image as the base image FROM dhi.io/deno:2 # Set the working directory WORKDIR /app # Copy server code into the container COPY server.ts . # Set permissions (optional but recommended for security) USER deno # Expose port 8000 EXPOSE 8000 # Run the Deno server CMD ["run", "--allow-net", "server.ts"] Using the Docker Official Image is straightforward. In the following Dockerfile, you'll notice that the `FROM` instruction uses `denoland/deno:latest` as the base image. This is the official image for Deno. This image is [available on the Docker Hub](https://hub.docker.com/r/denoland/deno) . # Use the official Deno image FROM denoland/deno:latest # Set the working directory WORKDIR /app # Copy server code into the container COPY server.ts . # Set permissions (optional but recommended for security) USER deno # Expose port 8000 EXPOSE 8000 # Run the Deno server CMD ["run", "--allow-net", "server.ts"] In addition to specifying the base image, the Dockerfile also: * Sets the working directory in the container to `/app`. * Copies `server.ts` into the container. * Sets the user to `deno` to run the application as a non-root user. * Exposes port 8000 to allow traffic to the application. * Runs the Deno server using the `CMD` instruction. * Uses the `--allow-net` flag to allow network access to the application. The `server.ts` file uses the Oak framework to create a simple API that listens on port 8000. [Run the application](https://docs.docker.com/guides/deno/containerize/#run-the-application) --------------------------------------------------------------------------------------------- Make sure you are in the `deno-docker` directory. Run the following command in a terminal to build and run the application. $ docker compose up --build Open a browser and view the application at [http://localhost:8000](http://localhost:8000/) . You will see a message `{"Status" : "OK"}` in the browser. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/deno/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `deno-docker` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8000](http://localhost:8000/) . In the terminal, run the following command to stop the application. $ docker compose down [Summary](https://docs.docker.com/guides/deno/containerize/#summary) --------------------------------------------------------------------- In this section, you learned how you can containerize and run your Deno application using Docker. Related information: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [.dockerignore file](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [Docker Compose overview](https://docs.docker.com/compose/) * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Docker Hardened Images](https://docs.docker.com/dhi/) [Next steps](https://docs.docker.com/guides/deno/containerize/#next-steps) --------------------------------------------------------------------------- In the next section, you'll learn how you can develop your application using containers. [Use containers for Deno development »](https://docs.docker.com/guides/deno/develop/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [PHP language-specific guide](https://docs.docker.com/guides/php/) This guide explains how to containerize PHP applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/php/php-original.svg "PHP") PHP 20 minutes [1](https://docs.docker.com/guides/php/containerize/) [Containerize your app](https://docs.docker.com/guides/php/containerize/) [2](https://docs.docker.com/guides/php/develop/) [Develop your app](https://docs.docker.com/guides/php/develop/) [3](https://docs.docker.com/guides/php/run-tests/) [Run your tests](https://docs.docker.com/guides/php/run-tests/) [4](https://docs.docker.com/guides/php/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/php/configure-ci-cd/) [5](https://docs.docker.com/guides/php/deploy/) [Test your deployment](https://docs.docker.com/guides/php/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a PHP application ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/php/containerize/#prerequisites) -------------------------------------------------------------------------------- * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. [Overview](https://docs.docker.com/guides/php/containerize/#overview) ---------------------------------------------------------------------- This section walks you through containerizing and running a PHP application. [Get the sample applications](https://docs.docker.com/guides/php/containerize/#get-the-sample-applications) ------------------------------------------------------------------------------------------------------------ In this guide, you will use a pre-built PHP application. The application uses Composer for library dependency management. You'll serve the application via an Apache web server. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository. $ git clone https://github.com/docker/docker-php-sample The sample application is a basic hello world application and an application that increments a counter in a database. In addition, the application uses PHPUnit for testing. [Initialize Docker assets](https://docs.docker.com/guides/php/containerize/#initialize-docker-assets) ------------------------------------------------------------------------------------------------------ Now that you have an application, you can use `docker init` to create the necessary Docker assets to containerize your application. Inside the `docker-php-sample` directory, run the `docker init` command in a terminal. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. For example, this application uses PHP version 8.2. Refer to the following `docker init` example and use the same answers for your prompts. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? PHP with Apache ? What version of PHP do you want to use? 8.2 ? What's the relative directory (with a leading .) for your app? ./src ? What local port do you want to use to access your server? 9000 You should now have the following contents in your `docker-php-sample` directory. ├── docker-php-sample/ │ ├── .git/ │ ├── src/ │ ├── tests/ │ ├── .dockerignore │ ├── .gitignore │ ├── compose.yaml │ ├── composer.json │ ├── composer.lock │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md To learn more about the files that `docker init` added, see the following: * [Dockerfile](https://docs.docker.com/reference/dockerfile/) * [.dockerignore](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [compose.yaml](https://docs.docker.com/reference/compose-file/) [Run the application](https://docs.docker.com/guides/php/containerize/#run-the-application) -------------------------------------------------------------------------------------------- Inside the `docker-php-sample` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:9000/hello.php](http://localhost:9000/hello.php) . You should see a simple hello world application. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/php/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-php-sample` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:9000/hello.php](http://localhost:9000/hello.php) . You should see a simple hello world application. In the terminal, run the following command to stop the application. $ docker compose down For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) . [Summary](https://docs.docker.com/guides/php/containerize/#summary) -------------------------------------------------------------------- In this section, you learned how you can containerize and run a simple PHP application using Docker. Related information: * [docker init reference](https://docs.docker.com/reference/cli/docker/init/) [Next steps](https://docs.docker.com/guides/php/containerize/#next-steps) -------------------------------------------------------------------------- In the next section, you'll learn how you can develop your application using Docker containers. [Use containers for PHP development »](https://docs.docker.com/guides/php/develop/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Go](https://docs.docker.com/guides/testcontainers-go-getting-started/) Learn how to create a Go application and test database interactions using Testcontainers for Go with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg "Go") Go Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-go-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-go-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-go-getting-started/test-suites/) [Test suites](https://docs.docker.com/guides/testcontainers-go-getting-started/test-suites/) [4](https://docs.docker.com/guides/testcontainers-go-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-go-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Go project ===================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Initialize the project](https://docs.docker.com/guides/testcontainers-go-getting-started/create-project/#initialize-the-project) ---------------------------------------------------------------------------------------------------------------------------------- Start by creating a Go project. $ mkdir testcontainers-go-demo $ cd testcontainers-go-demo $ go mod init github.com/testcontainers/testcontainers-go-demo This guide uses the [jackc/pgx](https://github.com/jackc/pgx) PostgreSQL driver to interact with the Postgres database and the testcontainers-go [Postgres module](https://golang.testcontainers.org/modules/postgres/) to spin up a Postgres Docker instance for testing. It also uses [testify](https://github.com/stretchr/testify) for running multiple tests as a suite and for writing assertions. Install these dependencies: $ go get github.com/jackc/pgx/v5 $ go get github.com/testcontainers/testcontainers-go $ go get github.com/testcontainers/testcontainers-go/modules/postgres $ go get github.com/stretchr/testify [Create Customer struct](https://docs.docker.com/guides/testcontainers-go-getting-started/create-project/#create-customer-struct) ---------------------------------------------------------------------------------------------------------------------------------- Create a `types.go` file in the `customer` package and define the `Customer` struct to model the customer details: package customer type Customer struct { Id int Name string Email string } [Create Repository](https://docs.docker.com/guides/testcontainers-go-getting-started/create-project/#create-repository) ------------------------------------------------------------------------------------------------------------------------ Next, create `customer/repo.go`, define the `Repository` struct, and add methods to create a customer and get a customer by email: package customer import ( "context" "fmt" "os" "github.com/jackc/pgx/v5" ) type Repository struct { conn *pgx.Conn } func NewRepository(ctx context.Context, connStr string) (*Repository, error) { conn, err := pgx.Connect(ctx, connStr) if err != nil { _, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err) return nil, err } return &Repository{ conn: conn, }, nil } func (r Repository) CreateCustomer(ctx context.Context, customer Customer) (Customer, error) { err := r.conn.QueryRow(ctx, "INSERT INTO customers (name, email) VALUES ($1, $2) RETURNING id", customer.Name, customer.Email).Scan(&customer.Id) return customer, err } func (r Repository) GetCustomerByEmail(ctx context.Context, email string) (Customer, error) { var customer Customer query := "SELECT id, name, email FROM customers WHERE email = $1" err := r.conn.QueryRow(ctx, query, email). Scan(&customer.Id, &customer.Name, &customer.Email) if err != nil { return Customer{}, err } return customer, nil } Here's what the code does: * `Repository` holds a `*pgx.Conn` for performing database operations. * `NewRepository(connStr)` takes a database connection string and initializes a `Repository`. * `CreateCustomer()` and `GetCustomerByEmail()` are methods on the `Repository` receiver that insert and query customer records. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for .NET](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/) Learn how to create a .NET application and test database interactions using Testcontainers for .NET with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg "C#") C# Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the .NET project ======================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the solution](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/create-project/#set-up-the-solution) -------------------------------------------------------------------------------------------------------------------------------- Create a .NET solution with source and test projects: $ dotnet new sln -o TestcontainersDemo $ cd TestcontainersDemo $ dotnet new classlib -o CustomerService $ dotnet sln add ./CustomerService/CustomerService.csproj $ dotnet new xunit -o CustomerService.Tests $ dotnet sln add ./CustomerService.Tests/CustomerService.Tests.csproj $ dotnet add ./CustomerService.Tests/CustomerService.Tests.csproj reference ./CustomerService/CustomerService.csproj Add the Npgsql dependency to the source project: $ dotnet add ./CustomerService/CustomerService.csproj package Npgsql [Implement the business logic](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/create-project/#implement-the-business-logic) -------------------------------------------------------------------------------------------------------------------------------------------------- Create a `Customer` record type: namespace Customers; public readonly record struct Customer(long Id, string Name); Create a `DbConnectionProvider` class to manage database connections: using System.Data.Common; using Npgsql; namespace Customers; public sealed class DbConnectionProvider { private readonly string _connectionString; public DbConnectionProvider(string connectionString) { _connectionString = connectionString; } public DbConnection GetConnection() { return new NpgsqlConnection(_connectionString); } } Create the `CustomerService` class: namespace Customers; public sealed class CustomerService { private readonly DbConnectionProvider _dbConnectionProvider; public CustomerService(DbConnectionProvider dbConnectionProvider) { _dbConnectionProvider = dbConnectionProvider; CreateCustomersTable(); } public IEnumerable GetCustomers() { IList customers = new List(); using var connection = _dbConnectionProvider.GetConnection(); using var command = connection.CreateCommand(); command.CommandText = "SELECT id, name FROM customers"; command.Connection?.Open(); using var dataReader = command.ExecuteReader(); while (dataReader.Read()) { var id = dataReader.GetInt64(0); var name = dataReader.GetString(1); customers.Add(new Customer(id, name)); } return customers; } public void Create(Customer customer) { using var connection = _dbConnectionProvider.GetConnection(); using var command = connection.CreateCommand(); var id = command.CreateParameter(); id.ParameterName = "@id"; id.Value = customer.Id; var name = command.CreateParameter(); name.ParameterName = "@name"; name.Value = customer.Name; command.CommandText = "INSERT INTO customers (id, name) VALUES(@id, @name)"; command.Parameters.Add(id); command.Parameters.Add(name); command.Connection?.Open(); command.ExecuteNonQuery(); } private void CreateCustomersTable() { using var connection = _dbConnectionProvider.GetConnection(); using var command = connection.CreateCommand(); command.CommandText = "CREATE TABLE IF NOT EXISTS customers (id BIGINT NOT NULL, name VARCHAR NOT NULL, PRIMARY KEY (id))"; command.Connection?.Open(); command.ExecuteNonQuery(); } } Here's what `CustomerService` does: * The constructor calls `CreateCustomersTable()` to ensure the table exists. * `GetCustomers()` fetches all rows from the `customers` table and returns them as `Customer` objects. * `Create()` inserts a customer record into the database. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Java](https://docs.docker.com/guides/testcontainers-java-getting-started/) Learn how to create a Java application and test database interactions using Testcontainers for Java with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Java project ======================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the Maven project](https://docs.docker.com/guides/testcontainers-java-getting-started/create-project/#set-up-the-maven-project) ---------------------------------------------------------------------------------------------------------------------------------------- Create a Java project with Maven from your preferred IDE. This guide uses Maven, but you can use Gradle if you prefer. Add the following dependencies to `pom.xml`: org.postgresql postgresql 42.7.3 ch.qos.logback logback-classic 1.5.6 org.junit.jupiter junit-jupiter 5.10.2 test org.apache.maven.plugins maven-surefire-plugin 3.2.5 This adds the Postgres JDBC driver, logback for logging, JUnit 5 for testing, and the latest `maven-surefire-plugin` for JUnit 5 support. [Implement the business logic](https://docs.docker.com/guides/testcontainers-java-getting-started/create-project/#implement-the-business-logic) ------------------------------------------------------------------------------------------------------------------------------------------------ Create a `Customer` record: package com.testcontainers.demo; public record Customer(Long id, String name) {} Create a `DBConnectionProvider` class to hold JDBC connection parameters and provide a database `Connection`: package com.testcontainers.demo; import java.sql.Connection; import java.sql.DriverManager; class DBConnectionProvider { private final String url; private final String username; private final String password; public DBConnectionProvider(String url, String username, String password) { this.url = url; this.username = username; this.password = password; } Connection getConnection() { try { return DriverManager.getConnection(url, username, password); } catch (Exception e) { throw new RuntimeException(e); } } } Create the `CustomerService` class: package com.testcontainers.demo; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class CustomerService { private final DBConnectionProvider connectionProvider; public CustomerService(DBConnectionProvider connectionProvider) { this.connectionProvider = connectionProvider; createCustomersTableIfNotExists(); } public void createCustomer(Customer customer) { try (Connection conn = this.connectionProvider.getConnection()) { PreparedStatement pstmt = conn.prepareStatement( "insert into customers(id,name) values(?,?)" ); pstmt.setLong(1, customer.id()); pstmt.setString(2, customer.name()); pstmt.execute(); } catch (SQLException e) { throw new RuntimeException(e); } } public List getAllCustomers() { List customers = new ArrayList<>(); try (Connection conn = this.connectionProvider.getConnection()) { PreparedStatement pstmt = conn.prepareStatement( "select id,name from customers" ); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { long id = rs.getLong("id"); String name = rs.getString("name"); customers.add(new Customer(id, name)); } } catch (SQLException e) { throw new RuntimeException(e); } return customers; } private void createCustomersTableIfNotExists() { try (Connection conn = this.connectionProvider.getConnection()) { PreparedStatement pstmt = conn.prepareStatement( """ create table if not exists customers ( id bigint not null, name varchar not null, primary key (id) ) """ ); pstmt.execute(); } catch (SQLException e) { throw new RuntimeException(e); } } } Here's what `CustomerService` does: * The constructor calls `createCustomersTableIfNotExists()` to ensure the table exists. * `createCustomer()` inserts a customer record into the database. * `getAllCustomers()` fetches all rows from the `customers` table and returns a list of `Customer` objects. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-getting-started/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations in Micronaut apps using WireMock](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/) Learn how to create a Micronaut application that integrates with external REST APIs, then test those integrations using WireMock and the Testcontainers WireMock module. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Micronaut project ============================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/#set-up-the-project) ------------------------------------------------------------------------------------------------------------------------------- Create a Micronaut project from [Micronaut Launch](https://micronaut.io/launch) by selecting the **http-client**, **micronaut-test-rest-assured**, and **testcontainers** features. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testing-rest-api-integrations-in-micronaut-apps-using-wiremock) . After generating the project, add the **WireMock** and **Testcontainers WireMock** libraries as test dependencies. The key dependencies in `pom.xml` are: io.micronaut.platform micronaut-parent 4.1.2 17 4.1.2 netty jitpack.io https://jitpack.io io.micronaut micronaut-http-client compile io.micronaut micronaut-http-server-netty compile io.micronaut.serde micronaut-serde-jackson compile io.micronaut.test micronaut-test-junit5 test io.micronaut.test micronaut-test-rest-assured test org.testcontainers testcontainers-junit-jupiter test org.testcontainers testcontainers test org.wiremock wiremock-standalone 3.2.0 test org.wiremock.integrations.testcontainers wiremock-testcontainers-module 1.0-alpha-13 test This guide builds an application that manages video albums. A third-party REST API handles photo assets. For demonstration purposes, the application uses the publicly available [JSONPlaceholder](https://jsonplaceholder.typicode.com/) API as a photo service. The application exposes a `GET /api/albums/{albumId}` endpoint that calls the photo service to fetch photos for a given album. [WireMock](https://wiremock.org/) is a tool for building mock APIs. Testcontainers provides a [WireMock module](https://testcontainers.com/modules/wiremock/) that runs WireMock as a Docker container. [Create the Album and Photo models](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/#create-the-album-and-photo-models) ------------------------------------------------------------------------------------------------------------------------------------------------------------- Create `Album.java` using Java records. Annotate both records with `@Serdeable` to allow serialization and deserialization: package com.testcontainers.demo; import io.micronaut.serde.annotation.Serdeable; import java.util.List; @Serdeable public record Album(Long albumId, List photos) {} @Serdeable record Photo(Long id, String title, String url, String thumbnailUrl) {} [Create the PhotoServiceClient](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/#create-the-photoserviceclient) ----------------------------------------------------------------------------------------------------------------------------------------------------- Micronaut provides [declarative HTTP client](https://docs.micronaut.io/latest/guide/#httpClient) support. Create an interface with a method that fetches photos for a given album ID: package com.testcontainers.demo; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.PathVariable; import io.micronaut.http.client.annotation.Client; import java.util.List; @Client(id = "photosapi") interface PhotoServiceClient { @Get("/albums/{albumId}/photos") List getPhotos(@PathVariable Long albumId); } The `@Client(id = "photosapi")` annotation ties this client to a named configuration. Add the following property to `src/main/resources/application.properties` to set the base URL: micronaut.http.services.photosapi.url=https://jsonplaceholder.typicode.com [Create the REST API endpoint](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/#create-the-rest-api-endpoint) --------------------------------------------------------------------------------------------------------------------------------------------------- Create `AlbumController.java`: package com.testcontainers.demo; import static io.micronaut.scheduling.TaskExecutors.BLOCKING; import io.micronaut.http.annotation.Controller; import io.micronaut.http.annotation.Get; import io.micronaut.http.annotation.PathVariable; import io.micronaut.scheduling.annotation.ExecuteOn; @Controller("/api") class AlbumController { private final PhotoServiceClient photoServiceClient; AlbumController(PhotoServiceClient photoServiceClient) { this.photoServiceClient = photoServiceClient; } @ExecuteOn(BLOCKING) @Get("/albums/{albumId}") public Album getAlbumById(@PathVariable Long albumId) { return new Album(albumId, photoServiceClient.getPhotos(albumId)); } } Here's what this controller does: * `@Controller("/api")` maps the controller to the `/api` path. * Constructor injection provides a `PhotoServiceClient` bean. * `@ExecuteOn(BLOCKING)` offloads blocking I/O to a separate thread pool so it doesn't block the event loop. * `@Get("/albums/{albumId}")` maps the `getAlbumById()` method to an HTTP GET request. This endpoint calls the photo service for a given album ID and returns a response like: { "albumId": 1, "photos": [\ {\ "id": 51,\ "title": "non sunt voluptatem placeat consequuntur rem incidunt",\ "url": "https://via.placeholder.com/600/8e973b",\ "thumbnailUrl": "https://via.placeholder.com/150/8e973b"\ },\ {\ "id": 52,\ "title": "eveniet pariatur quia nobis reiciendis laboriosam ea",\ "url": "https://via.placeholder.com/600/121fa4",\ "thumbnailUrl": "https://via.placeholder.com/150/121fa4"\ }\ ] } [Write tests with WireMock and Testcontainers »](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/) --- # What's next | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) What's next =========== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * The following sections provide step-by-step guides to help you understand core Docker concepts, building images, and running containers. [The basics](https://docs.docker.com/get-started/introduction/whats-next/#the-basics) -------------------------------------------------------------------------------------- Get started learning the core concepts of containers, images, registries, and Docker Compose. [### What is a container?\ \ Learn how to run your first container.](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/) [### What is an image?\ \ Learn the basics of image layers.](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-an-image/) [### What is a registry?\ \ Learn about container registries, explore their interoperability, and interact with registries.](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-registry/) [### What is Docker Compose?\ \ Gain a better understanding of Docker Compose.](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-docker-compose/) [Building images](https://docs.docker.com/get-started/introduction/whats-next/#building-images) ------------------------------------------------------------------------------------------------ Craft optimized container images with Dockerfiles, build cache, and multi-stage builds. [### Understanding image layers\ \ Learn about the layers of container images.](https://docs.docker.com/get-started/docker-concepts/building-images/understanding-image-layers/) [### Writing a Dockerfile\ \ Learn how to create an image using a Dockerfile.](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/) [### Build, tag and publish an image\ \ Learn how to build, tag, and publish an image to Docker Hub or any other registry.](https://docs.docker.com/get-started/docker-concepts/building-images/build-tag-and-publish-an-image/) [### Using the build cache\ \ Learn about the build cache, what changes invalidate the cache, and how to effectively use the build cache.](https://docs.docker.com/get-started/docker-concepts/building-images/using-the-build-cache/) [### Multi-stage builds\ \ Get a better understanding of multi-stage builds and their benefits.](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/) [Running containers](https://docs.docker.com/get-started/introduction/whats-next/#running-containers) ------------------------------------------------------------------------------------------------------ Master essential techniques for exposing ports, overriding defaults, persisting data, sharing files, and managing multi-container applications. [### Publishing ports\ \ Understand the significance of publishing and exposing ports in Docker.](https://docs.docker.com/get-started/docker-concepts/running-containers/publishing-ports/) [### Overriding container defaults\ \ Learn how to override the container defaults using the `docker run` command.](https://docs.docker.com/get-started/docker-concepts/running-containers/overriding-container-defaults/) [### Persisting container data\ \ Learn the significance of data persistence in Docker.](https://docs.docker.com/get-started/docker-concepts/running-containers/persisting-container-data/) [### Sharing local files with containers\ \ Explore the various storage options available in Docker and their common usage.](https://docs.docker.com/get-started/docker-concepts/running-containers/sharing-local-files/) [### Multi-container applications\ \ Learn the significance of multi-container applications and how they're different from single-container applications.](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing an ASP.NET Core web app with Testcontainers](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/) Learn how to test an ASP.NET Core web app using Testcontainers for .NET with a real Microsoft SQL Server instance instead of SQLite. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg "C#") C# Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/) [2](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/) [3](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Set up the project ================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Background](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/#background) ---------------------------------------------------------------------------------------------------------- This guide builds on top of Microsoft's [Integration tests in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests) documentation. The original sample uses an in-memory SQLite database as the backing store for integration tests. You'll replace SQLite with a real Microsoft SQL Server instance running in a Docker container using Testcontainers. You can find the original code sample in the [dotnet/AspNetCore.Docs.Samples](https://github.com/dotnet/AspNetCore.Docs.Samples/tree/main/test/integration-tests/IntegrationTestsSample) repository. [Clone the repository](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/#clone-the-repository) ------------------------------------------------------------------------------------------------------------------------------ Clone the Testcontainers guide repository and change into the project directory: $ git clone https://github.com/testcontainers/tc-guide-testing-aspnet-core.git $ cd tc-guide-testing-aspnet-core [Project structure](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/#project-structure) ------------------------------------------------------------------------------------------------------------------------ The solution contains two projects: RazorPagesProject.sln ├── src/RazorPagesProject/ # ASP.NET Core Razor Pages app └── tests/RazorPagesProject.Tests/ # xUnit integration tests ### [Application project](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/#application-project) The application project (`src/RazorPagesProject/RazorPagesProject.csproj`) is a Razor Pages web app that uses Entity Framework Core with SQLite as its default database provider: net9.0 enable all runtime; build; native; contentfiles; analyzers; buildtransitive The `ApplicationDbContext` stores `Message` entities and provides methods to query and manage them: public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } public virtual DbSet Messages { get; set; } public async virtual Task> GetMessagesAsync() { return await Messages .OrderBy(message => message.Text) .AsNoTracking() .ToListAsync(); } public async virtual Task AddMessageAsync(Message message) { await Messages.AddAsync(message); await SaveChangesAsync(); } public async virtual Task DeleteAllMessagesAsync() { foreach (Message message in Messages) { Messages.Remove(message); } await SaveChangesAsync(); } public async virtual Task DeleteMessageAsync(int id) { var message = await Messages.FindAsync(id); if (message != null) { Messages.Remove(message); await SaveChangesAsync(); } } public void Initialize() { Messages.AddRange(GetSeedingMessages()); SaveChanges(); } public static List GetSeedingMessages() { return new List() { new Message(){ Text = "You're standing on my scarf." }, new Message(){ Text = "Would you like a jelly baby?" }, new Message(){ Text = "To the rational mind, nothing is inexplicable; only unexplained." } }; } } ### [Test project](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/#test-project) The test project (`tests/RazorPagesProject.Tests/RazorPagesProject.Tests.csproj`) includes xUnit, the ASP.NET Core testing infrastructure, and the Testcontainers MSSQL module: net9.0 enable all runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive Always The key dependencies are: * `Microsoft.AspNetCore.Mvc.Testing` - provides `WebApplicationFactory` for bootstrapping the app in tests * `Microsoft.EntityFrameworkCore.SqlServer` - the SQL Server database provider for Entity Framework Core * `Testcontainers.MsSql` - the Testcontainers module for Microsoft SQL Server ### [Existing SQLite-based test factory](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/#existing-sqlite-based-test-factory) The original project includes a `CustomWebApplicationFactory` that replaces the application's database with an in-memory SQLite instance: public class CustomWebApplicationFactory : WebApplicationFactory where TProgram : class { protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { var dbContextDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbContextOptions)); services.Remove(dbContextDescriptor); var dbConnectionDescriptor = services.SingleOrDefault( d => d.ServiceType == typeof(DbConnection)); services.Remove(dbConnectionDescriptor); // Create open SqliteConnection so EF won't automatically close it. services.AddSingleton(container => { var connection = new SqliteConnection("DataSource=:memory:"); connection.Open(); return connection; }); services.AddDbContext((container, options) => { var connection = container.GetRequiredService(); options.UseSqlite(connection); }); }); builder.UseEnvironment("Development"); } } While this approach works, SQLite has behavioral differences from the database you'd use in production. In the next section, you'll replace it with a Testcontainers-managed Microsoft SQL Server instance. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing AWS service integrations using LocalStack](https://docs.docker.com/guides/testcontainers-java-aws-localstack/) Learn how to create a Spring Boot application with Spring Cloud AWS, then test S3 and SQS integrations using Testcontainers and LocalStack. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-aws-localstack/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-aws-localstack/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-aws-localstack/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-aws-localstack/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Spring Boot project ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/#set-up-the-project) --------------------------------------------------------------------------------------------------------------------------- Create a Spring Boot project from [Spring Initializr](https://start.spring.io/) by selecting the **Testcontainers** starter. Spring Cloud AWS starters are not available on Spring Initializr, so you need to add them manually. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testing-aws-service-integrations-using-localstack) . Add the Spring Cloud AWS BOM to your dependency management and add the S3, SQS starters as dependencies. Testcontainers provides a [LocalStack module](https://testcontainers.com/modules/localstack/) for testing AWS service integrations. You also need [Awaitility](http://www.awaitility.org/) for testing asynchronous SQS processing. The key dependencies in `pom.xml` are: 17 2.0.4 3.0.3 org.springframework.boot spring-boot-starter-web io.awspring.cloud spring-cloud-aws-starter-s3 io.awspring.cloud spring-cloud-aws-starter-sqs org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-testcontainers test org.testcontainers testcontainers-junit-jupiter test org.testcontainers testcontainers-localstack test org.awaitility awaitility test io.awspring.cloud spring-cloud-aws-dependencies ${awspring.version} pom import [Create the configuration properties](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/#create-the-configuration-properties) ------------------------------------------------------------------------------------------------------------------------------------------------------------- To make the SQS queue and S3 bucket names configurable, create an `ApplicationProperties` record: package com.testcontainers.demo; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "app") public record ApplicationProperties(String queue, String bucket) {} Then add `@ConfigurationPropertiesScan` to the main application class so that Spring automatically scans for `@ConfigurationProperties`\-annotated classes and registers them as beans: package com.testcontainers.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.context.properties.ConfigurationPropertiesScan; @SpringBootApplication @ConfigurationPropertiesScan public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } [Implement StorageService for S3](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/#implement-storageservice-for-s3) ----------------------------------------------------------------------------------------------------------------------------------------------------- Spring Cloud AWS provides higher-level abstractions like `S3Template` with convenience methods for uploading and downloading files. Create a `StorageService` class: package com.testcontainers.demo; import io.awspring.cloud.s3.S3Template; import java.io.IOException; import java.io.InputStream; import org.springframework.stereotype.Service; @Service public class StorageService { private final S3Template s3Template; public StorageService(S3Template s3Template) { this.s3Template = s3Template; } public void upload(String bucketName, String key, InputStream stream) { this.s3Template.upload(bucketName, key, stream); } public InputStream download(String bucketName, String key) throws IOException { return this.s3Template.download(bucketName, key).getInputStream(); } public String downloadAsString(String bucketName, String key) throws IOException { try (InputStream is = this.download(bucketName, key)) { return new String(is.readAllBytes()); } } } [Create the SQS message model](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/#create-the-sqs-message-model) ----------------------------------------------------------------------------------------------------------------------------------------------- Create a `Message` record that represents the payload you send to the SQS queue: package com.testcontainers.demo; import java.util.UUID; public record Message(UUID uuid, String content) {} [Implement the message sender](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/#implement-the-message-sender) ----------------------------------------------------------------------------------------------------------------------------------------------- Create `MessageSender`, which uses `SqsTemplate` to publish messages: package com.testcontainers.demo; import io.awspring.cloud.sqs.operations.SqsTemplate; import org.springframework.stereotype.Service; @Service public class MessageSender { private final SqsTemplate sqsTemplate; public MessageSender(SqsTemplate sqsTemplate) { this.sqsTemplate = sqsTemplate; } public void publish(String queueName, Message message) { sqsTemplate.send(to -> to.queue(queueName).payload(message)); } } [Implement the message listener](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/#implement-the-message-listener) --------------------------------------------------------------------------------------------------------------------------------------------------- Create `MessageListener` with a handler method annotated with `@SqsListener`. When a message arrives, the listener uploads the content to an S3 bucket using the message UUID as the key: package com.testcontainers.demo; import io.awspring.cloud.sqs.annotation.SqsListener; import java.io.ByteArrayInputStream; import java.nio.charset.StandardCharsets; import org.springframework.stereotype.Service; @Service public class MessageListener { private final StorageService storageService; private final ApplicationProperties properties; public MessageListener( StorageService storageService, ApplicationProperties properties ) { this.storageService = storageService; this.properties = properties; } @SqsListener(queueNames = { "${app.queue}" }) public void handle(Message message) { String bucketName = this.properties.bucket(); String key = message.uuid().toString(); ByteArrayInputStream is = new ByteArrayInputStream( message.content().getBytes(StandardCharsets.UTF_8) ); this.storageService.upload(bucketName, key, is); } } The `${app.queue}` expression reads the queue name from application configuration instead of hard-coding it. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-aws-localstack/write-tests/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Java language-specific guide](https://docs.docker.com/guides/java/) This guide demonstrates how to containerize Java applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java 20 minutes [1](https://docs.docker.com/guides/java/containerize/) [Containerize your app](https://docs.docker.com/guides/java/containerize/) [2](https://docs.docker.com/guides/java/develop/) [Develop your app](https://docs.docker.com/guides/java/develop/) [3](https://docs.docker.com/guides/java/run-tests/) [Run your tests](https://docs.docker.com/guides/java/run-tests/) [4](https://docs.docker.com/guides/java/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/java/configure-ci-cd/) [5](https://docs.docker.com/guides/java/deploy/) [Test your deployment](https://docs.docker.com/guides/java/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Java application =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/java/containerize/#prerequisites) --------------------------------------------------------------------------------- * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . Docker adds new features regularly and some parts of this guide may work only with the latest version of Docker Desktop. * You have a [Git client](https://git-scm.com/downloads) . The examples in this section use a command-line based Git client, but you can use any client. [Overview](https://docs.docker.com/guides/java/containerize/#overview) ----------------------------------------------------------------------- This section walks you through containerizing and running a Java application. [Get the sample applications](https://docs.docker.com/guides/java/containerize/#get-the-sample-applications) ------------------------------------------------------------------------------------------------------------- Clone the sample application that you'll be using to your local development machine. Run the following command in a terminal to clone the repository. $ git clone https://github.com/spring-projects/spring-petclinic.git The sample application is a Spring Boot application built using Maven. For more details, see `readme.md` in the repository. [Initialize Docker assets](https://docs.docker.com/guides/java/containerize/#initialize-docker-assets) ------------------------------------------------------------------------------------------------------- Now that you have an application, you can create the necessary Docker assets to containerize your application. You can use Docker Desktop's built-in Docker Init feature to help streamline the process, or you can manually create the assets. Use Docker Init Manually create assets Inside the `spring-petclinic` directory, run the `docker init` command. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. Refer to the following example to answer the prompts from `docker init` and use the same answers for your prompts. The sample application already contains Docker assets. You'll be prompted to overwrite the existing Docker assets. To continue with this guide, select `y` to overwrite them. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! WARNING: The following Docker files already exist in this directory: - docker-compose.yml ? Do you want to overwrite them? Yes ? What application platform does your project use? Java ? What's the relative directory (with a leading .) for your app? ./src ? What version of Java do you want to use? 21 ? What port does your server listen on? 8080 In the previous example, notice the `WARNING`. `docker-compose.yaml` already exists, so `docker init` overwrites that file rather than creating a new `compose.yaml` file. This prevents having multiple Compose files in the directory. Both names are supported, but Compose prefers the canonical `compose.yaml`. If you don't have Docker Desktop installed or prefer creating the assets manually, you can create the following files in your project directory. Create a file named `Dockerfile` with the following contents. Dockerfile Show more # syntax=docker/dockerfile:1 # Comments are provided throughout this file to help you get started. # If you need more help, visit the Dockerfile reference guide at # https://docs.docker.com/go/dockerfile-reference/ # Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 ################################################################################ # Create a stage for resolving and downloading dependencies. FROM eclipse-temurin:21-jdk-jammy as deps WORKDIR /build # Copy the mvnw wrapper with executable permissions. COPY --chmod=0755 mvnw mvnw COPY .mvn/ .mvn/ # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /root/.m2 so that subsequent builds don't have to # re-download packages. RUN --mount=type=bind,source=pom.xml,target=pom.xml \ --mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests ################################################################################ # Create a stage for building the application based on the stage with downloaded dependencies. # This Dockerfile is optimized for Java applications that output an uber jar, which includes # all the dependencies needed to run your app inside a JVM. If your app doesn't output an uber # jar and instead relies on an application server like Apache Tomcat, you'll need to update this # stage with the correct filename of your package and update the base image of the "final" stage # use the relevant app server, e.g., using tomcat (https://hub.docker.com/_/tomcat/) as a base image. FROM deps as package WORKDIR /build COPY ./src src/ RUN --mount=type=bind,source=pom.xml,target=pom.xml \ --mount=type=cache,target=/root/.m2 \ ./mvnw package -DskipTests && \ mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar ################################################################################ # Create a stage for extracting the application into separate layers. # Take advantage of Spring Boot's layer tools and Docker's caching by extracting # the packaged application into separate layers that can be copied into the final stage. # See Spring's docs for reference: # https://docs.spring.io/spring-boot/docs/current/reference/html/container-images.html FROM package as extract WORKDIR /build RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted ################################################################################ # Create a new stage for running the application that contains the minimal # runtime dependencies for the application. This often uses a different base # image from the install or build stage where the necessary files are copied # from the install stage. # # The example below uses eclipse-turmin's JRE image as the foundation for running the app. # By specifying the "17-jre-jammy" tag, it will also use whatever happens to be the # most recent version of that tag when you build your Dockerfile. # If reproducibility is important, consider using a specific digest SHA, like # eclipse-temurin@sha256:99cede493dfd88720b610eb8077c8688d3cca50003d76d1d539b0efc8cca72b4. FROM eclipse-temurin:21-jre-jammy AS final # Create a non-privileged user that the app will run under. # See https://docs.docker.com/go/dockerfile-user-best-practices/ ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser USER appuser # Copy the executable from the "package" stage. COPY --from=extract build/target/extracted/dependencies/ ./ COPY --from=extract build/target/extracted/spring-boot-loader/ ./ COPY --from=extract build/target/extracted/snapshot-dependencies/ ./ COPY --from=extract build/target/extracted/application/ ./ EXPOSE 8080 ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ] Hide The sample already contains a Compose file. Overwrite this file to follow along with the guide. Update the`docker-compose.yaml` with the following contents. docker-compose.yaml Show more # Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker Compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "server". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: server: build: context: . ports: - 8080:8080 # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker compose up`. # depends_on: # db: # condition: service_healthy # db: # image: postgres:18 # restart: always # user: postgres # secrets: # - db-password # volumes: # - db-data:/var/lib/postgresql # environment: # - POSTGRES_DB=example # - POSTGRES_PASSWORD_FILE=/run/secrets/db-password # expose: # - 5432 # healthcheck: # test: [ "CMD", "pg_isready" ] # interval: 10s # timeout: 5s # retries: 5 # volumes: # db-data: # secrets: # db-password: # file: db/password.txt Hide Create a file named `.dockerignore` with the following contents. .dockerignore Show more # Include any files or directories that you don't want to be copied to your # container here (e.g., local build artifacts, temporary files, etc.). # # For more help, visit the .dockerignore file reference guide at # https://docs.docker.com/go/build-context-dockerignore/ **/.classpath **/.dockerignore **/.env **/.git **/.gitignore **/.project **/.settings **/.toolstarget **/.vs **/.vscode **/.next **/.cache **/*.*proj.user **/*.dbmdl **/*.jfm **/charts **/docker-compose* **/compose.y*ml **/target **/Dockerfile* **/node_modules **/npm-debug.log **/obj **/secrets.dev.yaml **/values.dev.yaml **/vendor LICENSE README.md Hide You should now have the following three files in your `spring-petclinic` directory. * [Dockerfile](https://docs.docker.com/reference/dockerfile/) * [.dockerignore](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [docker-compose.yaml](https://docs.docker.com/reference/compose-file/) [Run the application](https://docs.docker.com/guides/java/containerize/#run-the-application) --------------------------------------------------------------------------------------------- Inside the `spring-petclinic` directory, run the following command in a terminal. $ docker compose up --build The first time you build and run the app, Docker downloads dependencies and builds the app. It may take several minutes depending on your network connection. Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple app for a pet clinic. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/java/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `spring-petclinic` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple app for a pet clinic. In the terminal, run the following command to stop the application. $ docker compose down For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) . [Summary](https://docs.docker.com/guides/java/containerize/#summary) --------------------------------------------------------------------- In this section, you learned how you can containerize and run a Java application using Docker. Related information: * [docker init reference](https://docs.docker.com/reference/cli/docker/init/) [Next steps](https://docs.docker.com/guides/java/containerize/#next-steps) --------------------------------------------------------------------------- In the next section, you'll learn how you can develop your application using Docker containers. [Use containers for Java development »](https://docs.docker.com/guides/java/develop/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testcontainers container lifecycle management using JUnit 5](https://docs.docker.com/guides/testcontainers-java-lifecycle/) Learn different approaches to manage container lifecycle with Testcontainers using JUnit 5 lifecycle callbacks, extension annotations, and the singleton containers pattern. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-lifecycle/lifecycle-callbacks/) [Lifecycle callbacks](https://docs.docker.com/guides/testcontainers-java-lifecycle/lifecycle-callbacks/) [3](https://docs.docker.com/guides/testcontainers-java-lifecycle/extension-annotations/) [Extension annotations](https://docs.docker.com/guides/testcontainers-java-lifecycle/extension-annotations/) [4](https://docs.docker.com/guides/testcontainers-java-lifecycle/singleton-containers/) [Singleton containers](https://docs.docker.com/guides/testcontainers-java-lifecycle/singleton-containers/) [« Back to all guides](https://docs.docker.com/guides/) Create the project and business logic ===================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/#set-up-the-project) ---------------------------------------------------------------------------------------------------------------------- Create a Java project with Maven and add the required dependencies: org.postgresql postgresql 42.7.3 ch.qos.logback logback-classic 1.5.6 org.junit.jupiter junit-jupiter 5.10.2 test org.testcontainers testcontainers-junit-jupiter 2.0.4 test org.testcontainers testcontainers-postgresql 2.0.4 test [Create the business logic](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/#create-the-business-logic) ------------------------------------------------------------------------------------------------------------------------------------ Create a `Customer` record: package com.testcontainers.demo; public record Customer(Long id, String name) {} Create a `CustomerService` class with methods to create, retrieve, and delete customers: package com.testcontainers.demo; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class CustomerService { private final String url; private final String username; private final String password; public CustomerService(String url, String username, String password) { this.url = url; this.username = username; this.password = password; createCustomersTableIfNotExists(); } public void createCustomer(Customer customer) { try (Connection conn = this.getConnection()) { PreparedStatement pstmt = conn.prepareStatement( "insert into customers(id,name) values(?,?)" ); pstmt.setLong(1, customer.id()); pstmt.setString(2, customer.name()); pstmt.execute(); } catch (SQLException e) { throw new RuntimeException(e); } } public List getAllCustomers() { List customers = new ArrayList<>(); try (Connection conn = this.getConnection()) { PreparedStatement pstmt = conn.prepareStatement( "select id,name from customers" ); ResultSet rs = pstmt.executeQuery(); while (rs.next()) { long id = rs.getLong("id"); String name = rs.getString("name"); customers.add(new Customer(id, name)); } } catch (SQLException e) { throw new RuntimeException(e); } return customers; } public Optional getCustomer(Long customerId) { try (Connection conn = this.getConnection()) { PreparedStatement pstmt = conn.prepareStatement( "select id,name from customers where id = ?" ); pstmt.setLong(1, customerId); ResultSet rs = pstmt.executeQuery(); if (rs.next()) { long id = rs.getLong("id"); String name = rs.getString("name"); return Optional.of(new Customer(id, name)); } } catch (SQLException e) { throw new RuntimeException(e); } return Optional.empty(); } public void deleteAllCustomers() { try (Connection conn = this.getConnection()) { PreparedStatement pstmt = conn.prepareStatement("delete from customers"); pstmt.execute(); } catch (SQLException e) { throw new RuntimeException(e); } } private void createCustomersTableIfNotExists() { try (Connection conn = this.getConnection()) { PreparedStatement pstmt = conn.prepareStatement( """ create table if not exists customers ( id bigint not null, name varchar not null, primary key (id) ) """ ); pstmt.execute(); } catch (SQLException e) { throw new RuntimeException(e); } } private Connection getConnection() { try { return DriverManager.getConnection(url, username, password); } catch (Exception e) { throw new RuntimeException(e); } } } [JUnit 5 lifecycle callbacks »](https://docs.docker.com/guides/testcontainers-java-lifecycle/lifecycle-callbacks/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Working with jOOQ and Flyway using Testcontainers](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/) Generate typesafe jOOQ code from a real PostgreSQL database managed by Flyway migrations, then test repositories using Testcontainers. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Spring Boot project ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/#set-up-the-project) ------------------------------------------------------------------------------------------------------------------------ Create a Spring Boot project from [Spring Initializr](https://start.spring.io/) by selecting Maven as the build tool and adding the **JOOQ Access Layer**, **Flyway Migration**, **Spring Boot DevTools**, **PostgreSQL Driver**, and **Testcontainers** starters. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-working-with-jooq-flyway-using-testcontainers) . jOOQ (jOOQ Object Oriented Querying) provides a fluent API for building typesafe SQL queries. To get the full benefit of its typesafe DSL, you need to generate Java code from your database tables, views, and other objects. > Tip > > To learn more about how the jOOQ code generator helps, read [Why You Should Use jOOQ With Code Generation](https://blog.jooq.org/why-you-should-use-jooq-with-code-generation/) > . The typical process for building and testing the application with jOOQ code generation is: 1. Create a database instance using Testcontainers. 2. Apply Flyway database migrations. 3. Run the jOOQ code generator to produce Java code from the database objects. 4. Run integration tests. The [testcontainers-jooq-codegen-maven-plugin](https://github.com/testcontainers/testcontainers-jooq-codegen-maven-plugin) automates this as part of the Maven build. [Create Flyway migration scripts](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/#create-flyway-migration-scripts) -------------------------------------------------------------------------------------------------------------------------------------------------- The sample application has `users`, `posts`, and `comments` tables. Create the first migration script following the Flyway naming convention. Create `src/main/resources/db/migration/V1__create_tables.sql`: create table users ( id bigserial not null, name varchar not null, email varchar not null, created_at timestamp, updated_at timestamp, primary key (id), constraint user_email_unique unique (email) ); create table posts ( id bigserial not null, title varchar not null, content varchar not null, created_by bigint references users (id) not null, created_at timestamp, updated_at timestamp, primary key (id) ); create table comments ( id bigserial not null, name varchar not null, content varchar not null, post_id bigint references posts (id) not null, created_at timestamp, updated_at timestamp, primary key (id) ); ALTER SEQUENCE users_id_seq RESTART WITH 101; ALTER SEQUENCE posts_id_seq RESTART WITH 101; ALTER SEQUENCE comments_id_seq RESTART WITH 101; The sequence values restart at 101 so that you can insert sample data with explicit primary key values for testing. [Configure jOOQ code generation](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/#configure-jooq-code-generation) ------------------------------------------------------------------------------------------------------------------------------------------------ Add the `testcontainers-jooq-codegen-maven-plugin` to `pom.xml`: 2.0.4 0.0.4 org.testcontainers testcontainers-jooq-codegen-maven-plugin ${testcontainers-jooq-codegen-maven-plugin.version} org.testcontainers testcontainers-postgresql ${testcontainers.version} org.postgresql postgresql ${postgresql.version} generate-jooq-sources generate generate-sources POSTGRES postgres:16-alpine filesystem:src/main/resources/db/migration .* flyway_schema_history public com.testcontainers.demo.jooq target/generated-sources/jooq Here's what the plugin configuration does: * The `/` section sets the database type to `POSTGRES` and the Docker image to `postgres:16-alpine`. * The `/` section points to the Flyway migration scripts. * The `/` section configures the package name and output directory for the generated code. You can use any configuration option that the official `jooq-code-generator` plugin supports. When you run `./mvnw clean package`, the plugin uses Testcontainers to spin up a PostgreSQL container, applies the Flyway migrations, and generates Java code under `target/generated-sources/jooq`. [Create model classes](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/#create-model-classes) ---------------------------------------------------------------------------------------------------------------------------- Create model classes to represent the data structures for various use cases. These records hold a subset of column values from the tables. `User.java`: package com.testcontainers.demo.domain; public record User(Long id, String name, String email) {} `Post.java`: package com.testcontainers.demo.domain; import java.time.LocalDateTime; import java.util.List; public record Post( Long id, String title, String content, User createdBy, List comments, LocalDateTime createdAt, LocalDateTime updatedAt ) {} `Comment.java`: package com.testcontainers.demo.domain; import java.time.LocalDateTime; public record Comment( Long id, String name, String content, LocalDateTime createdAt, LocalDateTime updatedAt ) {} [Implement repositories using jOOQ](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/#implement-repositories-using-jooq) ------------------------------------------------------------------------------------------------------------------------------------------------------ Create `UserRepository.java` with methods to create a user and look up a user by email: package com.testcontainers.demo.domain; import static com.testcontainers.demo.jooq.tables.Users.USERS; import static org.jooq.Records.mapping; import java.time.LocalDateTime; import java.util.Optional; import org.jooq.DSLContext; import org.springframework.stereotype.Repository; @Repository class UserRepository { private final DSLContext dsl; UserRepository(DSLContext dsl) { this.dsl = dsl; } public User createUser(User user) { return this.dsl.insertInto(USERS) .set(USERS.NAME, user.name()) .set(USERS.EMAIL, user.email()) .set(USERS.CREATED_AT, LocalDateTime.now()) .returningResult(USERS.ID, USERS.NAME, USERS.EMAIL) .fetchOne(mapping(User::new)); } public Optional getUserByEmail(String email) { return this.dsl.select(USERS.ID, USERS.NAME, USERS.EMAIL) .from(USERS) .where(USERS.EMAIL.equalIgnoreCase(email)) .fetchOptional(mapping(User::new)); } } The jOOQ DSL looks similar to SQL but written in Java. Because the code is generated from the database schema, it stays in sync with the database structure and provides type safety. For example, `where(USERS.EMAIL.equalIgnoreCase(email))` expects a `String` value. If you pass a non-string value like `123`, you get a compiler error. [Fetch complex object graphs](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/#fetch-complex-object-graphs) ------------------------------------------------------------------------------------------------------------------------------------------ jOOQ shines when it comes to complex queries. The database has a many-to-one relationship from `Post` to `User` and a one-to-many relationship from `Post` to `Comment`. Create `PostRepository.java` to load a `Post` with its creator and comments using a single query with jOOQ's MULTISET feature: package com.testcontainers.demo.domain; import static com.testcontainers.demo.jooq.Tables.COMMENTS; import static com.testcontainers.demo.jooq.tables.Posts.POSTS; import static org.jooq.Records.mapping; import static org.jooq.impl.DSL.multiset; import static org.jooq.impl.DSL.row; import static org.jooq.impl.DSL.select; import java.util.Optional; import org.jooq.DSLContext; import org.springframework.stereotype.Repository; @Repository class PostRepository { private final DSLContext dsl; PostRepository(DSLContext dsl) { this.dsl = dsl; } public Optional getPostById(Long id) { return this.dsl.select( POSTS.ID, POSTS.TITLE, POSTS.CONTENT, row(POSTS.users().ID, POSTS.users().NAME, POSTS.users().EMAIL) .mapping(User::new) .as("createdBy"), multiset( select( COMMENTS.ID, COMMENTS.NAME, COMMENTS.CONTENT, COMMENTS.CREATED_AT, COMMENTS.UPDATED_AT ) .from(COMMENTS) .where(POSTS.ID.eq(COMMENTS.POST_ID)) ) .as("comments") .convertFrom(r -> r.map(mapping(Comment::new))), POSTS.CREATED_AT, POSTS.UPDATED_AT ) .from(POSTS) .where(POSTS.ID.eq(id)) .fetchOptional(mapping(Post::new)); } } This uses jOOQ's [nested records](https://www.jooq.org/doc/latest/manual/sql-building/column-expressions/nested-records/) for the many-to-one `Post`\-to-`User` association and [MULTISET](https://www.jooq.org/doc/latest/manual/sql-building/column-expressions/multiset-value-constructor/) for the one-to-many `Post`\-to-`Comment` association. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Securing Spring Boot microservice using Keycloak and Testcontainers](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/) Learn how to create an OAuth 2.0 Resource Server using Spring Boot, secure API endpoints with Keycloak, and test the application using the Testcontainers Keycloak module. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 30 minutes [1](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Spring Boot project ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/#set-up-the-project) --------------------------------------------------------------------------------------------------------------------------------- Create a Spring Boot project from [Spring Initializr](https://start.spring.io/) by selecting the **Spring Web**, **Validation**, **JDBC API**, **PostgreSQL Driver**, **Spring Security**, **OAuth2 Resource Server**, and **Testcontainers** starters. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-securing-spring-boot-microservice-using-keycloak-and-testcontainers) . After generating the application, add the [testcontainers-keycloak](https://github.com/dasniko/testcontainers-keycloak) community module and [REST Assured](https://rest-assured.io/) as test dependencies. The key dependencies in `pom.xml` are: 17 2.0.4 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-validation org.springframework.boot spring-boot-starter-jdbc org.postgresql postgresql runtime org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-oauth2-resource-server org.springframework.boot spring-boot-starter-test test org.springframework.security spring-security-test test org.springframework.boot spring-boot-testcontainers test org.testcontainers testcontainers-junit-jupiter test org.testcontainers testcontainers-postgresql test com.github.dasniko testcontainers-keycloak 3.4.0 test io.rest-assured rest-assured test [Create the domain model](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/#create-the-domain-model) ------------------------------------------------------------------------------------------------------------------------------------------- Create a `Product` record that represents the domain object: package com.testcontainers.products.domain; import jakarta.validation.constraints.NotEmpty; public record Product(Long id, @NotEmpty String title, String description) {} [Create the repository](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/#create-the-repository) --------------------------------------------------------------------------------------------------------------------------------------- Implement `ProductRepository` using Spring `JdbcClient` to interact with a PostgreSQL database: package com.testcontainers.products.domain; import java.util.List; import org.springframework.jdbc.core.simple.JdbcClient; import org.springframework.jdbc.support.GeneratedKeyHolder; import org.springframework.jdbc.support.KeyHolder; import org.springframework.stereotype.Repository; @Repository public class ProductRepository { private final JdbcClient jdbcClient; public ProductRepository(JdbcClient jdbcClient) { this.jdbcClient = jdbcClient; } public List getAll() { return jdbcClient.sql("SELECT * FROM products").query(Product.class).list(); } public Product create(Product product) { String sql = "INSERT INTO products(title, description) VALUES (:title,:description) RETURNING id"; KeyHolder keyHolder = new GeneratedKeyHolder(); jdbcClient .sql(sql) .param("title", product.title()) .param("description", product.description()) .update(keyHolder); Long id = keyHolder.getKeyAs(Long.class); return new Product(id, product.title(), product.description()); } } [Add a schema creation script](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/#add-a-schema-creation-script) ----------------------------------------------------------------------------------------------------------------------------------------------------- Create `src/main/resources/schema.sql` to initialize the `products` table: CREATE TABLE products ( id bigserial primary key, title varchar not null, description text ); Enable schema initialization in `src/main/resources/application.properties`: spring.sql.init.mode=always For production applications, use a database migration tool like Flyway or Liquibase instead. [Implement the API endpoints](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/#implement-the-api-endpoints) --------------------------------------------------------------------------------------------------------------------------------------------------- Create `ProductController` with endpoints to fetch all products and create a product: package com.testcontainers.products.api; import com.testcontainers.products.domain.Product; import com.testcontainers.products.domain.ProductRepository; import jakarta.validation.Valid; import java.util.List; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/api/products") class ProductController { private final ProductRepository productRepository; ProductController(ProductRepository productRepository) { this.productRepository = productRepository; } @GetMapping List getAll() { return productRepository.getAll(); } @PostMapping @ResponseStatus(HttpStatus.CREATED) Product createProduct(@RequestBody @Valid Product product) { return productRepository.create(product); } } [Configure OAuth 2.0 security](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/#configure-oauth-20-security) ---------------------------------------------------------------------------------------------------------------------------------------------------- Create a `SecurityConfig` class that protects the API endpoints using JWT token-based authentication: package com.testcontainers.products.config; import static org.springframework.security.config.Customizer.withDefaults; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configurers.CorsConfigurer; import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.web.SecurityFilterChain; @Configuration @EnableWebSecurity class SecurityConfig { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(c -> c .requestMatchers(HttpMethod.GET, "/api/products") .permitAll() .requestMatchers(HttpMethod.POST, "/api/products") .authenticated() .anyRequest() .authenticated() ) .sessionManagement(c -> c.sessionCreationPolicy(SessionCreationPolicy.STATELESS) ) .cors(CorsConfigurer::disable) .csrf(CsrfConfigurer::disable) .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults())); return http.build(); } } This configuration: * Permits unauthenticated access to `GET /api/products`. * Requires authentication for `POST /api/products` and all other endpoints. * Configures the OAuth 2.0 Resource Server with JWT token-based authentication. * Disables CORS and CSRF because this is a stateless API. Add the JWT issuer URI to `application.properties`: spring.security.oauth2.resourceserver.jwt.issuer-uri=http://localhost:9090/realms/keycloaktcdemo [Export the Keycloak realm configuration](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/#export-the-keycloak-realm-configuration) --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Before writing the tests, export a Keycloak realm configuration so that the test environment can import it automatically. Start a temporary Keycloak instance: $ docker run -p 9090:8080 \ -e KEYCLOAK_ADMIN=admin \ -e KEYCLOAK_ADMIN_PASSWORD=admin \ quay.io/keycloak/keycloak:25 start-dev Open `http://localhost:9090` and sign in to the Admin Console with `admin/admin`. Then set up the realm: 1. In the top-left corner, select the realm drop-down and create a realm named `keycloaktcdemo`. 2. Under the `keycloaktcdemo` realm, create a client with the following settings: * **Client ID**: `product-service` * **Client Authentication**: **On** * **Authentication flow**: select only **Service accounts roles** 3. On the **Client details** screen, go to the **Credentials** tab and copy the **Client secret** value. Export the realm configuration: $ docker ps # copy the keycloak container id $ docker exec -it /bin/bash $ /opt/keycloak/bin/kc.sh export --dir /opt/keycloak/data/import --realm keycloaktcdemo $ exit $ docker cp :/opt/keycloak/data/import/keycloaktcdemo-realm.json keycloaktcdemo-realm.json Copy the exported `keycloaktcdemo-realm.json` file into `src/test/resources`. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing Micronaut Kafka Listener using Testcontainers](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/) Learn how to create a Micronaut application with a Kafka listener that persists data in MySQL, then test it using Testcontainers Kafka and MySQL modules with Awaitility. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Micronaut project ============================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/#set-up-the-project) ---------------------------------------------------------------------------------------------------------------------------- Create a Micronaut project from [Micronaut Launch](https://micronaut.io/launch) by selecting the **kafka**, **data-jpa**, **mysql**, **awaitility**, **assertj**, and **testcontainers** features. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testing-micronaut-kafka-listener) . You'll use the [Awaitility](http://www.awaitility.org/) library to assert the expectations of an asynchronous process flow. The key dependencies in `pom.xml` are: io.micronaut.platform micronaut-parent 4.1.4 io.micronaut.data micronaut-data-hibernate-jpa compile io.micronaut.kafka micronaut-kafka compile io.micronaut.serde micronaut-serde-jackson compile io.micronaut.sql micronaut-jdbc-hikari compile mysql mysql-connector-java runtime org.awaitility awaitility 4.2.0 test org.testcontainers testcontainers-junit-jupiter test org.testcontainers testcontainers-kafka test org.testcontainers testcontainers-mysql test The Micronaut parent POM manages the Testcontainers BOM, so you don't need to specify versions for Testcontainers modules individually. [Create the JPA entity](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/#create-the-jpa-entity) ---------------------------------------------------------------------------------------------------------------------------------- The application listens to a topic called `product-price-changes`. When a message arrives, it extracts the product code and price from the event payload and updates the price for that product in the MySQL database. Create `Product.java`: package com.testcontainers.demo; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; import java.math.BigDecimal; @Entity @Table(name = "products") public class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String code; @Column(nullable = false) private String name; @Column(nullable = false) private BigDecimal price; public Product() {} public Product(Long id, String code, String name, BigDecimal price) { this.id = id; this.code = code; this.name = name; this.price = price; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } } [Create the Micronaut Data JPA repository](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/#create-the-micronaut-data-jpa-repository) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ Create a repository interface for the `Product` entity with a method to find a product by code and a method to update the price for a given product code: package com.testcontainers.demo; import io.micronaut.data.annotation.Query; import io.micronaut.data.annotation.Repository; import io.micronaut.data.jpa.repository.JpaRepository; import java.math.BigDecimal; import java.util.Optional; @Repository public interface ProductRepository extends JpaRepository { Optional findByCode(String code); @Query("update Product p set p.price = :price where p.code = :productCode") void updateProductPrice(String productCode, BigDecimal price); } Unlike Spring Data JPA, Micronaut Data uses compile-time annotation processing to implement repository methods, avoiding runtime reflection. [Create the event payload](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/#create-the-event-payload) ---------------------------------------------------------------------------------------------------------------------------------------- Create a record named `ProductPriceChangedEvent` that represents the structure of the event payload received from the Kafka topic: package com.testcontainers.demo; import io.micronaut.serde.annotation.Serdeable; import java.math.BigDecimal; @Serdeable public record ProductPriceChangedEvent(String productCode, BigDecimal price) {} The `@Serdeable` annotation tells Micronaut Serialization that this type can be serialized and deserialized. The sender and receiver agree on the following JSON format: { "productCode": "P100", "price": 25.0 } [Implement the Kafka listener](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/#implement-the-kafka-listener) ------------------------------------------------------------------------------------------------------------------------------------------------ Create `ProductPriceChangedEventHandler.java`, which handles messages from the `product-price-changes` topic and updates the product price in the database: package com.testcontainers.demo; import static io.micronaut.configuration.kafka.annotation.OffsetReset.EARLIEST; import io.micronaut.configuration.kafka.annotation.KafkaListener; import io.micronaut.configuration.kafka.annotation.Topic; import jakarta.inject.Singleton; import jakarta.transaction.Transactional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Singleton @Transactional class ProductPriceChangedEventHandler { private static final Logger LOG = LoggerFactory.getLogger(ProductPriceChangedEventHandler.class); private final ProductRepository productRepository; ProductPriceChangedEventHandler(ProductRepository productRepository) { this.productRepository = productRepository; } @Topic("product-price-changes") @KafkaListener(offsetReset = EARLIEST, groupId = "demo") public void handle(ProductPriceChangedEvent event) { LOG.info("Received a ProductPriceChangedEvent with productCode:{}: ", event.productCode()); productRepository.updateProductPrice(event.productCode(), event.price()); } } Key details: * The `@KafkaListener` annotation marks this class as a Kafka message listener. Setting `offsetReset` to `EARLIEST` makes the listener start consuming messages from the beginning of the partition, which is useful during testing. * The `@Topic` annotation specifies which topic to subscribe to. * Micronaut handles JSON deserialization of the `ProductPriceChangedEvent` automatically using Micronaut Serialization. [Configure the datasource](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/#configure-the-datasource) ---------------------------------------------------------------------------------------------------------------------------------------- Add the following properties to `src/main/resources/application.properties`: micronaut.application.name=tc-guide-testing-micronaut-kafka-listener datasources.default.db-type=mysql datasources.default.dialect=MYSQL jpa.default.properties.hibernate.hbm2ddl.auto=update jpa.default.entity-scan.packages=com.testcontainers.demo datasources.default.driver-class-name=com.mysql.cj.jdbc.Driver Hibernate's `hbm2ddl.auto=update` creates and updates the database schema automatically. For testing, you'll override this to `create-drop` in the test properties file. Create `src/test/resources/application-test.properties`: jpa.default.properties.hibernate.hbm2ddl.auto=create-drop [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/write-tests/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [.NET language-specific guide](https://docs.docker.com/guides/dotnet/) Learn how to containerize .NET applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg "C#") C# 20 minutes [1](https://docs.docker.com/guides/dotnet/containerize/) [Containerize your app](https://docs.docker.com/guides/dotnet/containerize/) [2](https://docs.docker.com/guides/dotnet/develop/) [Develop your app](https://docs.docker.com/guides/dotnet/develop/) [3](https://docs.docker.com/guides/dotnet/run-tests/) [Run your tests](https://docs.docker.com/guides/dotnet/run-tests/) [4](https://docs.docker.com/guides/dotnet/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/dotnet/configure-ci-cd/) [5](https://docs.docker.com/guides/dotnet/deploy/) [Test your deployment](https://docs.docker.com/guides/dotnet/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a .NET application =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/dotnet/containerize/#prerequisites) ----------------------------------------------------------------------------------- * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. [Overview](https://docs.docker.com/guides/dotnet/containerize/#overview) ------------------------------------------------------------------------- This section walks you through containerizing and running a .NET application. [Get the sample applications](https://docs.docker.com/guides/dotnet/containerize/#get-the-sample-applications) --------------------------------------------------------------------------------------------------------------- In this guide, you will use a pre-built .NET application. The application is similar to the application built in the Docker Blog article, [Building a Multi-Container .NET App Using Docker Desktop](https://www.docker.com/blog/building-multi-container-net-app-using-docker-desktop/) . Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository. $ git clone https://github.com/docker/docker-dotnet-sample [Initialize Docker assets](https://docs.docker.com/guides/dotnet/containerize/#initialize-docker-assets) --------------------------------------------------------------------------------------------------------- Now that you have an application, you can create the necessary Docker assets to containerize it. You can choose between using the official .NET images or Docker Hardened Images (DHI). > [Docker Hardened Images (DHIs)](https://docs.docker.com/dhi/) > are minimal, secure, and production-ready container base and application images maintained by Docker. DHI images are recommended for better security—they are designed to reduce vulnerabilities and simplify compliance. Using Docker Hardened Images Using the official .NET 10 image Docker Hardened Images (DHIs) for .NET are available in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/aspnetcore) . Docker Hardened Images are freely available to everyone with no subscription required. You can pull and use them like any other Docker image after signing in to the DHI registry. For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the .NET SDK DHI (check the catalog for available versions): $ docker pull dhi.io/dotnet:10-sdk 3. Pull the ASP.NET Core runtime DHI (check the catalog for available versions): $ docker pull dhi.io/aspnetcore:10 You can use `docker init` to generate Docker assets, then modify the Dockerfile to use DHI images: $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? ASP.NET Core ? What's the name of your solution's main project? myWebApp ? What version of .NET do you want to use? 10.0 ? What local port do you want to use to access your server? 8080 In the following Dockerfile, the `FROM` instructions use `dhi.io/dotnet:10-sdk` and `dhi.io/aspnetcore:10` as the base images. Dockerfile # syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM dhi.io/dotnet:10-sdk AS build ARG TARGETARCH COPY . /source WORKDIR /source/src RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app FROM dhi.io/aspnetcore:10 WORKDIR /app COPY --from=build /app . ENTRYPOINT ["dotnet", "myWebApp.dll"] > Note > > DHI runtime images already run as a non-root user (`nonroot`, UID 65532), so there's no need to create a user or specify `USER` in your Dockerfile. This reduces the attack surface and simplifies your configuration. You can use `docker init` to create the necessary Docker assets. Inside the `docker-dotnet-sample` directory, run the `docker init` command in a terminal. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. Refer to the following example to answer the prompts from `docker init` and use the same answers for your prompts. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? ASP.NET Core ? What's the name of your solution's main project? myWebApp ? What version of .NET do you want to use? 10.0 ? What local port do you want to use to access your server? 8080 This generates a Dockerfile using the official .NET 10 images from Microsoft Container Registry: Dockerfile # syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build ARG TARGETARCH COPY . /source WORKDIR /source/src RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final WORKDIR /app COPY --from=build /app . ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser USER appuser ENTRYPOINT ["dotnet", "myWebApp.dll"] You should now have the following contents in your `docker-dotnet-sample` directory. ├── docker-dotnet-sample/ │ ├── .git/ │ ├── src/ │ ├── .dockerignore │ ├── compose.yaml │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md To learn more about the files, see the following: * [Dockerfile](https://docs.docker.com/reference/dockerfile/) * [.dockerignore](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [compose.yaml](https://docs.docker.com/reference/compose-file/) [Run the application](https://docs.docker.com/guides/dotnet/containerize/#run-the-application) ----------------------------------------------------------------------------------------------- Inside the `docker-dotnet-sample` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple web application. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/dotnet/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-dotnet-sample` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple web application. In the terminal, run the following command to stop the application. $ docker compose down For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) . [Summary](https://docs.docker.com/guides/dotnet/containerize/#summary) ----------------------------------------------------------------------- In this section, you learned how you can containerize and run your .NET application using Docker. Related information: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [.dockerignore file reference](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [Docker Compose overview](https://docs.docker.com/compose/) * [Docker Hardened Images](https://docs.docker.com/dhi/) [Next steps](https://docs.docker.com/guides/dotnet/containerize/#next-steps) ----------------------------------------------------------------------------- In the next section, you'll learn how you can develop your application using Docker containers. [Use containers for .NET development »](https://docs.docker.com/guides/dotnet/develop/) --- # Build workflow | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [How to build an AI-powered code quality workflow with SonarQube and E2B](https://docs.docker.com/guides/github-sonarqube-sandbox/) Build AI-powered code quality workflows using E2B sandboxes with Docker's MCP catalog to automate GitHub and SonarQube integration. DevOps 40 minutes [1](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/) [Build workflow](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/) [2](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/) [Customize workflow](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/) [3](https://docs.docker.com/guides/github-sonarqube-sandbox/troubleshoot/) [Troubleshoot](https://docs.docker.com/guides/github-sonarqube-sandbox/troubleshoot/) Resources: * [E2B Documentation](https://e2b.dev/docs) * [Docker MCP Catalog](https://hub.docker.com/mcp) * [Sandboxes](https://docs.docker.com/ai/mcp-catalog-and-toolkit/sandboxes/) [« Back to all guides](https://docs.docker.com/guides/) Build a code quality check workflow =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * In this section, you'll build a complete code quality automation workflow step-by-step. You'll start by creating an E2B sandbox with GitHub and SonarQube MCP servers, then progressively add functionality until you have a production-ready workflow that analyzes code quality and creates pull requests. By working through each step sequentially, you'll learn how MCP servers work, how to interact with them through Claude, and how to chain operations together to build powerful automation workflows. [Prerequisites](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#prerequisites) ------------------------------------------------------------------------------------------------- Before you begin, make sure you have: * E2B account with [API access](https://e2b.dev/docs/api-key) * [Anthropic API key](https://docs.claude.com/en/api/admin-api/apikeys/get-api-key) > Note > > This example uses Claude CLI which comes pre-installed in E2B sandboxes, but you can adapt the example to work with other AI assistants of your choice. See [E2B's MCP documentation](https://e2b.dev/docs/mcp/quickstart) > for alternative connection methods. * GitHub account with: * A repository containing code to analyze * [Personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) with `repo` scope * SonarCloud account with: * [Organization](https://docs.sonarsource.com/sonarqube-cloud/administering-sonarcloud/resources-structure/organization) created * [Project configured](https://docs.sonarsource.com/sonarqube-community-build/project-administration/creating-and-importing-projects) for your repository * [User token](https://docs.sonarsource.com/sonarqube-server/instance-administration/security/administering-tokens) generated * Language runtime installed: * TypeScript: [Node.js 18+](https://nodejs.org/en/download) * Python: [Python 3.8+](https://www.python.org/downloads/) > Note > > This guide uses Claude's `--dangerously-skip-permissions` flag to enable automated command execution in E2B sandboxes. This flag bypasses permission prompts, which is appropriate for isolated container environments like E2B where sandboxes are disposable and separate from your local machine. > > However, be aware that Claude can execute any commands within the sandbox, including accessing files and credentials available in that environment. Only use this approach with trusted code and workflows. For more information, see [Anthropic's guidance on container security](https://docs.anthropic.com/en/docs/claude-code/devcontainer) > . [Set up your project](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#set-up-your-project) ------------------------------------------------------------------------------------------------------------- TypeScript Python 1. Create a new directory for your workflow and initialize Node.js: mkdir github-sonarqube-workflow cd github-sonarqube-workflow npm init -y 2. Open `package.json` and configure it for ES modules: { "name": "github-sonarqube-workflow", "version": "1.0.0", "description": "Automated code quality workflow using E2B, GitHub, and SonarQube", "type": "module", "main": "quality-workflow.ts", "scripts": { "start": "tsx quality-workflow.ts" }, "keywords": ["e2b", "github", "sonarqube", "mcp", "code-quality"], "author": "", "license": "MIT" } 3. Install required dependencies: npm install e2b dotenv npm install -D typescript tsx @types/node 4. Create a `.env` file in your project root: touch .env 5. Add your API keys and configuration, replacing the placeholders with your actual credentials: E2B_API_KEY=your_e2b_api_key_here ANTHROPIC_API_KEY=your_anthropic_api_key_here GITHUB_TOKEN=ghp_your_personal_access_token_here GITHUB_OWNER=your_github_username GITHUB_REPO=your_repository_name SONARQUBE_ORG=your_sonarcloud_org_key SONARQUBE_TOKEN=your_sonarqube_user_token SONARQUBE_URL=https://sonarcloud.io 6. Protect your credentials by adding `.env` to `.gitignore`: echo ".env" >> .gitignore echo "node_modules/" >> .gitignore 1. Create a new directory for your workflow: mkdir github-sonarqube-workflow cd github-sonarqube-workflow 2. Create a virtual environment and activate it: python3 -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate 3. Install required dependencies: pip install e2b python-dotenv 4. Create a `.env` file in your project root: touch .env 5. Add your API keys and configuration, replacing the placeholders with your actual credentials: E2B_API_KEY=your_e2b_api_key_here ANTHROPIC_API_KEY=your_anthropic_api_key_here GITHUB_TOKEN=ghp_your_personal_access_token_here GITHUB_OWNER=your_github_username GITHUB_REPO=your_repository_name SONARQUBE_ORG=your_sonarcloud_org_key SONARQUBE_TOKEN=your_sonarqube_user_token SONARQUBE_URL=https://sonarcloud.io 6. Protect your credentials by adding `.env` to `.gitignore`: echo ".env" >> .gitignore echo "venv/" >> .gitignore echo "__pycache__/" >> .gitignore [Step 1: Create your first sandbox](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#step-1-create-your-first-sandbox) ---------------------------------------------------------------------------------------------------------------------------------------- Let's start by creating a sandbox and verifying the MCP servers are configured correctly. TypeScript Python Create a file named `01-test-connection.ts` in your project root: import "dotenv/config"; import { Sandbox } from "e2b"; async function testConnection() { console.log( "Creating E2B sandbox with GitHub and SonarQube MCP servers...\n", ); const sbx = await Sandbox.betaCreate({ envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, GITHUB_TOKEN: process.env.GITHUB_TOKEN!, SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { org: process.env.SONARQUBE_ORG!, token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, }); const mcpUrl = sbx.betaGetMcpUrl(); const mcpToken = await sbx.betaGetMcpToken(); console.log(" Sandbox created successfully!"); console.log(`MCP Gateway URL: ${mcpUrl}\n`); // Wait for MCP initialization await new Promise((resolve) => setTimeout(resolve, 1000)); // Configure Claude to use the MCP gateway console.log("Connecting Claude CLI to MCP gateway..."); await sbx.commands.run( `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log, }, ); console.log("\nœConnection successful! Cleaning up..."); await sbx.kill(); } testConnection().catch(console.error); Run this script to verify your setup: npx tsx 01-test-connection.ts Create a file named `01_test_connection.py` in your project root: import os import asyncio from dotenv import load_dotenv from e2b import AsyncSandbox load_dotenv() async def test_connection(): print("Creating E2B sandbox with GitHub and SonarQube MCP servers...\n") sbx = await AsyncSandbox.beta_create( envs={ "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), }, mcp={ "githubOfficial": { "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), }, "sonarqube": { "org": os.getenv("SONARQUBE_ORG"), "token": os.getenv("SONARQUBE_TOKEN"), "url": "https://sonarcloud.io", }, }, ) mcp_url = sbx.beta_get_mcp_url() mcp_token = await sbx.beta_get_mcp_token() print(" Sandbox created successfully!") print(f"MCP Gateway URL: {mcp_url}\n") # Wait for MCP initialization await asyncio.sleep(1) # Configure Claude to use the MCP gateway print("Connecting Claude CLI to MCP gateway...") await sbx.commands.run( f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, on_stdout=print, on_stderr=print, ) print("\n Connection successful! Cleaning up...") await sbx.kill() if __name__ == "__main__": asyncio.run(test_connection()) Run this script to verify your setup: python 01_test_connection.py Your output should look similar to the following example: Show more Creating E2B sandbox with GitHub and SonarQube MCP servers... ✓ Sandbox created successfully! MCP Gateway URL: https://50005-xxxxx.e2b.app/mcp Connecting Claude CLI to MCP gateway... Added HTTP MCP server e2b-mcp-gateway with URL: https://50005-xxxxx.e2b.app/mcp to local config Headers: { "Authorization": "Bearer xxxxx-xxxx-xxxx" } File modified: /home/user/.claude.json [project: /home/user] ✓ Connection successful! Cleaning up... Hide You've just learned how to create an E2B sandbox with multiple MCP servers configured. The `betaCreate` method initializes a cloud environment with Claude CLI and your specified MCP servers. [Step 2: Discover available MCP tools](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#step-2-discover-available-mcp-tools) ---------------------------------------------------------------------------------------------------------------------------------------------- MCP servers expose tools that Claude can call. The GitHub MCP server provides repository management tools, while SonarQube provides code analysis tools. By listing their tools, you know what operations are possible. To try listing MCP tools: TypeScript Python Create `02-list-tools.ts`: import "dotenv/config"; import { Sandbox } from "e2b"; async function listTools() { console.log("Creating sandbox...\n"); const sbx = await Sandbox.betaCreate({ envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, GITHUB_TOKEN: process.env.GITHUB_TOKEN!, SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { org: process.env.SONARQUBE_ORG!, token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, }); const mcpUrl = sbx.betaGetMcpUrl(); const mcpToken = await sbx.betaGetMcpToken(); // Wait for MCP initialization await new Promise((resolve) => setTimeout(resolve, 1000)); await sbx.commands.run( `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, ); console.log("\nDiscovering available MCP tools...\n"); const prompt = "List all MCP tools you have access to. For each tool, show its exact name and a brief description."; await sbx.commands.run( `echo '${prompt}' | claude -p --dangerously-skip-permissions`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, ); await sbx.kill(); } listTools().catch(console.error); Run the script: npx tsx 02-list-tools.ts Create `02_list_tools.py`: import os import asyncio from dotenv import load_dotenv from e2b import AsyncSandbox load_dotenv() async def list_tools(): print("Creating sandbox...\n") sbx = await AsyncSandbox.beta_create( envs={ "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), }, mcp={ "githubOfficial": { "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), }, "sonarqube": { "org": os.getenv("SONARQUBE_ORG"), "token": os.getenv("SONARQUBE_TOKEN"), "url": "https://sonarcloud.io", }, }, ) mcp_url = sbx.beta_get_mcp_url() mcp_token = await sbx.beta_get_mcp_token() # Wait for MCP initialization await asyncio.sleep(1) await sbx.commands.run( f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, on_stdout=print, on_stderr=print, ) print("\nDiscovering available MCP tools...\n") prompt = "List all MCP tools you have access to. For each tool, show its exact name and a brief description." await sbx.commands.run( f"echo '{prompt}' | claude -p --dangerously-skip-permissions", timeout=0, on_stdout=print, on_stderr=print, ) await sbx.kill() if __name__ == "__main__": asyncio.run(list_tools()) Run the script: python 02_list_tools.py In the console, you should see a list of MCP tools: Show more Creating sandbox... Sandbox created Connecting to MCP gateway... Discovering available MCP tools... I have access to the following MCP tools: **GitHub Tools:** 1. mcp__create_repository - Create a new GitHub repository 2. mcp__list_issues - List issues in a repository 3. mcp__create_issue - Create a new issue 4. mcp__get_file_contents - Get file contents from a repository 5. mcp__create_or_update_file - Create or update files in a repository 6. mcp__create_pull_request - Create a pull request 7. mcp__create_branch - Create a new branch 8. mcp__push_files - Push multiple files in a single commit ... (30+ more GitHub tools) **SonarQube Tools:** 1. mcp__get_projects - List projects in organization 2. mcp__get_quality_gate_status - Get quality gate status for a project 3. mcp__list_project_issues - List quality issues in a project 4. mcp__search_issues - Search for specific quality issues ... (SonarQube analysis tools) Hide [Step 3: Test GitHub MCP tools](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#step-3-test-github-mcp-tools) -------------------------------------------------------------------------------------------------------------------------------- Let's try testing GitHub using MCP tools. Start simple by listing repository issues. TypeScript Python Create `03-test-github.ts`: import "dotenv/config"; import { Sandbox } from "e2b"; async function testGitHub() { console.log("Creating sandbox...\n"); const sbx = await Sandbox.betaCreate({ envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, GITHUB_TOKEN: process.env.GITHUB_TOKEN!, }, mcp: { githubOfficial: { githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, }, }); const mcpUrl = sbx.betaGetMcpUrl(); const mcpToken = await sbx.betaGetMcpToken(); await new Promise((resolve) => setTimeout(resolve, 1000)); await sbx.commands.run( `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, ); const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; console.log(`\nListing issues in ${repoPath}...\n`); const prompt = `Using the GitHub MCP tools, list all open issues in the repository "${repoPath}". Show the issue number, title, and author for each.`; await sbx.commands.run( `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log, }, ); await sbx.kill(); } testGitHub().catch(console.error); Run the script: npx tsx 03-test-github.ts Create `03_test_github.py`: import os import asyncio from dotenv import load_dotenv from e2b import AsyncSandbox load_dotenv() async def test_github(): print("Creating sandbox...\n") sbx = await AsyncSandbox.beta_create( envs={ "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), }, mcp={ "githubOfficial": { "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), }, }, ) mcp_url = sbx.beta_get_mcp_url() mcp_token = await sbx.beta_get_mcp_token() await asyncio.sleep(1) await sbx.commands.run( f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, on_stdout=print, on_stderr=print, ) repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" print(f"\nListing issues in {repo_path}...\n") prompt = f'Using the GitHub MCP tools, list all open issues in the repository "{repo_path}". Show the issue number, title, and author for each.' await sbx.commands.run( f"echo '{prompt}' | claude -p --dangerously-skip-permissions", timeout=0, on_stdout=print, on_stderr=print, ) await sbx.kill() if __name__ == "__main__": asyncio.run(test_github()) Run the script: python 03_test_github.py You should see Claude use the GitHub MCP tools to list your repository's issues: Show more Creating sandbox... Connecting to MCP gateway... Listing issues in ... Here are the first 10 open issues in the repository: 1. **Issue #23577**: Update README (author: user1) 2. **Issue #23575**: release-notes for Compose v2.40.1 version (author: user2) 3. **Issue #23570**: engine-cli: fix `docker volume prune` output (author: user3) 4. **Issue #23568**: Engdocs update (author: user4) 5. **Issue #23565**: add new section (author: user5) ... (continues with more issues) Hide You can now send prompts to Claude and interact with GitHub through natural language. Claude decides what tool to call based on your prompt. [Step 4: Test SonarQube MCP tools](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#step-4-test-sonarqube-mcp-tools) -------------------------------------------------------------------------------------------------------------------------------------- Let's analyze code quality using SonarQube MCP tools. TypeScript Python Create `04-test-sonarqube.ts`: import "dotenv/config"; import { Sandbox } from "e2b"; async function testSonarQube() { console.log("Creating sandbox...\n"); const sbx = await Sandbox.betaCreate({ envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, GITHUB_TOKEN: process.env.GITHUB_TOKEN!, SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { org: process.env.SONARQUBE_ORG!, token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, }); const mcpUrl = sbx.betaGetMcpUrl(); const mcpToken = await sbx.betaGetMcpToken(); await new Promise((resolve) => setTimeout(resolve, 1000)); await sbx.commands.run( `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, ); console.log("\nAnalyzing code quality with SonarQube...\n"); const prompt = `Using the SonarQube MCP tools: 1. List all projects in my organization 2. For the first project, show: - Quality gate status (pass/fail) - Number of bugs - Number of code smells - Number of security vulnerabilities 3. List the top 5 most critical issues found`; await sbx.commands.run( `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log, }, ); await sbx.kill(); } testSonarQube().catch(console.error); Run the script: npx tsx 04-test-sonarqube.ts Create `04_test_sonarqube.py`: import os import asyncio from dotenv import load_dotenv from e2b import AsyncSandbox load_dotenv() async def test_sonarqube(): print("Creating sandbox...\n") sbx = await AsyncSandbox.beta_create( envs={ "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), }, mcp={ "githubOfficial": { "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), }, "sonarqube": { "org": os.getenv("SONARQUBE_ORG"), "token": os.getenv("SONARQUBE_TOKEN"), "url": "https://sonarcloud.io", }, }, ) mcp_url = sbx.beta_get_mcp_url() mcp_token = await sbx.beta_get_mcp_token() await asyncio.sleep(1) await sbx.commands.run( f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, on_stdout=print, on_stderr=print, ) print("\nAnalyzing code quality with SonarQube...\n") prompt = """Using the SonarQube MCP tools: 1. List all projects in my organization 2. For the first project, show: - Quality gate status (pass/fail) - Number of bugs - Number of code smells - Number of security vulnerabilities 3. List the top 5 most critical issues found""" await sbx.commands.run( f"echo '{prompt}' | claude -p --dangerously-skip-permissions", timeout=0, on_stdout=print, on_stderr=print, ) await sbx.kill() if __name__ == "__main__": asyncio.run(test_sonarqube()) Run the script: python 04_test_sonarqube.py > Note > > This script may take a few minutes to run. You should see Claude output SonarQube analysis results: Show more Creating sandbox... Analyzing code quality with SonarQube... ## SonarQube Analysis Results ### 1. Projects in Your Organization Found **1 project**: - **Project Name**: project-1 - **Project Key**: project-testing ### 2. Project Analysis ... ### 3. Top 5 Most Critical Issues Found 1 total issues (all are code smells with no critical/blocker severity): 1. **MAJOR Severity** - test.js:2 - **Rule**: javascript:S1854 - **Message**: Remove this useless assignment to variable "unusedVariable" - **Status**: OPEN **Summary**: The project is in good health with no bugs or vulnerabilities detected. Hide You can now use SonarQube MCP tools to analyze code quality through natural language. You can retrieve quality metrics, identify issues, and understand what code needs fixing. [Step 5: Create a branch and make code changes](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#step-5-create-a-branch-and-make-code-changes) ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Now, let's teach Claude to fix code based on quality issues discovered by SonarQube. TypeScript Python Create `05-fix-code-issue.ts`: import "dotenv/config"; import { Sandbox } from "e2b"; async function fixCodeIssue() { console.log("Creating sandbox...\n"); const sbx = await Sandbox.betaCreate({ envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, GITHUB_TOKEN: process.env.GITHUB_TOKEN!, SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { org: process.env.SONARQUBE_ORG!, token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, }); const mcpUrl = sbx.betaGetMcpUrl(); const mcpToken = await sbx.betaGetMcpToken(); await new Promise((resolve) => setTimeout(resolve, 1000)); await sbx.commands.run( `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, ); const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; const branchName = `quality-fix-${Date.now()}`; console.log("\nFixing a code quality issue...\n"); const prompt = `Using GitHub and SonarQube MCP tools: 1. Analyze code quality in repository "${repoPath}" with SonarQube 2. Find ONE simple issue that can be confidently fixed (like an unused variable or code smell) 3. Create a new branch called "${branchName}" 4. Read the file containing the issue using GitHub tools 5. Fix the issue in the code 6. Commit the fix to the new branch with a clear commit message Important: Only fix issues you're 100% confident about. Explain what you're fixing and why.`; await sbx.commands.run( `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log, }, ); console.log(`\nœCheck your repository for branch: ${branchName}`); await sbx.kill(); } fixCodeIssue().catch(console.error); Run the script: npx tsx 05-fix-code-issue.ts Create `05_fix_code_issue.py`: import os import asyncio import time from dotenv import load_dotenv from e2b import AsyncSandbox load_dotenv() async def fix_code_issue(): print("Creating sandbox...\n") sbx = await AsyncSandbox.beta_create( envs={ "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), }, mcp={ "githubOfficial": { "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), }, "sonarqube": { "org": os.getenv("SONARQUBE_ORG"), "token": os.getenv("SONARQUBE_TOKEN"), "url": "https://sonarcloud.io", }, }, ) mcp_url = sbx.beta_get_mcp_url() mcp_token = await sbx.beta_get_mcp_token() await asyncio.sleep(1) await sbx.commands.run( f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, on_stdout=print, on_stderr=print, ) repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" branch_name = f"quality-fix-{int(time.time() * 1000)}" print("\nFixing a code quality issue...\n") prompt = f"""Using GitHub and SonarQube MCP tools: 1. Analyze code quality in repository "{repo_path}" with SonarQube 2. Find ONE simple issue that can be confidently fixed (like an unused variable or code smell) 3. Create a new branch called "{branch_name}" 4. Read the file containing the issue using GitHub tools 5. Fix the issue in the code 6. Commit the fix to the new branch with a clear commit message Important: Only fix issues you're 100% confident about. Explain what you're fixing and why.""" await sbx.commands.run( f"echo '{prompt}' | claude -p --dangerously-skip-permissions", timeout=0, on_stdout=print, on_stderr=print, ) print(f"\n Check your repository for branch: {branch_name}") await sbx.kill() if __name__ == "__main__": asyncio.run(fix_code_issue()) Run the script: python 05_fix_code_issue.py > Note > > This script may take a few minutes to run. Claude will analyze your repository and fix a code quality issue: Show more Creating sandbox... Fixing a code quality issue... ## Task Completed Successfully! I've successfully analyzed the repository and fixed a code quality issue. Here's what I did: ### 1. Analysis Found 1 SonarQube issues in the `` repository: - **Unused variable** (MINOR severity, line 2) - Useless assignment (MAJOR severity, line 2) - For-of loop suggestion (MINOR severity, lines 5-7) ### 2. Issue Fixed **Location:** test.js:2 **Issue:** Unused variable declaration **SonarQube Rule:** javascript:S1481 **What was wrong:** The line `const unusedVariable = "this is not used";` declared a variable that was never used anywhere in the function. ### 3. Why This Fix is Safe - The variable was only declared and assigned, never referenced - Removing it has zero impact on functionality - It eliminates dead code and improves code quality - This is a textbook example of a code smell that should be removed ### 4. Changes Made - Created branch: `quality-fix-xxxxx` - Removed the unused variable declaration - Committed with a clear message explaining the fix and referencing the SonarQube rule **Commit SHA:** `xxxxxxxxxxxxxxxxxxxxxxxxx` **Branch:** https://github.com///tree/quality-fix-xxxxx The fix is ready for review and can be merged to improve the code quality of the repository! Hide You can now use GitHub and SonarQube MCP tools in the same workflow to read files, make code changes, and commit them. [Step 6: Create quality-gated pull requests](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#step-6-create-quality-gated-pull-requests) ---------------------------------------------------------------------------------------------------------------------------------------------------------- Finally, let's build the complete workflow: analyze quality, fix issues, and create a PR only if improvements are made. TypeScript Python Create `06-quality-gated-pr.ts`: import "dotenv/config"; import { Sandbox } from "e2b"; async function qualityGatedPR() { console.log("Creating sandbox for quality-gated PR workflow...\n"); const sbx = await Sandbox.betaCreate({ envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, GITHUB_TOKEN: process.env.GITHUB_TOKEN!, SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { org: process.env.SONARQUBE_ORG!, token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, }); const mcpUrl = sbx.betaGetMcpUrl(); const mcpToken = await sbx.betaGetMcpToken(); await new Promise((resolve) => setTimeout(resolve, 1000)); await sbx.commands.run( `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, ); const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; const branchName = `quality-improvements-${Date.now()}`; console.log("\nRunning quality-gated PR workflow...\n"); const prompt = `You are a code quality engineer. Using GitHub and SonarQube MCP tools: STEP 1: ANALYSIS - Get current code quality status from SonarQube for "${repoPath}" - Record the current number of bugs, code smells, and vulnerabilities - Identify 1-3 issues that you can confidently fix STEP 2: FIX ISSUES - Create branch "${branchName}" - For each issue you're fixing: * Read the file with the issue * Make the fix * Commit with a descriptive message - Only fix issues where you're 100% confident the fix is correct STEP 3: VERIFICATION - After your fixes, check if quality metrics would improve - Calculate: Would this reduce bugs/smells/vulnerabilities? STEP 4: QUALITY GATE - Only proceed if your changes improve quality - If quality would not improve, explain why and stop STEP 5: CREATE PR (only if quality gate passes) - Create a pull request from "${branchName}" to main - Title: "Quality improvements: [describe what you fixed]" - Description should include: * What issues you fixed * Before/after quality metrics * Why these fixes improve code quality - Add a comment with detailed SonarQube analysis Be thorough and explain your decisions at each step.`; await sbx.commands.run( `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log, }, ); console.log(`\n Workflow complete! Check ${repoPath} for new pull request.`); await sbx.kill(); } qualityGatedPR().catch(console.error); Run the script: npx tsx 06-quality-gated-pr.ts Create `06_quality_gated_pr.py`: import os import asyncio import time from dotenv import load_dotenv from e2b import AsyncSandbox load_dotenv() async def quality_gated_pr(): print("Creating sandbox for quality-gated PR workflow...\n") sbx = await AsyncSandbox.beta_create( envs={ "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), }, mcp={ "githubOfficial": { "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), }, "sonarqube": { "org": os.getenv("SONARQUBE_ORG"), "token": os.getenv("SONARQUBE_TOKEN"), "url": "https://sonarcloud.io", }, }, ) mcp_url = sbx.beta_get_mcp_url() mcp_token = await sbx.beta_get_mcp_token() await asyncio.sleep(1) await sbx.commands.run( f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, on_stdout=print, on_stderr=print, ) repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" branch_name = f"quality-improvements-{int(time.time() * 1000)}" print("\nRunning quality-gated PR workflow...\n") prompt = f"""You are a code quality engineer. Using GitHub and SonarQube MCP tools: STEP 1: ANALYSIS - Get current code quality status from SonarQube for "{repo_path}" - Record the current number of bugs, code smells, and vulnerabilities - Identify 1-3 issues that you can confidently fix STEP 2: FIX ISSUES - Create branch "{branch_name}" - For each issue you are fixing: Read the file with the issue Make the fix Commit with a descriptive message - Only fix issues where you are 100 percent confident the fix is correct STEP 3: VERIFICATION - After your fixes, check if quality metrics would improve - Calculate: Would this reduce bugs/smells/vulnerabilities? STEP 4: QUALITY GATE - Only proceed if your changes improve quality - If quality would not improve, explain why and stop STEP 5: CREATE PR (only if quality gate passes) - Create a pull request from "{branch_name}" to main - Title: "Quality improvements: [describe what you fixed]" - Description should include: What issues you fixed Before/after quality metrics Why these fixes improve code quality - Add a comment with detailed SonarQube analysis Be thorough and explain your decisions at each step.""" await sbx.commands.run( f"echo '{prompt.replace(chr(39), chr(39) + chr(92) + chr(39) + chr(39))}' | claude -p --dangerously-skip-permissions", timeout=0, on_stdout=print, on_stderr=print, ) print(f"\n Workflow complete! Check {repo_path} for new pull request.") await sbx.kill() if __name__ == "__main__": asyncio.run(quality_gated_pr()) Run the script: python 06_quality_gated_pr.py > Note > > This script may take a few minutes to run. Claude will run the entire workflow, creating a quality improvement and opening a PR in GitHub: Show more Creating sandbox for quality-gated PR workflow... Running quality-gated PR workflow... ## Complete - Code Quality Improvement Successfully Executed ### Summary of Work Completed **Pull Request Created:** https://github.com///pull/1 --- ### STEP 1: ANALYSIS **Current State:** - **Bugs:** 0 - **Vulnerabilities:** 0 - **Code Smells:** 1 - **Lines of Code:** 12 - **All Ratings:** A (Reliability, Security, Maintainability) **Issues Identified (all in test.js):** 1. xxxxxxxxx - Unused variable declaration (MINOR) --- ### STEP 2: FIX ISSUES **Branch Created:** `quality-improvements-xxxxxxxx` **Changes Made:** 1. **Removed unused variable** (line 2) - Eliminated dead code that served no purpose 2. **Modernized loop pattern** (lines 5-7) - Converted `for (let i = 0; i < items.length; i++)` to `for (const item of items)` **Commit:** xxxxxxxxxx --- ### STEP 3: VERIFICATION **Expected Impact:** - Code Smells: 1 → 0 (100% reduction) - Bugs: 0 → 0 (maintained) - Vulnerabilities: 0 → 0 (maintained) - All quality ratings maintained at A --- ### STEP 4: QUALITY GATE PASSED **Decision Criteria Met:** - ✅ Reduces code smells by 100% - ✅ No new bugs or vulnerabilities introduced - ✅ Code is more readable and maintainable - ✅ Follows modern JavaScript best practices - ✅ All fixes are low-risk refactorings with no behavioral changes --- ### STEP 5: CREATE PR **Pull Request Details:** - **Number:** #1 - **Title:** Quality improvements: Remove unused variable and modernize for loop - **Branch:** quality-improvements-xxxxxxxx → main - **URL:** https://github.com//pull/1 **PR Includes:** - Comprehensive description with before/after metrics - Detailed SonarQube analysis comment with issue breakdown - Code comparison showing improvements - Quality metrics table The pull request is now ready for review and merge! Hide You've now built a complete, multi-step workflow with conditional logic. Claude analyzes quality with SonarQube, makes fixes using GitHub tools, verifies improvements, and only creates a PR if quality actually improves. [Step 7: Add error handling](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#step-7-add-error-handling) -------------------------------------------------------------------------------------------------------------------------- Production workflows need error handling. Let's make the workflow more robust. TypeScript Python Create `07-robust-workflow.ts`: import "dotenv/config"; import { Sandbox } from "e2b"; async function robustWorkflow() { let sbx: Sandbox | undefined; try { console.log("Creating sandbox...\n"); sbx = await Sandbox.betaCreate({ envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY!, GITHUB_TOKEN: process.env.GITHUB_TOKEN!, SONARQUBE_TOKEN: process.env.SONARQUBE_TOKEN!, }, mcp: { githubOfficial: { githubPersonalAccessToken: process.env.GITHUB_TOKEN!, }, sonarqube: { org: process.env.SONARQUBE_ORG!, token: process.env.SONARQUBE_TOKEN!, url: "https://sonarcloud.io", }, }, }); const mcpUrl = sbx.betaGetMcpUrl(); const mcpToken = await sbx.betaGetMcpToken(); await new Promise((resolve) => setTimeout(resolve, 1000)); await sbx.commands.run( `claude mcp add --transport http e2b-mcp-gateway ${mcpUrl} --header "Authorization: Bearer ${mcpToken}"`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log }, ); const repoPath = `${process.env.GITHUB_OWNER}/${process.env.GITHUB_REPO}`; console.log("\nRunning workflow with error handling...\n"); const prompt = `Run a quality improvement workflow for "${repoPath}". ERROR HANDLING RULES: 1. If SonarQube is unreachable, explain the error and stop gracefully 2. If GitHub API fails, retry once, then explain and stop 3. If no fixable issues are found, explain why and exit (this is not an error) 4. If file modifications fail, explain which file and why 5. At each step, check for errors before proceeding Run the workflow and handle any errors you encounter professionally.`; await sbx.commands.run( `echo '${prompt.replace(/'/g, "'\\''")}' | claude -p --dangerously-skip-permissions`, { timeoutMs: 0, onStdout: console.log, onStderr: console.log, }, ); console.log("\n Workflow completed"); } catch (error) { const err = error as Error; console.error("\n Workflow failed:", err.message); if (err.message.includes("403")) { console.error("\n Check your E2B account has MCP gateway access"); } else if (err.message.includes("401")) { console.error("\n Check your API tokens are valid"); } else if (err.message.includes("Credit balance")) { console.error("\n Check your Anthropic API credit balance"); } process.exit(1); } finally { if (sbx) { console.log("\n Cleaning up sandbox..."); await sbx.kill(); } } } robustWorkflow().catch(console.error); Run the script: npx tsx 07-robust-workflow.ts Create `07_robust_workflow.py`: import os import asyncio import sys from dotenv import load_dotenv from e2b import AsyncSandbox load_dotenv() async def robust_workflow(): sbx = None try: print("Creating sandbox...\n") sbx = await AsyncSandbox.beta_create( envs={ "ANTHROPIC_API_KEY": os.getenv("ANTHROPIC_API_KEY"), "GITHUB_TOKEN": os.getenv("GITHUB_TOKEN"), "SONARQUBE_TOKEN": os.getenv("SONARQUBE_TOKEN"), }, mcp={ "githubOfficial": { "githubPersonalAccessToken": os.getenv("GITHUB_TOKEN"), }, "sonarqube": { "org": os.getenv("SONARQUBE_ORG"), "token": os.getenv("SONARQUBE_TOKEN"), "url": "https://sonarcloud.io", }, }, ) mcp_url = sbx.beta_get_mcp_url() mcp_token = await sbx.beta_get_mcp_token() await asyncio.sleep(1) await sbx.commands.run( f'claude mcp add --transport http e2b-mcp-gateway {mcp_url} --header "Authorization: Bearer {mcp_token}"', timeout=0, # Fixed: was timeout_ms on_stdout=print, on_stderr=print, ) repo_path = f"{os.getenv('GITHUB_OWNER')}/{os.getenv('GITHUB_REPO')}" print("\nRunning workflow with error handling...\n") prompt = f"""Run a quality improvement workflow for "{repo_path}". ERROR HANDLING RULES: 1. If SonarQube is unreachable, explain the error and stop gracefully 2. If GitHub API fails, retry once, then explain and stop 3. If no fixable issues are found, explain why and exit (this is not an error) 4. If file modifications fail, explain which file and why 5. At each step, check for errors before proceeding Run the workflow and handle any errors you encounter professionally.""" await sbx.commands.run( f"echo '{prompt}' | claude -p --dangerously-skip-permissions", timeout=0, on_stdout=print, on_stderr=print, ) print("\n Workflow completed") except Exception as error: print(f"\n✗ Workflow failed: {str(error)}") error_msg = str(error) if "403" in error_msg: print("\n Check your E2B account has MCP gateway access") elif "401" in error_msg: print("\n Check your API tokens are valid") elif "Credit balance" in error_msg: print("\n Check your Anthropic API credit balance") sys.exit(1) finally: if sbx: print("\n Cleaning up sandbox...") await sbx.kill() if __name__ == "__main__": asyncio.run(robust_workflow()) Run the script: python 07_robust_workflow.py Claude will run the entire workflow, and if it encounters an error, respond with robust error messaging. [Next steps](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/#next-steps) ------------------------------------------------------------------------------------------- In the next section, you'll customize your workflow for your needs. [Customize a code quality check workflow »](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing Quarkus applications with Testcontainers](https://docs.docker.com/guides/testcontainers-java-quarkus/) Learn how to create a Quarkus REST API with Hibernate ORM with Panache and PostgreSQL, then test it using Quarkus Dev Services, Testcontainers, and REST Assured. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-quarkus/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-quarkus/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-quarkus/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-quarkus/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-quarkus/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-quarkus/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Quarkus project ========================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-quarkus/create-project/#set-up-the-project) -------------------------------------------------------------------------------------------------------------------- Create a Quarkus project from [code.quarkus.io](https://code.quarkus.io/) by selecting the **RESTEasy Classic**, **RESTEasy Classic Jackson**, **Hibernate Validator**, **Hibernate ORM with Panache**, **JDBC Driver - PostgreSQL**, and **Flyway** extensions. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testcontainers-in-quarkus-applications) . The key dependencies in `pom.xml` are: 3.22.3 io.quarkus quarkus-hibernate-orm-panache io.quarkus quarkus-flyway io.quarkus quarkus-hibernate-validator io.quarkus quarkus-resteasy io.quarkus quarkus-resteasy-jackson io.quarkus quarkus-jdbc-postgresql io.quarkus quarkus-junit5 test io.rest-assured rest-assured test [Create the JPA entity](https://docs.docker.com/guides/testcontainers-java-quarkus/create-project/#create-the-jpa-entity) -------------------------------------------------------------------------------------------------------------------------- Hibernate ORM with Panache supports the Active Record pattern and the Repository pattern to simplify JPA usage. This guide uses the Active Record pattern. Create `Customer.java` by extending `PanacheEntity`. This gives the entity built-in persistence methods such as `persist()`, `listAll()`, and `findById()`. package com.testcontainers.demo; import io.quarkus.hibernate.orm.panache.PanacheEntity; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Table; @Entity @Table(name = "customers") public class Customer extends PanacheEntity { @Column(nullable = false) public String name; @Column(nullable = false, unique = true) public String email; public Customer() {} public Customer(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } } [Create the CustomerService CDI bean](https://docs.docker.com/guides/testcontainers-java-quarkus/create-project/#create-the-customerservice-cdi-bean) ------------------------------------------------------------------------------------------------------------------------------------------------------ Create a `CustomerService` class annotated with `@ApplicationScoped` and `@Transactional` to handle persistence operations: package com.testcontainers.demo; import jakarta.enterprise.context.ApplicationScoped; import jakarta.transaction.Transactional; import java.util.List; @ApplicationScoped @Transactional public class CustomerService { public List getAll() { return Customer.listAll(); } public Customer create(Customer customer) { customer.persist(); return customer; } } [Add the Flyway database migration script](https://docs.docker.com/guides/testcontainers-java-quarkus/create-project/#add-the-flyway-database-migration-script) ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Create `src/main/resources/db/migration/V1__init_database.sql`: create sequence customers_seq start with 1 increment by 50; create table customers ( id bigint DEFAULT nextval('customers_seq') not null, name varchar not null, email varchar not null, primary key (id) ); insert into customers(name, email) values ('john', 'john@mail.com'), ('rambo', 'rambo@mail.com'); Enable Flyway migrations in `src/main/resources/application.properties`: quarkus.flyway.migrate-at-start=true [Create the REST API endpoints](https://docs.docker.com/guides/testcontainers-java-quarkus/create-project/#create-the-rest-api-endpoints) ------------------------------------------------------------------------------------------------------------------------------------------ Create `CustomerResource.java` with endpoints for fetching all customers and creating a customer: package com.testcontainers.demo; import jakarta.ws.rs.Consumes; import jakarta.ws.rs.GET; import jakarta.ws.rs.POST; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; import jakarta.ws.rs.core.MediaType; import jakarta.ws.rs.core.Response; import java.util.List; @Path("/api/customers") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) public class CustomerResource { private final CustomerService customerService; public CustomerResource(CustomerService customerService) { this.customerService = customerService; } @GET public List getAllCustomers() { return customerService.getAll(); } @POST public Response createCustomer(Customer customer) { var savedCustomer = customerService.create(customer); return Response.status(Response.Status.CREATED).entity(savedCustomer).build(); } } [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-quarkus/write-tests/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Ruby on Rails language-specific guide](https://docs.docker.com/guides/ruby/) This guide explains how to containerize Ruby on Rails applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/ruby/ruby-original.svg "Ruby") Ruby Frameworks 20 minutes [1](https://docs.docker.com/guides/ruby/containerize/) [Containerize your app](https://docs.docker.com/guides/ruby/containerize/) [2](https://docs.docker.com/guides/ruby/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/ruby/configure-github-actions/) [3](https://docs.docker.com/guides/ruby/develop/) [Develop your app](https://docs.docker.com/guides/ruby/develop/) [4](https://docs.docker.com/guides/ruby/deploy/) [Test your deployment](https://docs.docker.com/guides/ruby/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Ruby on Rails application ======================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/ruby/containerize/#prerequisites) --------------------------------------------------------------------------------- * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [Git client](https://git-scm.com/downloads) . The examples in this section show the Git CLI, but you can use any client. [Overview](https://docs.docker.com/guides/ruby/containerize/#overview) ----------------------------------------------------------------------- This section walks you through containerizing and running a [Ruby on Rails](https://rubyonrails.org/) application. Starting from Rails 7.1 [Docker is supported out of the box](https://guides.rubyonrails.org/7_1_release_notes.html#generate-dockerfiles-for-new-rails-applications) . This means that you will get a `Dockerfile`, `.dockerignore` and `bin/docker-entrypoint` files generated for you when you create a new Rails application. If you have an existing Rails application, you will need to create the Docker assets manually. Unfortunately `docker init` command does not yet support Rails. This means that if you are working with Rails, you'll need to copy Dockerfile and other related configurations manually from the examples below. [1\. Initialize Docker assets](https://docs.docker.com/guides/ruby/containerize/#1-initialize-docker-assets) ------------------------------------------------------------------------------------------------------------- Rails 7.1 and newer generates multistage Dockerfile out of the box. Following are two versions of such a file: one using Docker Hardened Images (DHIs) and another using the Docker Official Image (DOIs). Although the Dockerfile is generated automatically, understanding its purpose and functionality is important. Reviewing the following example is highly recommended. [Docker Hardened Images (DHIs)](https://docs.docker.com/dhi/) are minimal, secure, and production-ready container base and application images maintained by Docker. DHIs are recommended whenever it is possible for better security. They are designed to reduce vulnerabilities and simplify compliance, freely available to everyone with no subscription required, no usage restrictions, and no vendor lock-in. Multistage Dockerfiles help create smaller, more efficient images by separating build and runtime dependencies, ensuring only necessary components are included in the final image. Read more in the [Multi-stage builds guide](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/) . Using DHIs Using DOIs You must authenticate to `dhi.io` before you can pull Docker Hardened Images. Run `docker login dhi.io` to authenticate. Dockerfile # syntax=docker/dockerfile:1 # check=error=true # This Dockerfile is designed for production, not development. # docker build -t app . # docker run -d -p 80:80 -e RAILS_MASTER_KEY= --name app app # For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html # Make sure RUBY_VERSION matches the Ruby version in .ruby-version ARG RUBY_VERSION=3.4.8 FROM dhi.io/ruby:$RUBY_VERSION-dev AS base # Rails app lives here WORKDIR /rails # Install base packages # Replace libpq-dev with sqlite3 if using SQLite, or libmysqlclient-dev if using MySQL RUN apt-get update -qq && \ apt-get install --no-install-recommends -y curl libjemalloc2 libvips libpq-dev && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Set production environment ENV RAILS_ENV="production" \ BUNDLE_DEPLOYMENT="1" \ BUNDLE_PATH="/usr/local/bundle" \ BUNDLE_WITHOUT="development" # Throw-away build stage to reduce size of final image FROM base AS build # Install packages needed to build gems RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential curl git pkg-config libyaml-dev && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Install JavaScript dependencies and Node.js for asset compilation # # Uncomment the following lines if you are using NodeJS need to compile assets # # ARG NODE_VERSION=18.12.0 # ARG YARN_VERSION=1.22.19 # ENV PATH=/usr/local/node/bin:$PATH # RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ # /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \ # npm install -g yarn@$YARN_VERSION && \ # npm install -g mjml && \ # rm -rf /tmp/node-build-master # Install application gems COPY Gemfile Gemfile.lock ./ RUN bundle install && \ rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ bundle exec bootsnap precompile --gemfile # Install node modules # # Uncomment the following lines if you are using NodeJS need to compile assets # # COPY package.json yarn.lock ./ # RUN --mount=type=cache,id=yarn,target=/rails/.cache/yarn YARN_CACHE_FOLDER=/rails/.cache/yarn \ # yarn install --frozen-lockfile # Copy application code COPY . . # Precompile bootsnap code for faster boot times RUN bundle exec bootsnap precompile app/ lib/ # Precompiling assets for production without requiring secret RAILS_MASTER_KEY RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile # Final stage for app image FROM base # Copy built artifacts: gems, application COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" COPY --from=build /rails /rails # Run and own only the runtime files as a non-root user for security RUN groupadd --system --gid 1000 rails && \ useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ chown -R rails:rails db log storage tmp USER 1000:1000 # Entrypoint prepares the database. ENTRYPOINT ["/rails/bin/docker-entrypoint"] # Start server via Thruster by default, this can be overwritten at runtime EXPOSE 80 CMD ["./bin/thrust", "./bin/rails", "server"] Dockerfile # syntax=docker/dockerfile:1 # check=error=true # This Dockerfile is designed for production, not development. # docker build -t app . # docker run -d -p 80:80 -e RAILS_MASTER_KEY= --name app app # For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html # Make sure RUBY_VERSION matches the Ruby version in .ruby-version ARG RUBY_VERSION=3.4.8 FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base # Rails app lives here WORKDIR /rails # Install base packages # Replace libpq-dev with sqlite3 if using SQLite, or libmysqlclient-dev if using MySQL RUN apt-get update -qq && \ apt-get install --no-install-recommends -y curl libjemalloc2 libvips libpq-dev && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Set production environment ENV RAILS_ENV="production" \ BUNDLE_DEPLOYMENT="1" \ BUNDLE_PATH="/usr/local/bundle" \ BUNDLE_WITHOUT="development" # Throw-away build stage to reduce size of final image FROM base AS build # Install packages needed to build gems RUN apt-get update -qq && \ apt-get install --no-install-recommends -y build-essential curl git pkg-config libyaml-dev && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Install JavaScript dependencies and Node.js for asset compilation # # Uncomment the following lines if you are using NodeJS need to compile assets # # ARG NODE_VERSION=18.12.0 # ARG YARN_VERSION=1.22.19 # ENV PATH=/usr/local/node/bin:$PATH # RUN curl -sL https://github.com/nodenv/node-build/archive/master.tar.gz | tar xz -C /tmp/ && \ # /tmp/node-build-master/bin/node-build "${NODE_VERSION}" /usr/local/node && \ # npm install -g yarn@$YARN_VERSION && \ # npm install -g mjml && \ # rm -rf /tmp/node-build-master # Install application gems COPY Gemfile Gemfile.lock ./ RUN bundle install && \ rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \ bundle exec bootsnap precompile --gemfile # Install node modules # # Uncomment the following lines if you are using NodeJS need to compile assets # # COPY package.json yarn.lock ./ # RUN --mount=type=cache,id=yarn,target=/rails/.cache/yarn YARN_CACHE_FOLDER=/rails/.cache/yarn \ # yarn install --frozen-lockfile # Copy application code COPY . . # Precompile bootsnap code for faster boot times RUN bundle exec bootsnap precompile app/ lib/ # Precompiling assets for production without requiring secret RAILS_MASTER_KEY RUN SECRET_KEY_BASE_DUMMY=1 ./bin/rails assets:precompile # Final stage for app image FROM base # Copy built artifacts: gems, application COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}" COPY --from=build /rails /rails # Run and own only the runtime files as a non-root user for security RUN groupadd --system --gid 1000 rails && \ useradd rails --uid 1000 --gid 1000 --create-home --shell /bin/bash && \ chown -R rails:rails db log storage tmp USER 1000:1000 # Entrypoint prepares the database. ENTRYPOINT ["/rails/bin/docker-entrypoint"] # Start server via Thruster by default, this can be overwritten at runtime EXPOSE 80 CMD ["./bin/thrust", "./bin/rails", "server"] The Dockerfile above assumes you are using Thruster together with Puma as an application server. In case you are using any other server, you can replace the last three lines with the following: # Start the application server EXPOSE 3000 CMD ["./bin/rails", "server"] This Dockerfile uses a script at `./bin/docker-entrypoint` as the container's entrypoint. This script prepares the database and runs the application server. Below is an example of such a script. docker-entrypoint #!/bin/bash -e # Enable jemalloc for reduced memory usage and latency. if [ -z "${LD_PRELOAD+x}" ]; then LD_PRELOAD=$(find /usr/lib -name libjemalloc.so.2 -print -quit) export LD_PRELOAD fi # If running the rails server then create or migrate existing database if [ "${@: -2:1}" == "./bin/rails" ] && [ "${@: -1:1}" == "server" ]; then ./bin/rails db:prepare fi exec "${@}" Besides the two files above you will also need a `.dockerignore` file. This file is used to exclude files and directories from the context of the build. Below is an example of a `.dockerignore` file. .dockerignore Show more # See https://docs.docker.com/engine/reference/builder/#dockerignore-file for more about ignoring files. # Ignore git directory. /.git/ /.gitignore # Ignore bundler config. /.bundle # Ignore all environment files. /.env* # Ignore all default key files. /config/master.key /config/credentials/*.key # Ignore all logfiles and tempfiles. /log/* /tmp/* !/log/.keep !/tmp/.keep # Ignore pidfiles, but keep the directory. /tmp/pids/* !/tmp/pids/.keep # Ignore storage (uploaded files in development and any SQLite databases). /storage/* !/storage/.keep /tmp/storage/* !/tmp/storage/.keep # Ignore assets. /node_modules/ /app/assets/builds/* !/app/assets/builds/.keep /public/assets # Ignore CI service files. /.github # Ignore development files /.devcontainer # Ignore Docker-related files /.dockerignore /Dockerfile* Hide The last optional file that you may want is the `compose.yaml` file, which is used by Docker Compose to define the services that make up the application. Since SQLite is being used as the database, there is no need to define a separate service for the database. The only service required is the Rails application itself. compose.yaml services: web: build: . environment: - RAILS_MASTER_KEY ports: - "3000:80" You should now have the following files in your application folder: * `.dockerignore` * `compose.yaml` * `Dockerfile` * `bin/docker-entrypoint` To learn more about the files, see the following: * [Dockerfile](https://docs.docker.com/reference/dockerfile) * [.dockerignore](https://docs.docker.com/reference/dockerfile#dockerignore-file) * [compose.yaml](https://docs.docker.com/reference/compose-file/) * [docker-entrypoint](https://docs.docker.com/reference/dockerfile/#entrypoint) [2\. Run the application](https://docs.docker.com/guides/ruby/containerize/#2-run-the-application) --------------------------------------------------------------------------------------------------- To run the application, run the following command in a terminal inside the application's directory. $ RAILS_MASTER_KEY= docker compose up --build Open a browser and view the application at [http://localhost:3000](http://localhost:3000/) . You should see a simple Ruby on Rails application. In the terminal, press `ctrl`+`c` to stop the application. [3\. Run the application in the background](https://docs.docker.com/guides/ruby/containerize/#3-run-the-application-in-the-background) --------------------------------------------------------------------------------------------------------------------------------------- You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-ruby-on-rails` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:3000](http://localhost:3000/) . You should see a simple Ruby on Rails application. In the terminal, run the following command to stop the application. $ docker compose down For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) . [Summary](https://docs.docker.com/guides/ruby/containerize/#summary) --------------------------------------------------------------------- In this section, you learned how you can containerize and run your Ruby application using Docker. Related information: * [Docker Compose overview](https://docs.docker.com/compose/) [Next steps](https://docs.docker.com/guides/ruby/containerize/#next-steps) --------------------------------------------------------------------------- In the next section, you'll take a look at how to set up a CI/CD pipeline using GitHub Actions. [Automate your builds with GitHub Actions »](https://docs.docker.com/guides/ruby/configure-github-actions/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing a Spring Boot REST API with Testcontainers](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/) Learn how to create a Spring Boot REST API with Spring Data JPA and PostgreSQL, then test it using Testcontainers and REST Assured. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Spring Boot project ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/#set-up-the-project) --------------------------------------------------------------------------------------------------------------------------------- Create a Spring Boot project from [Spring Initializr](https://start.spring.io/) by selecting the **Spring Web**, **Spring Data JPA**, **PostgreSQL Driver**, and **Testcontainers** starters. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testing-spring-boot-rest-api) . The key dependencies in `pom.xml` are: 17 2.0.4 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-web org.postgresql postgresql runtime org.springframework.boot spring-boot-starter-test test org.testcontainers testcontainers-junit-jupiter test org.testcontainers testcontainers-postgresql test io.rest-assured rest-assured test Using the Testcontainers BOM (Bill of Materials) is recommended so that you don't have to repeat the version for every Testcontainers module dependency. [Create the JPA entity](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/#create-the-jpa-entity) --------------------------------------------------------------------------------------------------------------------------------------- Create `Customer.java`: package com.testcontainers.demo; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; @Entity @Table(name = "customers") class Customer { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String name; @Column(nullable = false, unique = true) private String email; public Customer() {} public Customer(Long id, String name, String email) { this.id = id; this.name = name; this.email = email; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } [Create the Spring Data JPA repository](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/#create-the-spring-data-jpa-repository) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- package com.testcontainers.demo; import org.springframework.data.jpa.repository.JpaRepository; interface CustomerRepository extends JpaRepository {} [Add the schema creation script](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/#add-the-schema-creation-script) --------------------------------------------------------------------------------------------------------------------------------------------------------- Create `src/main/resources/schema.sql`: create table if not exists customers ( id bigserial not null, name varchar not null, email varchar not null, primary key (id), UNIQUE (email) ); Enable schema initialization in `src/main/resources/application.properties`: spring.sql.init.mode=always [Create the REST API endpoint](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/#create-the-rest-api-endpoint) ----------------------------------------------------------------------------------------------------------------------------------------------------- Create `CustomerController.java`: package com.testcontainers.demo; import java.util.List; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController class CustomerController { private final CustomerRepository repo; CustomerController(CustomerRepository repo) { this.repo = repo; } @GetMapping("/api/customers") List getAll() { return repo.findAll(); } } [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Python](https://docs.docker.com/guides/testcontainers-python-getting-started/) Learn how to create a Python application and test database interactions using Testcontainers for Python with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg "Python") Python Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-python-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-python-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-python-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-python-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Python project ========================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Initialize the project](https://docs.docker.com/guides/testcontainers-python-getting-started/create-project/#initialize-the-project) -------------------------------------------------------------------------------------------------------------------------------------- Start by creating a Python project with a virtual environment: $ mkdir tc-python-demo $ cd tc-python-demo $ python3 -m venv venv $ source venv/bin/activate This guide uses [psycopg3](https://www.psycopg.org/psycopg3/) to interact with the Postgres database, [pytest](https://pytest.org/) for testing, and [testcontainers-python](https://testcontainers-python.readthedocs.io/) for running a PostgreSQL database in a container. Install the dependencies: $ pip install "psycopg[binary]" pytest testcontainers[postgres] $ pip freeze > requirements.txt The `pip freeze` command generates a `requirements.txt` file so that others can install the same package versions using `pip install -r requirements.txt`. [Create the database helper](https://docs.docker.com/guides/testcontainers-python-getting-started/create-project/#create-the-database-helper) ---------------------------------------------------------------------------------------------------------------------------------------------- Create a `db/connection.py` file with a function to get a database connection: import os import psycopg def get_connection(): host = os.getenv("DB_HOST", "localhost") port = os.getenv("DB_PORT", "5432") username = os.getenv("DB_USERNAME", "postgres") password = os.getenv("DB_PASSWORD", "postgres") database = os.getenv("DB_NAME", "postgres") return psycopg.connect(f"host={host} dbname={database} user={username} password={password} port={port}") Instead of hard-coding the database connection parameters, the function uses environment variables. This makes it possible to run the application in different environments without changing code. [Create the business logic](https://docs.docker.com/guides/testcontainers-python-getting-started/create-project/#create-the-business-logic) -------------------------------------------------------------------------------------------------------------------------------------------- Create a `customers/customers.py` file and define the `Customer` class: class Customer: def __init__(self, cust_id, name, email): self.id = cust_id self.name = name self.email = email def __str__(self): return f"Customer({self.id}, {self.name}, {self.email})" Add a `create_table()` function to create the `customers` table: from db.connection import get_connection def create_table(): with get_connection() as conn: with conn.cursor() as cur: cur.execute(""" CREATE TABLE customers ( id serial PRIMARY KEY, name varchar not null, email varchar not null unique) """) conn.commit() The function obtains a database connection using `get_connection()` and creates the `customers` table. The `with` statement automatically closes the connection when done. Add the remaining CRUD functions: def create_customer(name, email): with get_connection() as conn: with conn.cursor() as cur: cur.execute( "INSERT INTO customers (name, email) VALUES (%s, %s)", (name, email)) conn.commit() def get_all_customers() -> list[Customer]: with get_connection() as conn: with conn.cursor() as cur: cur.execute("SELECT * FROM customers") return [Customer(cid, name, email) for cid, name, email in cur] def get_customer_by_email(email) -> Customer: with get_connection() as conn: with conn.cursor() as cur: cur.execute("SELECT id, name, email FROM customers WHERE email = %s", (email,)) (cid, name, email) = cur.fetchone() return Customer(cid, name, email) def delete_all_customers(): with get_connection() as conn: with conn.cursor() as cur: cur.execute("DELETE FROM customers") conn.commit() > Note > > To keep it straightforward for this guide, each function creates a new connection. In a real-world application, use a connection pool to reuse connections. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/) --- # Multi-container applications | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Multi-container applications ============================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/#explanation) -------------------------------------------------------------------------------------------------------------------------------- Starting up a single-container application is easy. For example, a Python script that performs a specific data processing task runs within a container with all its dependencies. Similarly, a Node.js application serving a static website with a small API endpoint can be effectively containerized with all its necessary libraries and dependencies. However, as applications grow in size, managing them as individual containers becomes more difficult. Imagine the data processing Python script needs to connect to a database. Suddenly, you're now managing not just the script but also a database server within the same container. If the script requires user logins, you'll need an authentication mechanism, further bloating the container size. One best practice for containers is that each container should do one thing and do it well. While there are exceptions to this rule, avoid the tendency to have one container do multiple things. Now you might ask, "Do I need to run these containers separately? If I run them separately, how shall I connect them all together?" While `docker run` is a convenient tool for launching containers, it becomes difficult to manage a growing application stack with it. Here's why: * Imagine running several `docker run` commands (frontend, backend, and database) with different configurations for development, testing, and production environments. It's error-prone and time-consuming. * Applications often rely on each other. Manually starting containers in a specific order and managing network connections become difficult as the stack expands. * Each application needs its `docker run` command, making it difficult to scale individual services. Scaling the entire application means potentially wasting resources on components that don't need a boost. * Persisting data for each application requires separate volume mounts or configurations within each `docker run` command. This creates a scattered data management approach. * Setting environment variables for each application through separate `docker run` commands is tedious and error-prone. That's where Docker Compose comes to the rescue. Docker Compose defines your entire multi-container application in a single YAML file called `compose.yml`. This file specifies configurations for all your containers, their dependencies, environment variables, and even volumes and networks. With Docker Compose: * You don't need to run multiple `docker run` commands. All you need to do is define your entire multi-container application in a single YAML file. This centralizes configuration and simplifies management. * You can run containers in a specific order and manage network connections easily. * You can simply scale individual services up or down within the multi-container setup. This allows for efficient allocation based on real-time needs. * You can implement persistent volumes with ease. * It's easy to set environment variables once in your Docker Compose file. By leveraging Docker Compose for running multi-container setups, you can build complex applications with modularity, scalability, and consistency at their core. [Try it out](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/#try-it-out) ------------------------------------------------------------------------------------------------------------------------------ In this hands-on guide, you'll first see how to build and run a counter web application based on Node.js, an Nginx reverse proxy, and a Redis database using the `docker run` commands. You’ll also see how you can simplify the entire deployment process using Docker Compose. ### [Set up](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/#set-up) 1. Get the sample application. If you have Git, you can clone the repository for the sample application. Otherwise, you can download the sample application. Choose one of the following options. Clone with git Download Use the following command in a terminal to clone the sample application repository. $ git clone https://github.com/dockersamples/nginx-node-redis Navigate into the `nginx-node-redis` directory: $ cd nginx-node-redis Inside this directory, you'll find two sub-directories - `nginx` and `web`. Download the source and extract it. [Download the source](https://github.com/dockersamples/nginx-node-redis/archive/refs/heads/main.zip) Navigate into the `nginx-node-redis-main` directory: $ cd nginx-node-redis-main Inside this directory, you'll find two sub-directories - `nginx` and `web`. 2. [Download and install](https://docs.docker.com/get-started/get-docker/) Docker Desktop. ### [Build the images](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/#build-the-images) 1. Navigate into the `nginx` directory to build the image by running the following command: $ docker build -t nginx . 2. Navigate into the `web` directory and run the following command to build the first web image: $ docker build -t web . ### [Run the containers](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/#run-the-containers) 1. Before you can run a multi-container application, you need to create a network for them all to communicate through. You can do so using the `docker network create` command: $ docker network create sample-app 2. Start the Redis container by running the following command, which will attach it to the previously created network and create a network alias (useful for DNS lookups): $ docker run -d --name redis --network sample-app --network-alias redis redis 3. Start the first web container by running the following command: $ docker run -d --name web1 -h web1 --network sample-app --network-alias web1 web 4. Start the second web container by running the following: $ docker run -d --name web2 -h web2 --network sample-app --network-alias web2 web 5. Start the Nginx container by running the following command: $ docker run -d --name nginx --network sample-app -p 80:80 nginx > Note > > Nginx is typically used as a reverse proxy for web applications, routing traffic to backend servers. In this case, it routes to the Node.js backend containers (web1 or web2). 6. Verify the containers are up by running the following command: $ docker ps You will see output like the following: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 2cf7c484c144 nginx "/docker-entrypoint.…" 9 seconds ago Up 8 seconds 0.0.0.0:80->80/tcp nginx 7a070c9ffeaa web "docker-entrypoint.s…" 19 seconds ago Up 18 seconds web2 6dc6d4e60aaf web "docker-entrypoint.s…" 34 seconds ago Up 33 seconds web1 008e0ecf4f36 redis "docker-entrypoint.s…" About a minute ago Up About a minute 6379/tcp redis 7. If you look at the Docker Desktop Dashboard, you can see the containers and dive deeper into their configuration. ![A screenshot of the Docker Desktop Dashboard showing multi-container applications](https://docs.docker.com/get-started/docker-concepts/running-containers/images/multi-container-apps.webp) 8. With everything up and running, you can open [http://localhost](http://localhost/) in your browser to see the site. Refresh the page several times to see the host that’s handling the request and the total number of requests: web2: Number of visits is: 9 web1: Number of visits is: 10 web2: Number of visits is: 11 web1: Number of visits is: 12 > Note > > You might have noticed that Nginx, acting as a reverse proxy, likely distributes incoming requests in a round-robin fashion between the two backend containers. This means each request might be directed to a different container (web1 and web2) on a rotating basis. The output shows consecutive increments for both the web1 and web2 containers and the actual counter value stored in Redis is updated only after the response is sent back to the client. 9. You can use the Docker Desktop Dashboard to remove the containers by selecting the containers and selecting the **Delete** button. ![A screenshot of Docker Desktop Dashboard showing how to delete the multi-container applications](https://docs.docker.com/get-started/docker-concepts/running-containers/images/delete-multi-container-apps.webp) [Simplify the deployment using Docker Compose](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/#simplify-the-deployment-using-docker-compose) -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Docker Compose provides a structured and streamlined approach for managing multi-container deployments. As stated earlier, with Docker Compose, you don’t need to run multiple `docker run` commands. All you need to do is define your entire multi-container application in a single YAML file called `compose.yml`. Let’s see how it works. Navigate to the root of the project directory. Inside this directory, you'll find a file named `compose.yml`. This YAML file is where all the magic happens. It defines all the services that make up your application, along with their configurations. Each service specifies its image, ports, volumes, networks, and any other settings necessary for its functionality. 1. Use the `docker compose up` command to start the application: $ docker compose up -d --build When you run this command, you should see output similar to the following: ✔ Network nginx-node-redis_default Created 0.0s ✔ Container nginx-node-redis-web2-1 Created 0.1s ✔ Container nginx-node-redis-web1-1 Created 0.1s ✔ Container nginx-node-redis-redis-1 Created 0.1s ✔ Container nginx-node-redis-nginx-1 Created 2. If you look at the Docker Desktop Dashboard, you can see the containers and dive deeper into their configuration. ![A screenshot of the Docker Desktop Dashboard showing the containers of the application stack deployed using Docker Compose](https://docs.docker.com/get-started/docker-concepts/running-containers/images/list-containers.webp) 3. Alternatively, you can use the Docker Desktop Dashboard to remove the containers by selecting the application stack and selecting the **Delete** button. ![A screenshot of Docker Desktop Dashboard that shows how to remove the containers that you deployed using Docker Compose](https://docs.docker.com/get-started/docker-concepts/running-containers/images/delete-containers.webp) In this guide, you learned how easy it is to use Docker Compose to start and stop a multi-container application compared to `docker run` which is error-prone and difficult to manage. [Additional resources](https://docs.docker.com/get-started/docker-concepts/running-containers/multi-container-applications/#additional-resources) -------------------------------------------------------------------------------------------------------------------------------------------------- * [`docker container run` CLI reference](https://docs.docker.com/reference/cli/docker/container/run/) * [What is Docker Compose](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-docker-compose/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Node.js](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/) Learn how to create a Node.js application and test database interactions using Testcontainers for Node.js with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Node.js project ========================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Initialize the project](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/create-project/#initialize-the-project) -------------------------------------------------------------------------------------------------------------------------------------- Create a new Node.js project: $ npm init -y Add `pg`, `jest`, and `@testcontainers/postgresql` as dependencies: $ npm install pg --save $ npm install jest @testcontainers/postgresql --save-dev [Implement the customer repository](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/create-project/#implement-the-customer-repository) ------------------------------------------------------------------------------------------------------------------------------------------------------------ Create `src/customer-repository.js` with functions to manage customers in PostgreSQL: async function createCustomerTable(client) { const sql = "CREATE TABLE IF NOT EXISTS customers (id INT NOT NULL, name VARCHAR NOT NULL, PRIMARY KEY (id))"; await client.query(sql); } async function createCustomer(client, customer) { const sql = "INSERT INTO customers (id, name) VALUES($1, $2)"; await client.query(sql, [customer.id, customer.name]); } async function getCustomers(client) { const sql = "SELECT * FROM customers"; const result = await client.query(sql); return result.rows; } module.exports = { createCustomerTable, createCustomer, getCustomers }; The module provides three functions: * `createCustomerTable()` creates the `customers` table if it doesn't exist. * `createCustomer()` inserts a customer record. * `getCustomers()` fetches all customer records. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/write-tests/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing Spring Boot Kafka Listener using Testcontainers](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/) Learn how to create a Spring Boot application with a Kafka listener that persists data in MySQL, then test it using Testcontainers Kafka and MySQL modules with Awaitility. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Spring Boot project ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/#set-up-the-project) ------------------------------------------------------------------------------------------------------------------------------ Create a Spring Boot project from [Spring Initializr](https://start.spring.io/) by selecting the **Spring for Apache Kafka**, **Spring Data JPA**, **MySQL Driver**, and **Testcontainers** starters. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testing-spring-boot-kafka-listener) . After generating the application, add the Awaitility library as a test dependency. You'll use it later to assert the expectations of an asynchronous process flow. The key dependencies in `pom.xml` are: 17 2.0.4 org.springframework.boot spring-boot-starter-data-jpa org.springframework.kafka spring-kafka com.mysql mysql-connector-j runtime org.springframework.boot spring-boot-starter-test test org.springframework.kafka spring-kafka-test test org.testcontainers testcontainers-junit-jupiter test org.testcontainers testcontainers-kafka test org.testcontainers testcontainers-mysql test org.awaitility awaitility test Using the Testcontainers BOM (Bill of Materials) is recommended so that you don't have to repeat the version for every Testcontainers module dependency. [Create the JPA entity](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/#create-the-jpa-entity) ------------------------------------------------------------------------------------------------------------------------------------ The application listens to a topic called `product-price-changes`. When a message arrives, it extracts the product code and price from the event payload and updates the price for that product in the MySQL database. Create `Product.java`: package com.testcontainers.demo; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.Table; import java.math.BigDecimal; @Entity @Table(name = "products") class Product { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false, unique = true) private String code; @Column(nullable = false) private String name; @Column(nullable = false) private BigDecimal price; public Product() {} public Product(Long id, String code, String name, BigDecimal price) { this.id = id; this.code = code; this.name = name; this.price = price; } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getName() { return name; } public void setName(String name) { this.name = name; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } } [Create the Spring Data JPA repository](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/#create-the-spring-data-jpa-repository) -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Create a repository interface for the `Product` entity with a method to find a product by code and a method to update the price for a given product code: package com.testcontainers.demo; import java.math.BigDecimal; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; interface ProductRepository extends JpaRepository { Optional findByCode(String code); @Modifying @Query("update Product p set p.price = :price where p.code = :productCode") void updateProductPrice( @Param("productCode") String productCode, @Param("price") BigDecimal price ); } [Add a schema creation script](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/#add-a-schema-creation-script) -------------------------------------------------------------------------------------------------------------------------------------------------- Because the application doesn't use an in-memory database, you need to create the MySQL tables. The recommended approach for production is a migration tool like Flyway or Liquibase, but for this guide the built-in Spring Boot schema initialization is sufficient. Create `src/main/resources/schema.sql`: create table products ( id int NOT NULL AUTO_INCREMENT, code varchar(255) not null, name varchar(255) not null, price numeric(5,2) not null, PRIMARY KEY (id), UNIQUE (code) ); Enable schema initialization in `src/main/resources/application.properties`: spring.sql.init.mode=always [Create the event payload](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/#create-the-event-payload) ------------------------------------------------------------------------------------------------------------------------------------------ Create a record named `ProductPriceChangedEvent` that represents the structure of the event payload received from the Kafka topic: package com.testcontainers.demo; import java.math.BigDecimal; record ProductPriceChangedEvent(String productCode, BigDecimal price) {} The sender and receiver agree on the following JSON format: { "productCode": "P100", "price": 25.0 } [Implement the Kafka listener](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/#implement-the-kafka-listener) -------------------------------------------------------------------------------------------------------------------------------------------------- Create `ProductPriceChangedEventHandler.java`, which handles messages from the `product-price-changes` topic and updates the product price in the database: package com.testcontainers.demo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.kafka.annotation.KafkaListener; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component @Transactional class ProductPriceChangedEventHandler { private static final Logger log = LoggerFactory.getLogger( ProductPriceChangedEventHandler.class ); private final ProductRepository productRepository; ProductPriceChangedEventHandler(ProductRepository productRepository) { this.productRepository = productRepository; } @KafkaListener(topics = "product-price-changes", groupId = "demo") public void handle(ProductPriceChangedEvent event) { log.info( "Received a ProductPriceChangedEvent with productCode:{}: ", event.productCode() ); productRepository.updateProductPrice(event.productCode(), event.price()); } } The `@KafkaListener` annotation specifies the topic name to listen to. Spring Kafka handles serialization and deserialization based on the properties configured in `application.properties`. [Configure Kafka serialization](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/#configure-kafka-serialization) ---------------------------------------------------------------------------------------------------------------------------------------------------- Add the following Kafka properties to `src/main/resources/application.properties`: ######## Kafka Configuration ######### spring.kafka.bootstrap-servers=localhost:9092 spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer spring.kafka.producer.value-serializer=org.springframework.kafka.support.serializer.JsonSerializer spring.kafka.consumer.group-id=demo spring.kafka.consumer.auto-offset-reset=latest spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer spring.kafka.consumer.value-deserializer=org.springframework.kafka.support.serializer.JsonDeserializer spring.kafka.consumer.properties.spring.json.trusted.packages=com.testcontainers.demo The `productCode` key is (de)serialized using `StringSerializer`/`StringDeserializer`, and the `ProductPriceChangedEvent` value is (de)serialized using `JsonSerializer`/`JsonDeserializer`. [Write tests with Testcontainers »](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/) --- # Containerize | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Angular language-specific guide](https://docs.docker.com/guides/angular/) This guide explains how to containerize Angular applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript 20 minutes [1](https://docs.docker.com/guides/angular/containerize/) [Containerize](https://docs.docker.com/guides/angular/containerize/) [2](https://docs.docker.com/guides/angular/develop/) [Develop your app](https://docs.docker.com/guides/angular/develop/) [3](https://docs.docker.com/guides/angular/run-tests/) [Run your tests](https://docs.docker.com/guides/angular/run-tests/) [4](https://docs.docker.com/guides/angular/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/angular/configure-github-actions/) [5](https://docs.docker.com/guides/angular/deploy/) [Test your deployment](https://docs.docker.com/guides/angular/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize an Angular Application =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/angular/containerize/#prerequisites) ------------------------------------------------------------------------------------ Before you begin, make sure the following tools are installed and available on your system: * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. > **New to Docker?** > Start with the [Docker basics](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/) > guide to get familiar with key concepts like images, containers, and Dockerfiles. * * * [Overview](https://docs.docker.com/guides/angular/containerize/#overview) -------------------------------------------------------------------------- This guide walks you through the complete process of containerizing an Angular application with Docker. You’ll learn how to create a production-ready Docker image using best practices that improve performance, security, scalability, and deployment efficiency. By the end of this guide, you will: * Containerize an Angular application using Docker. * Create and optimize a Dockerfile for production builds. * Use multi-stage builds to minimize image size. * Serve the application efficiently with a custom Nginx configuration. * Build secure and maintainable Docker images by following best practices. * * * [Get the sample application](https://docs.docker.com/guides/angular/containerize/#get-the-sample-application) -------------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, navigate to the directory where you want to work, and run the following command to clone the git repository: $ git clone https://github.com/kristiyan-velkov/docker-angular-sample * * * [Generate a Dockerfile](https://docs.docker.com/guides/angular/containerize/#generate-a-dockerfile) ---------------------------------------------------------------------------------------------------- Docker provides an interactive CLI tool called `docker init` that helps scaffold the necessary configuration files for containerizing your application. This includes generating a `Dockerfile`, `.dockerignore`, `compose.yaml`, and `README.Docker.md`. To begin, navigate to the root of your project directory: $ cd docker-angular-sample Then run the following command: $ docker init You’ll see output similar to: Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! The CLI will prompt you with a few questions about your app setup. For consistency, please use the same responses shown in the example below when prompted: | Question | Answer | | --- | --- | | What application platform does your project use? | Node | | What version of Node do you want to use? | 24.12.0-alpine | | Which package manager do you want to use? | npm | | Do you want to run "npm run build" before starting server? | yes | | What directory is your build output to? | dist | | What command do you want to use to start the app? | npm run start | | What port does your server listen on? | 8080 | After completion, your project directory will contain the following new files: ├── docker-angular-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ └── README.Docker.md * * * [Build the Docker image](https://docs.docker.com/guides/angular/containerize/#build-the-docker-image) ------------------------------------------------------------------------------------------------------ The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, Angular is a front-end framework that compiles into static assets, so we need to tailor the Dockerfile to optimize for how Angular applications are built and served in a production environment. ### [Step 1: Improve the generated Dockerfile and configuration](https://docs.docker.com/guides/angular/containerize/#step-1-improve-the-generated-dockerfile-and-configuration) In this step, you’ll improve the Dockerfile and configuration files by following best practices: * Use multi-stage builds to keep the final image clean and small * Serve the app using Nginx, a fast and secure web server * Improve performance and security by only including what’s needed These updates help ensure your app is easy to deploy, fast to load, and production-ready. > Note > > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. > For full details, see the [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) > . ### [Step 2: Configure the Dockerfile](https://docs.docker.com/guides/angular/containerize/#step-2-configure-the-dockerfile) Before creating a Dockerfile, you need to choose a base image. You can either use the [Node.js Official Image](https://hub.docker.com/_/node) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog) . Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/) . > Important > > This guide uses a stable Node.js LTS image tag that is considered secure when the guide is written. Because new releases and security patches are published regularly, the tag shown here may no longer be the safest option when you follow the guide. Always review the latest available image tags and select a secure, up-to-date version before building or deploying your application. > > Official Node.js Docker Images: [https://hub.docker.com/\_/node](https://hub.docker.com/_/node) Using Docker Hardened Images Using the Docker Official Image Docker Hardened Images (DHIs) are available for Node.js in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/node) . Docker Hardened Images are freely available to everyone with no subscription required. You can pull and use them like any other Docker image after signing in to the DHI registry. For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Node.js DHI (check the catalog for available versions): $ docker pull dhi.io/node:24-alpine3.22-dev In the following Dockerfile, the `FROM` instruction uses `dhi.io/node:24-alpine3.22-dev` as the base image. # ========================================= # Stage 1: Build the Angular Application # ========================================= # Use a lightweight DHI Node.js image for building FROM dhi.io/node:24-alpine3.22-dev AS builder # Set the working directory inside the container WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json package-lock.json* ./ # Install project dependencies using npm ci (ensures a clean, reproducible install) RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . # Build the Angular application RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files # ========================================= FROM dhi.io/nginx:1.28.0-alpine3.21-dev AS runner # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html # Use a non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic # Note: The default Nginx container now listens on port 8080 instead of 80 EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] Now you need to create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following optimized configuration: # ========================================= # Stage 1: Build the Angular Application # ========================================= ARG NODE_VERSION=24.12.0-alpine ARG NGINX_VERSION=alpine3.22 # Use a lightweight Node.js image for building (customizable via ARG) FROM node:${NODE_VERSION} AS builder # Set the working directory inside the container WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json *package-lock.json* ./ # Install project dependencies using npm ci (ensures a clean, reproducible install) RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . # Build the Angular application RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files # ========================================= FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html # Use a built-in non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic # Note: The default Nginx container now listens on port 8080 instead of 80 EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] > Note > > We are using nginx-unprivileged instead of the standard Nginx image to follow security best practices. Running as a non-root user in the final image: > > * Reduces the attack surface > * Aligns with Docker’s recommendations for container hardening > * Helps comply with stricter security policies in production environments ### [Step 3: Configure the .dockerignore file](https://docs.docker.com/guides/angular/containerize/#step-3-configure-the-dockerignore-file) The `.dockerignore` file tells Docker which files and folders to exclude when building the image. > Note > > This helps: > > * Reduce image size > * Speed up the build process > * Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. > > To learn more, visit the [.dockerignore reference](https://docs.docker.com/reference/dockerfile/#dockerignore-file) > . Copy and replace the contents of your existing `.dockerignore` with the configuration below: # ================================ # Node and build output # ================================ node_modules dist out-tsc .angular .cache .tmp # ================================ # Testing & Coverage # ================================ coverage jest cypress cypress/screenshots cypress/videos reports playwright-report .vite .vitepress # ================================ # Environment & log files # ================================ *.env* !*.env.production *.log *.tsbuildinfo # ================================ # IDE & OS-specific files # ================================ .vscode .idea .DS_Store Thumbs.db *.swp # ================================ # Version control & CI files # ================================ .git .gitignore # ================================ # Docker & local orchestration # ================================ Dockerfile Dockerfile.* .dockerignore docker-compose.yml docker-compose*.yml # ================================ # Miscellaneous # ================================ *.bak *.old *.tmp ### [Step 4: Create the `nginx.conf` file](https://docs.docker.com/guides/angular/containerize/#step-4-create-the-nginxconf-file) To serve your Angular application efficiently inside the container, you’ll configure Nginx with a custom setup. This configuration is optimized for performance, browser caching, gzip compression, and support for client-side routing. Create a file named `nginx.conf` in the root of your project directory, and add the following content: > Note > > To learn more about configuring Nginx, see the [official Nginx documentation](https://nginx.org/en/docs/) > . worker_processes auto; pid /tmp/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; client_body_temp_path /tmp/client_temp; proxy_temp_path /tmp/proxy_temp_path; fastcgi_temp_path /tmp/fastcgi_temp; uwsgi_temp_path /tmp/uwsgi_temp; scgi_temp_path /tmp/scgi_temp; # Logging access_log off; error_log /dev/stderr warn; # Performance sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; keepalive_requests 1000; # Compression gzip on; gzip_vary on; gzip_proxied any; gzip_min_length 256; gzip_comp_level 6; gzip_types text/plain text/css text/xml text/javascript application/javascript application/x-javascript application/json application/xml application/xml+rss font/ttf font/otf image/svg+xml; server { listen 8080; server_name localhost; root /usr/share/nginx/html; index index.html; # Angular Routing location / { try_files $uri $uri/ /index.html; } # Static Assets Caching location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ { expires 1y; access_log off; add_header Cache-Control "public, immutable"; } # Optional: Explicit asset route location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; } } } ### [Step 5: Build the Angular application image](https://docs.docker.com/guides/angular/containerize/#step-5-build-the-angular-application-image) With your custom configuration in place, you're now ready to build the Docker image for your Angular application. The updated setup includes: * The updated setup includes a clean, production-ready Nginx configuration tailored specifically for Angular. * Efficient multi-stage Docker build, ensuring a small and secure final image. After completing the previous steps, your project directory should now contain the following files: ├── docker-angular-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ ├── nginx.conf │ └── README.Docker.md Now that your Dockerfile is configured, you can build the Docker image for your Angular application. > Note > > The `docker build` command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the [build context](https://docs.docker.com/build/concepts/context/#what-is-a-build-context) > ). Run the following command from the root of your project: $ docker build --tag docker-angular-sample . What this command does: * Uses the Dockerfile in the current directory (.) * Packages the application and its dependencies into a Docker image * Tags the image as docker-angular-sample so you can reference it later #### [Step 6: View local images](https://docs.docker.com/guides/angular/containerize/#step-6--view-local-images) After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](https://docs.docker.com/desktop/use-desktop/images/) . Since you're already working in the terminal, let's use the Docker CLI. To list all locally available Docker images, run the following command: $ docker images Example Output: REPOSITORY TAG IMAGE ID CREATED SIZE docker-angular-sample latest 34e66bdb9d40 14 seconds ago 76.4MB This output provides key details about your images: * **Repository** – The name assigned to the image. * **Tag** – A version label that helps identify different builds (e.g., latest). * **Image ID** – A unique identifier for the image. * **Created** – The timestamp indicating when the image was built. * **Size** – The total disk space used by the image. If the build was successful, you should see `docker-angular-sample` image listed. * * * [Run the containerized application](https://docs.docker.com/guides/angular/containerize/#run-the-containerized-application) ---------------------------------------------------------------------------------------------------------------------------- In the previous step, you created a Dockerfile for your Angular application and built a Docker image using the docker build command. Now it’s time to run that image in a container and verify that your application works as expected. Inside the `docker-angular-sample` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple Angular web application. Press `ctrl+c` in the terminal to stop your application. ### [Run the application in the background](https://docs.docker.com/guides/angular/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-angular-sample` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see your Angular application running in the browser. To confirm that the container is running, use `docker ps` command: $ docker ps This will list all active containers along with their ports, names, and status. Look for a container exposing port 8080. Example Output: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES eb13026806d1 docker-angular-sample-server "nginx -c /etc/nginx…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp docker-angular-sample-server-1 To stop the application, run: $ docker compose down > Note > > For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) > . * * * [Summary](https://docs.docker.com/guides/angular/containerize/#summary) ------------------------------------------------------------------------ In this guide, you learned how to containerize, build, and run an Angular application using Docker. By following best practices, you created a secure, optimized, and production-ready setup. What you accomplished: * Initialized your project using `docker init` to scaffold essential Docker configuration files. * Replaced the default `Dockerfile` with a multi-stage build that compiles the Angular application and serves the static files using Nginx. * Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. * Built your Docker image using `docker build`. * Ran the container using `docker compose up`, both in the foreground and in detached mode. * Verified that the app was running by visiting [http://localhost:8080](http://localhost:8080/) . * Learned how to stop the containerized application using `docker compose down`. You now have a fully containerized Angular application, running in a Docker container, and ready for deployment across any environment with confidence and consistency. * * * [Related resources](https://docs.docker.com/guides/angular/containerize/#related-resources) -------------------------------------------------------------------------------------------- Explore official references and best practices to sharpen your Docker workflow: * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) – Learn how to separate build and runtime stages. * [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. * [Build context in Docker](https://docs.docker.com/build/concepts/context/) – Learn how context affects image builds. * [`docker init` CLI reference](https://docs.docker.com/reference/cli/docker/init/) – Scaffold Docker assets automatically. * [`docker build` CLI reference](https://docs.docker.com/reference/cli/docker/image/build/) – Build Docker images from a Dockerfile. * [`docker images` CLI reference](https://docs.docker.com/reference/cli/docker/image/ls/) – Manage and inspect local Docker images. * [`docker compose up` CLI reference](https://docs.docker.com/reference/cli/docker/compose/up/) – Start and run multi-container applications. * [`docker compose down` CLI reference](https://docs.docker.com/reference/cli/docker/compose/down/) – Stop and remove containers, networks, and volumes. * * * [Next steps](https://docs.docker.com/guides/angular/containerize/#next-steps) ------------------------------------------------------------------------------ With your Angular application now containerized, you're ready to move on to the next step. In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine. [Use containers for Angular development »](https://docs.docker.com/guides/angular/develop/) --- # Multi-stage builds | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) Multi-stage builds ================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Explanation](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/#explanation) ------------------------------------------------------------------------------------------------------------------- In a traditional build, all build instructions are executed in sequence, and in a single build container: downloading dependencies, compiling code, and packaging the application. All those layers end up in your final image. This approach works, but it leads to bulky images carrying unnecessary weight and increasing your security risks. This is where multi-stage builds come in. Multi-stage builds introduce multiple stages in your Dockerfile, each with a specific purpose. Think of it like the ability to run different parts of a build in multiple different environments, concurrently. By separating the build environment from the final runtime environment, you can significantly reduce the image size and attack surface. This is especially beneficial for applications with large build dependencies. Multi-stage builds are recommended for all types of applications. * For interpreted languages, like JavaScript or Ruby or Python, you can build and minify your code in one stage, and copy the production-ready files to a smaller runtime image. This optimizes your image for deployment. * For compiled languages, like C or Go or Rust, multi-stage builds let you compile in one stage and copy the compiled binaries into a final runtime image. No need to bundle the entire compiler in your final image. Here's a simplified example of a multi-stage build structure using pseudo-code. Notice there are multiple `FROM` statements and a new `AS `. In addition, the `COPY` statement in the second stage is copying `--from` the previous stage. # Stage 1: Build Environment FROM builder-image AS build-stage # Install build tools (e.g., Maven, Gradle) # Copy source code # Build commands (e.g., compile, package) # Stage 2: Runtime environment FROM runtime-image AS final-stage # Copy application artifacts from the build stage (e.g., JAR file) COPY --from=build-stage /path/in/build/stage /path/to/place/in/final/stage # Define runtime configuration (e.g., CMD, ENTRYPOINT) This Dockerfile uses two stages: * The build stage uses a base image containing build tools needed to compile your application. It includes commands to install build tools, copy source code, and execute build commands. * The final stage uses a smaller base image suitable for running your application. It copies the compiled artifacts (a JAR file, for example) from the build stage. Finally, it defines the runtime configuration (using `CMD` or `ENTRYPOINT`) for starting your application. [Try it out](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/#try-it-out) ----------------------------------------------------------------------------------------------------------------- In this hands-on guide, you'll unlock the power of multi-stage builds to create lean and efficient Docker images for a sample Java application. You'll use a simple “Hello World” Spring Boot-based application built with Maven as your example. 1. [Download and install](https://www.docker.com/products/docker-desktop/) Docker Desktop. 2. Open this [pre-initialized project](https://start.spring.io/#!type=maven-project&language=java&platformVersion=4.0.1&packaging=jar&configurationFileFormat=properties&jvmVersion=21&groupId=com.example&artifactId=spring-boot-docker&name=spring-boot-docker&description=Demo%20project%20for%20Spring%20Boot&packageName=com.example.spring-boot-docker&dependencies=web) to generate a ZIP file. Here’s how that looks: ![A screenshot of Spring Initializr tool selected with Java 21, Spring Web and Spring Boot 3.4.0](https://docs.docker.com/get-started/docker-concepts/building-images/images/multi-stage-builds-spring-initializer.webp) [Spring Initializr](https://start.spring.io/) is a quickstart generator for Spring projects. It provides an extensible API to generate JVM-based projects with implementations for several common concepts — like basic language generation for Java, Kotlin, Groovy, and Maven. Select **Generate** to create and download the zip file for this project. For this demonstration, you’ve paired Maven build automation with Java, a Spring Web dependency, and Java 21 for your metadata. 3. Navigate the project directory. Once you unzip the file, you'll see the following project directory structure: spring-boot-docker ├── HELP.md ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main │ ├── java │ │ └── com │ │ └── example │ │ └── spring_boot_docker │ │ └── SpringBootDockerApplication.java │ └── resources │ ├── application.properties │ ├── static │ └── templates └── test └── java └── com └── example └── spring_boot_docker └── SpringBootDockerApplicationTests.java 15 directories, 7 files The `src/main/java` directory contains your project's source code, the `src/test/java` directory contains the test source, and the `pom.xml` file is your project’s Project Object Model (POM). The `pom.xml` file is the core of a Maven project's configuration. It's a single configuration file that contains most of the information needed to build a customized project. The POM is huge and can seem daunting. Thankfully, you don't yet need to understand every intricacy to use it effectively. 4. Create a RESTful web service that displays "Hello World!". Under the `src/main/java/com/example/spring_boot_docker/` directory, you can modify your `SpringBootDockerApplication.java` file with the following content: package com.example.spring_boot_docker; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @SpringBootApplication public class SpringBootDockerApplication { @RequestMapping("/") public String home() { return "Hello World"; } public static void main(String[] args) { SpringApplication.run(SpringBootDockerApplication.class, args); } } The `SpringbootDockerApplication.java` file starts by declaring your `com.example.spring_boot_docker` package and importing necessary Spring frameworks. This Java file creates a simple Spring Boot web application that responds with "Hello World" when a user visits its homepage. ### [Create the Dockerfile](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/#create-the-dockerfile) Now that you have the project, you’re ready to create the `Dockerfile`. 1. Create a file named `Dockerfile` in the same folder that contains all the other folders and files (like src, pom.xml, etc.). 2. In the `Dockerfile`, define your base image by adding the following line: FROM eclipse-temurin:21.0.8_9-jdk-jammy 3. Now, define the working directory by using the `WORKDIR` instruction. This will specify where future commands will run and the directory files will be copied inside the container image. WORKDIR /app 4. Copy both the Maven wrapper script and your project's `pom.xml` file into the current working directory `/app` within the Docker container. COPY .mvn/ .mvn COPY mvnw pom.xml ./ 5. Execute a command within the container. It runs the `./mvnw dependency:go-offline` command, which uses the Maven wrapper (`./mvnw`) to download all dependencies for your project without building the final JAR file (useful for faster builds). RUN ./mvnw dependency:go-offline 6. Copy the `src` directory from your project on the host machine to the `/app` directory within the container. COPY src ./src 7. Set the default command to be executed when the container starts. This command instructs the container to run the Maven wrapper (`./mvnw`) with the `spring-boot:run` goal, which will build and execute your Spring Boot application. CMD ["./mvnw", "spring-boot:run"] And with that, you should have the following Dockerfile: FROM eclipse-temurin:21.0.8_9-jdk-jammy WORKDIR /app COPY .mvn/ .mvn COPY mvnw pom.xml ./ RUN ./mvnw dependency:go-offline COPY src ./src CMD ["./mvnw", "spring-boot:run"] ### [Build the container image](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/#build-the-container-image) 1. Execute the following command to build the Docker image: $ docker build -t spring-helloworld . 2. Check the size of the Docker image by using the `docker images` command: $ docker images Doing so will produce output like the following: REPOSITORY TAG IMAGE ID CREATED SIZE spring-helloworld latest ff708d5ee194 3 minutes ago 880MB This output shows that your image is 880MB in size. It contains the full JDK, Maven toolchain, and more. In production, you don’t need that in your final image. ### [Run the Spring Boot application](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/#run-the-spring-boot-application) 1. Now that you have an image built, it's time to run the container. $ docker run -p 8080:8080 spring-helloworld You'll then see output similar to the following in the container log: [INFO] --- spring-boot:3.3.4:run (default-cli) @ spring-boot-docker --- [INFO] Attaching agents: [] . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v3.3.4) 2024-09-29T23:54:07.157Z INFO 159 --- [spring-boot-docker] [ main] c.e.s.SpringBootDockerApplication : Starting SpringBootDockerApplication using Java 21.0.2 with PID 159 (/app/target/classes started by root in /app) …. 2. Access your “Hello World” page through your web browser at [http://localhost:8080](http://localhost:8080/) , or via this curl command: $ curl localhost:8080 Hello World ### [Use multi-stage builds](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/#use-multi-stage-builds) 1. Consider the following Dockerfile: FROM eclipse-temurin:21.0.8_9-jdk-jammy AS builder WORKDIR /opt/app COPY .mvn/ .mvn COPY mvnw pom.xml ./ RUN ./mvnw dependency:go-offline COPY ./src ./src RUN ./mvnw clean install FROM eclipse-temurin:21.0.8_9-jre-jammy AS final WORKDIR /opt/app EXPOSE 8080 COPY --from=builder /opt/app/target/*.jar /opt/app/*.jar ENTRYPOINT ["java", "-jar", "/opt/app/*.jar"] Notice that this Dockerfile has been split into two stages. * The first stage remains the same as the previous Dockerfile, providing a Java Development Kit (JDK) environment for building the application. This stage is given the name of builder. * The second stage is a new stage named `final`. It uses a slimmer `eclipse-temurin:21.0.2_13-jre-jammy` image, containing just the Java Runtime Environment (JRE) needed to run the application. This image provides a Java Runtime Environment (JRE) which is enough for running the compiled application (JAR file). > For production use, it's highly recommended that you produce a custom JRE-like runtime using jlink. JRE images are available for all versions of Eclipse Temurin, but `jlink` allows you to create a minimal runtime containing only the necessary Java modules for your application. This can significantly reduce the size and improve the security of your final image. [Refer to this page](https://hub.docker.com/_/eclipse-temurin) > for more information. With multi-stage builds, a Docker build uses one base image for compilation, packaging, and unit tests and then a separate image for the application runtime. As a result, the final image is smaller in size since it doesn’t contain any development or debugging tools. By separating the build environment from the final runtime environment, you can significantly reduce the image size and increase the security of your final images. 2. Now, rebuild your image and run your ready-to-use production build. $ docker build -t spring-helloworld-builder . This command builds a Docker image named `spring-helloworld-builder` using the final stage from your `Dockerfile` file located in the current directory. > Note > > In your multi-stage Dockerfile, the final stage (final) is the default target for building. This means that if you don't explicitly specify a target stage using the `--target` flag in the `docker build` command, Docker will automatically build the last stage by default. You could use `docker build -t spring-helloworld-builder --target builder .` to build only the builder stage with the JDK environment. 3. Look at the image size difference by using the `docker images` command: $ docker images You'll get output similar to the following: spring-helloworld-builder latest c5c76cb815c0 24 minutes ago 428MB spring-helloworld latest ff708d5ee194 About an hour ago 880MB Your final image is just 428 MB, compared to the original build size of 880 MB. By optimizing each stage and only including what's necessary, you were able to significantly reduce the overall image size while still achieving the same functionality. This not only improves performance but also makes your Docker images more lightweight, more secure, and easier to manage. [Additional resources](https://docs.docker.com/get-started/docker-concepts/building-images/multi-stage-builds/#additional-resources) ------------------------------------------------------------------------------------------------------------------------------------- * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) * [Dockerfile best practices](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) * [Base images](https://docs.docker.com/build/building/base-images/) * [Spring Boot Docker](https://spring.io/guides/topicals/spring-boot-docker) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations using WireMock](https://docs.docker.com/guides/testcontainers-java-wiremock/) Learn how to create a Spring Boot application that integrates with external REST APIs, then test those integrations using Testcontainers and WireMock. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Spring Boot project ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/#set-up-the-project) --------------------------------------------------------------------------------------------------------------------- Create a Spring Boot project from [Spring Initializr](https://start.spring.io/) by selecting the **Spring Web** and **Testcontainers** starters. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testing-rest-api-integrations-using-wiremock) . After generating the project, add the **REST Assured**, **WireMock**, and **WireMock Testcontainers module** libraries as test dependencies. The key dependencies in `pom.xml` are: 17 2.0.4 1.0-alpha-13 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.testcontainers testcontainers-junit-jupiter test org.wiremock wiremock-standalone 3.6.0 test org.wiremock.integrations.testcontainers wiremock-testcontainers-module ${wiremock-testcontainers.version} test io.rest-assured rest-assured test Using the Testcontainers BOM (Bill of Materials) is recommended so that you don't have to repeat the version for every Testcontainers module dependency. This guide builds an application that manages video albums. A third-party REST API handles photo assets. For demonstration purposes, the application uses the publicly available [JSONPlaceholder](https://jsonplaceholder.typicode.com/) API as a photo service. The application exposes a `GET /api/albums/{albumId}` endpoint that calls the photo service to fetch photos for a given album. [WireMock](https://wiremock.org/) is a tool for building mock APIs. Testcontainers provides a [WireMock module](https://testcontainers.com/modules/wiremock/) that runs WireMock as a Docker container. [Create the Album and Photo models](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/#create-the-album-and-photo-models) --------------------------------------------------------------------------------------------------------------------------------------------------- Create `Album.java` using Java records: package com.testcontainers.demo; import java.util.List; public record Album(Long albumId, List photos) {} record Photo(Long id, String title, String url, String thumbnailUrl) {} [Create the PhotoServiceClient](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/#create-the-photoserviceclient) ------------------------------------------------------------------------------------------------------------------------------------------- Create `PhotoServiceClient.java`, which uses `RestTemplate` to fetch photos for a given album ID: package com.testcontainers.demo; import java.util.List; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; @Service class PhotoServiceClient { private final String baseUrl; private final RestTemplate restTemplate; PhotoServiceClient( @Value("${photos.api.base-url}") String baseUrl, RestTemplateBuilder builder ) { this.baseUrl = baseUrl; this.restTemplate = builder.build(); } List getPhotos(Long albumId) { String url = baseUrl + "/albums/{albumId}/photos"; ResponseEntity> response = restTemplate.exchange( url, HttpMethod.GET, null, new ParameterizedTypeReference<>() {}, albumId ); return response.getBody(); } } The photo service base URL is externalized as a configuration property. Add the following entry to `src/main/resources/application.properties`: photos.api.base-url=https://jsonplaceholder.typicode.com [Create the REST API endpoint](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/#create-the-rest-api-endpoint) ----------------------------------------------------------------------------------------------------------------------------------------- Create `AlbumController.java`: package com.testcontainers.demo; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestClientResponseException; @RestController @RequestMapping("/api") class AlbumController { private static final Logger logger = LoggerFactory.getLogger( AlbumController.class ); private final PhotoServiceClient photoServiceClient; AlbumController(PhotoServiceClient photoServiceClient) { this.photoServiceClient = photoServiceClient; } @GetMapping("/albums/{albumId}") public ResponseEntity getAlbumById(@PathVariable Long albumId) { try { List photos = photoServiceClient.getPhotos(albumId); return ResponseEntity.ok(new Album(albumId, photos)); } catch (RestClientResponseException e) { logger.error("Failed to get photos", e); return new ResponseEntity<>(e.getStatusCode()); } } } This endpoint calls the photo service for a given album ID and returns a response like: { "albumId": 1, "photos": [\ {\ "id": 51,\ "title": "non sunt voluptatem placeat consequuntur rem incidunt",\ "url": "https://via.placeholder.com/600/8e973b",\ "thumbnailUrl": "https://via.placeholder.com/150/8e973b"\ },\ {\ "id": 52,\ "title": "eveniet pariatur quia nobis reiciendis laboriosam ea",\ "url": "https://via.placeholder.com/600/121fa4",\ "thumbnailUrl": "https://via.placeholder.com/150/121fa4"\ }\ ] } [Write tests with WireMock and Testcontainers »](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/) --- # Containerize | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Vue.js language-specific guide](https://docs.docker.com/guides/vuejs/) This guide explains how to containerize Vue.js applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Frameworks Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/vuejs/containerize/) [Containerize](https://docs.docker.com/guides/vuejs/containerize/) [2](https://docs.docker.com/guides/vuejs/develop/) [Develop your app](https://docs.docker.com/guides/vuejs/develop/) [3](https://docs.docker.com/guides/vuejs/run-tests/) [Run your tests](https://docs.docker.com/guides/vuejs/run-tests/) [4](https://docs.docker.com/guides/vuejs/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/vuejs/configure-github-actions/) [5](https://docs.docker.com/guides/vuejs/deploy/) [Test your deployment](https://docs.docker.com/guides/vuejs/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize an Vue.js Application ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/vuejs/containerize/#prerequisites) ---------------------------------------------------------------------------------- Before you begin, make sure the following tools are installed and available on your system: * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. > **New to Docker?** > Start with the [Docker basics](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/) > guide to get familiar with key concepts like images, containers, and Dockerfiles. * * * [Overview](https://docs.docker.com/guides/vuejs/containerize/#overview) ------------------------------------------------------------------------ This guide walks you through the complete process of containerizing an Vue.js application with Docker. You’ll learn how to create a production-ready Docker image using best practices that improve performance, security, scalability, and deployment efficiency. By the end of this guide, you will: * Containerize an Vue.js application using Docker. * Create and optimize a Dockerfile for production builds. * Use multi-stage builds to minimize image size. * Serve the application efficiently with a custom Nginx configuration. * Build secure and maintainable Docker images by following best practices. * * * [Get the sample application](https://docs.docker.com/guides/vuejs/containerize/#get-the-sample-application) ------------------------------------------------------------------------------------------------------------ Clone the sample application to use with this guide. Open a terminal, navigate to the directory where you want to work, and run the following command to clone the git repository: $ git clone https://github.com/kristiyan-velkov/docker-vuejs-sample * * * [Generate a Dockerfile](https://docs.docker.com/guides/vuejs/containerize/#generate-a-dockerfile) -------------------------------------------------------------------------------------------------- Docker provides an interactive CLI tool called `docker init` that helps scaffold the necessary configuration files for containerizing your application. This includes generating a `Dockerfile`, `.dockerignore`, `compose.yaml`, and `README.Docker.md`. To begin, navigate to the root of your project directory: $ cd docker-vuejs-sample Then run the following command: $ docker init You’ll see output similar to: Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! The CLI will prompt you with a few questions about your app setup. For consistency, please use the same responses shown in the example below when prompted: | Question | Answer | | --- | --- | | What application platform does your project use? | Node | | What version of Node do you want to use? | 24.12.0-alpine | | Which package manager do you want to use? | npm | | Do you want to run "npm run build" before starting server? | yes | | What directory is your build output to? | dist | | What command do you want to use to start the app? | npm run build | | What port does your server listen on? | 8080 | After completion, your project directory will contain the following new files: ├── docker-vuejs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ └── README.Docker.md * * * [Build the Docker image](https://docs.docker.com/guides/vuejs/containerize/#build-the-docker-image) ---------------------------------------------------------------------------------------------------- The default Dockerfile generated by `docker init` provides a solid foundation for typical Node.js applications. However, Vue.js is a front-end framework that compiles into static assets, which means the Dockerfile needs to be customized to align with how Vue.js applications are built and efficiently served in a production environment. Tailoring it properly ensures better performance, smaller image sizes, and a smoother deployment process. ### [Step 1: Review the generated files](https://docs.docker.com/guides/vuejs/containerize/#step-1-review-the-generated-files) In this step, you’ll improve the Dockerfile and configuration files by following best practices: * Use multi-stage builds to keep the final image clean and small * Serve the app using Nginx, a fast and secure web server * Improve performance and security by only including what’s needed These updates help ensure your app is easy to deploy, fast to load, and production-ready. > Note > > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. > For full details, see the [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) > . ### [Step 2: Configure the Dockerfile](https://docs.docker.com/guides/vuejs/containerize/#step-2-configure-the-dockerfile) Before creating a Dockerfile, you need to choose a base image. You can either use the [Node.js Official Image](https://hub.docker.com/_/node) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog) . Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/) . > Important > > This guide uses a stable Node.js LTS image tag that is considered secure when the guide is written. Because new releases and security patches are published regularly, the tag shown here may no longer be the safest option when you follow the guide. Always review the latest available image tags and select a secure, up-to-date version before building or deploying your application. > > Official Node.js Docker Images: [https://hub.docker.com/\_/node](https://hub.docker.com/_/node) Using Docker Hardened Images Using the Docker Official Image Docker Hardened Images (DHIs) are available for Node.js in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/node) . Docker Hardened Images are freely available to everyone with no subscription required. You can pull and use them like any other Docker image after signing in to the DHI registry. For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Node.js DHI (check the catalog for available versions): $ docker pull dhi.io/node:24-alpine3.22-dev 3. Pull the Nginx DHI (check the catalog for available versions): $ docker pull dhi.io/nginx:1.28.0-alpine3.21-dev In the following Dockerfile, the `FROM` instructions use `dhi.io/node:24-alpine3.22-dev` and `dhi.io/nginx:1.28.0-alpine3.21-dev` as the base images. # ========================================= # Stage 1: Build the Vue.js Application # ========================================= # Use a lightweight DHI Node.js image for building FROM dhi.io/node:24-alpine3.22-dev AS builder # Set the working directory inside the container WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json package-lock.json* ./ # Install project dependencies using npm ci (ensures a clean, reproducible install) RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . # Build the Vue.js application RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files # ========================================= FROM dhi.io/nginx:1.28.0-alpine3.21-dev AS runner # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html # Use a built-in non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic # Note: The default Nginx container now listens on port 8080 instead of 80 EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] Replace the contents of your current `Dockerfile` with the optimized configuration below. This setup is tailored specifically for building and serving Vue.js applications in a clean, efficient, and production-ready environment. # ========================================= # Stage 1: Build the Vue.js Application # ========================================= ARG NODE_VERSION=24.12.0-alpine ARG NGINX_VERSION=alpine3.22 # Use a lightweight Node.js image for building (customizable via ARG) FROM node:${NODE_VERSION} AS builder # Set the working directory inside the container WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json package-lock.json* ./ # Install project dependencies using npm ci (ensures a clean, reproducible install) RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . # Build the Vue.js application RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files # ========================================= FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html # Use a built-in non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic # Note: The default Nginx container now listens on port 8080 instead of 80 EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] > Note > > We are using nginx-unprivileged instead of the standard Nginx image to follow security best practices. Running as a non-root user in the final image: > > * Reduces the attack surface > * Aligns with Docker’s recommendations for container hardening > * Helps comply with stricter security policies in production environments ### [Step 3: Configure the .dockerignore file](https://docs.docker.com/guides/vuejs/containerize/#step-3-configure-the-dockerignore-file) The `.dockerignore` file plays a crucial role in optimizing your Docker image by specifying which files and directories should be excluded from the build context. > Note > > This helps: > > * Reduce image size > * Speed up the build process > * Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. > > To learn more, visit the [.dockerignore reference](https://docs.docker.com/reference/dockerfile/#dockerignore-file) > . Copy and replace the contents of your existing `.dockerignore` with the configuration below: # ------------------------------- # Dependency directories # ------------------------------- node_modules/ # ------------------------------- # Production and build outputs # ------------------------------- dist/ out/ build/ public/build/ # ------------------------------- # Vite, VuePress, and cache dirs # ------------------------------- .vite/ .vitepress/ .cache/ .tmp/ # ------------------------------- # Test output and coverage # ------------------------------- coverage/ reports/ jest/ cypress/ cypress/screenshots/ cypress/videos/ # ------------------------------- # Environment and config files # ------------------------------- *.env* !.env.production # Keep production env if needed *.local *.log # ------------------------------- # TypeScript artifacts # ------------------------------- *.tsbuildinfo # ------------------------------- # Editor and IDE config # ------------------------------- .vscode/ .idea/ *.swp # ------------------------------- # System files # ------------------------------- .DS_Store Thumbs.db # ------------------------------- # Lockfiles (optional) # ------------------------------- npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* # ------------------------------- # Git files # ------------------------------- .git/ .gitignore # ------------------------------- # Docker-related files # ------------------------------- Dockerfile .dockerignore docker-compose.yml docker-compose.override.yml ### [Step 4: Create the `nginx.conf` file](https://docs.docker.com/guides/vuejs/containerize/#step-4-create-the-nginxconf-file) To serve your Vue.js application efficiently inside the container, you’ll configure Nginx with a custom setup. This configuration is optimized for performance, browser caching, gzip compression, and support for client-side routing. Create a file named `nginx.conf` in the root of your project directory, and add the following content: > Note > > To learn more about configuring Nginx, see the [official Nginx documentation](https://nginx.org/en/docs/) > . worker_processes auto; pid /tmp/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; charset utf-8; access_log off; error_log /dev/stderr warn; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; keepalive_requests 1000; gzip on; gzip_comp_level 6; gzip_proxied any; gzip_min_length 256; gzip_vary on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; server { listen 8080; server_name localhost; root /usr/share/nginx/html; index index.html; location / { try_files $uri $uri/ /index.html; } location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ { expires 1y; access_log off; add_header Cache-Control "public, immutable"; add_header X-Content-Type-Options nosniff; } location /assets/ { expires 1y; add_header Cache-Control "public, immutable"; add_header X-Content-Type-Options nosniff; } error_page 404 /index.html; } } ### [Step 5: Build the Vue.js application image](https://docs.docker.com/guides/vuejs/containerize/#step-5-build-the-vuejs-application-image) With your custom configuration in place, you're now ready to build the Docker image for your Vue.js application. The updated setup includes: * The updated setup includes a clean, production-ready Nginx configuration tailored specifically for Vue.js. * Efficient multi-stage Docker build, ensuring a small and secure final image. After completing the previous steps, your project directory should now contain the following files: ├── docker-vuejs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ ├── nginx.conf │ └── README.Docker.md Now that your Dockerfile is configured, you can build the Docker image for your Vue.js application. > Note > > The `docker build` command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the [build context](https://docs.docker.com/build/concepts/context/#what-is-a-build-context) > ). Run the following command from the root of your project: $ docker build --tag docker-vuejs-sample . What this command does: * Uses the Dockerfile in the current directory (.) * Packages the application and its dependencies into a Docker image * Tags the image as docker-vuejs-sample so you can reference it later #### [Step 6: View local images](https://docs.docker.com/guides/vuejs/containerize/#step-6-view-local-images) After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](https://docs.docker.com/desktop/use-desktop/images/) . Since you're already working in the terminal, let's use the Docker CLI. To list all locally available Docker images, run the following command: $ docker images Example Output: REPOSITORY TAG IMAGE ID CREATED SIZE docker-vuejs-sample latest 8c9c199179d4 14 seconds ago 76.2MB This output provides key details about your images: * **Repository** – The name assigned to the image. * **Tag** – A version label that helps identify different builds (e.g., latest). * **Image ID** – A unique identifier for the image. * **Created** – The timestamp indicating when the image was built. * **Size** – The total disk space used by the image. If the build was successful, you should see `docker-vuejs-sample` image listed. * * * [Run the containerized application](https://docs.docker.com/guides/vuejs/containerize/#run-the-containerized-application) -------------------------------------------------------------------------------------------------------------------------- In the previous step, you created a Dockerfile for your Vue.js application and built a Docker image using the docker build command. Now it’s time to run that image in a container and verify that your application works as expected. Inside the `docker-vuejs-sample` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple Vue.js web application. Press `ctrl+c` in the terminal to stop your application. ### [Run the application in the background](https://docs.docker.com/guides/vuejs/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-vuejs-sample` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see your Vue.js application running in the browser. To confirm that the container is running, use `docker ps` command: $ docker ps This will list all active containers along with their ports, names, and status. Look for a container exposing port 8080. Example Output: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 37a1fa85e4b0 docker-vuejs-sample-server "nginx -c /etc/nginx…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp docker-vuejs-sample-server-1 To stop the application, run: $ docker compose down > Note > > For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) > . * * * [Summary](https://docs.docker.com/guides/vuejs/containerize/#summary) ---------------------------------------------------------------------- In this guide, you learned how to containerize, build, and run an Vue.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup. What you accomplished: * Initialized your project using `docker init` to scaffold essential Docker configuration files. * Replaced the default `Dockerfile` with a multi-stage build that compiles the Vue.js application and serves the static files using Nginx. * Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. * Built your Docker image using `docker build`. * Ran the container using `docker compose up`, both in the foreground and in detached mode. * Verified that the app was running by visiting [http://localhost:8080](http://localhost:8080/) . * Learned how to stop the containerized application using `docker compose down`. You now have a fully containerized Vue.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency. * * * [Related resources](https://docs.docker.com/guides/vuejs/containerize/#related-resources) ------------------------------------------------------------------------------------------ Explore official references and best practices to sharpen your Docker workflow: * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) – Learn how to separate build and runtime stages. * [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. * [Build context in Docker](https://docs.docker.com/build/concepts/context/) – Learn how context affects image builds. * [`docker init` CLI reference](https://docs.docker.com/reference/cli/docker/init/) – Scaffold Docker assets automatically. * [`docker build` CLI reference](https://docs.docker.com/reference/cli/docker/image/build/) – Build Docker images from a Dockerfile. * [`docker images` CLI reference](https://docs.docker.com/reference/cli/docker/image/ls/) – Manage and inspect local Docker images. * [`docker compose up` CLI reference](https://docs.docker.com/reference/cli/docker/compose/up/) – Start and run multi-container applications. * [`docker compose down` CLI reference](https://docs.docker.com/reference/cli/docker/compose/down/) – Stop and remove containers, networks, and volumes. * * * [Next steps](https://docs.docker.com/guides/vuejs/containerize/#next-steps) ---------------------------------------------------------------------------- With your Vue.js application now containerized, you're ready to move on to the next step. In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine. [Use containers for Vue.js development »](https://docs.docker.com/guides/vuejs/develop/) --- # Create the project | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations using MockServer](https://docs.docker.com/guides/testcontainers-java-mockserver/) Learn how to create a Spring Boot application that integrates with external REST APIs, then test those integrations using Testcontainers and MockServer. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Create the Spring Boot project ============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Set up the project](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/#set-up-the-project) ----------------------------------------------------------------------------------------------------------------------- Create a Spring Boot project from [Spring Initializr](https://start.spring.io/) by selecting the **Spring Web**, **Spring Reactive Web**, and **Testcontainers** starters. Alternatively, clone the [guide repository](https://github.com/testcontainers/tc-guide-testing-rest-api-integrations-using-mockserver) . After generating the project, add the **REST Assured** and **MockServer** libraries as test dependencies. The key dependencies in `pom.xml` are: 17 2.0.4 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-webflux org.springframework.boot spring-boot-starter-test test org.testcontainers testcontainers-junit-jupiter test org.testcontainers testcontainers-mockserver test org.mock-server mockserver-netty 5.15.0 test io.rest-assured rest-assured test Using the Testcontainers BOM (Bill of Materials) is recommended so that you don't have to repeat the version for every Testcontainers module dependency. This guide builds an application that manages video albums. A third-party REST API handles photo assets. For demonstration purposes, the application uses the publicly available [JSONPlaceholder](https://jsonplaceholder.typicode.com/) API as a photo service. The application exposes a `GET /api/albums/{albumId}` endpoint that calls the photo service to fetch photos for a given album. [MockServer](https://www.mock-server.com/) is a library for mocking HTTP-based services. Testcontainers provides a [MockServer module](https://java.testcontainers.org/modules/mockserver/) that runs MockServer as a Docker container. [Create the Album and Photo models](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/#create-the-album-and-photo-models) ----------------------------------------------------------------------------------------------------------------------------------------------------- Create `Album.java` using Java records: package com.testcontainers.demo; import java.util.List; public record Album(Long albumId, List photos) {} record Photo(Long id, String title, String url, String thumbnailUrl) {} [Create the PhotoServiceClient interface](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/#create-the-photoserviceclient-interface) ----------------------------------------------------------------------------------------------------------------------------------------------------------------- Spring Framework 6 introduced [declarative HTTP client support](https://docs.spring.io/spring-framework/reference/integration/rest-clients.html#rest-http-interface) . Create an interface with a method that fetches photos for a given album ID: package com.testcontainers.demo; import java.util.List; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.service.annotation.GetExchange; interface PhotoServiceClient { @GetExchange("/albums/{albumId}/photos") List getPhotos(@PathVariable Long albumId); } [Register PhotoServiceClient as a bean](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/#register-photoserviceclient-as-a-bean) ------------------------------------------------------------------------------------------------------------------------------------------------------------- To generate a runtime implementation of `PhotoServiceClient`, register it as a Spring bean using `HttpServiceProxyFactory`. The factory requires an `HttpClientAdapter` implementation. Spring Boot provides `WebClientAdapter` as part of the `spring-webflux` library: package com.testcontainers.demo; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.reactive.function.client.WebClient; import org.springframework.web.reactive.function.client.support.WebClientAdapter; import org.springframework.web.service.invoker.HttpServiceProxyFactory; @Configuration public class AppConfig { @Bean public PhotoServiceClient photoServiceClient( @Value("${photos.api.base-url}") String photosApiBaseUrl ) { WebClient client = WebClient.builder().baseUrl(photosApiBaseUrl).build(); HttpServiceProxyFactory factory = HttpServiceProxyFactory .builder(WebClientAdapter.forClient(client)) .build(); return factory.createClient(PhotoServiceClient.class); } } The photo service base URL is externalized as a configuration property. Add the following entry to `src/main/resources/application.properties`: photos.api.base-url=https://jsonplaceholder.typicode.com [Create the REST API endpoint](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/#create-the-rest-api-endpoint) ------------------------------------------------------------------------------------------------------------------------------------------- Create `AlbumController.java`: package com.testcontainers.demo; import java.util.List; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.reactive.function.client.WebClientResponseException; @RestController @RequestMapping("/api") class AlbumController { private static final Logger logger = LoggerFactory.getLogger( AlbumController.class ); private final PhotoServiceClient photoServiceClient; AlbumController(PhotoServiceClient photoServiceClient) { this.photoServiceClient = photoServiceClient; } @GetMapping("/albums/{albumId}") public ResponseEntity getAlbumById(@PathVariable Long albumId) { try { List photos = photoServiceClient.getPhotos(albumId); return ResponseEntity.ok(new Album(albumId, photos)); } catch (WebClientResponseException e) { logger.error("Failed to get photos", e); return new ResponseEntity<>(e.getStatusCode()); } } } This endpoint calls the photo service for a given album ID and returns a response like: { "albumId": 1, "photos": [\ {\ "id": 51,\ "title": "non sunt voluptatem placeat consequuntur rem incidunt",\ "url": "https://via.placeholder.com/600/8e973b",\ "thumbnailUrl": "https://via.placeholder.com/150/8e973b"\ },\ {\ "id": 52,\ "title": "eveniet pariatur quia nobis reiciendis laboriosam ea",\ "url": "https://via.placeholder.com/600/121fa4",\ "thumbnailUrl": "https://via.placeholder.com/150/121fa4"\ }\ ] } [Write tests with Testcontainers MockServer »](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/) --- # Containerize | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [React.js language-specific guide](https://docs.docker.com/guides/reactjs/) This guide explains how to containerize React.js applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript 20 minutes [1](https://docs.docker.com/guides/reactjs/containerize/) [Containerize](https://docs.docker.com/guides/reactjs/containerize/) [2](https://docs.docker.com/guides/reactjs/develop/) [Develop your app](https://docs.docker.com/guides/reactjs/develop/) [3](https://docs.docker.com/guides/reactjs/run-tests/) [Run your tests](https://docs.docker.com/guides/reactjs/run-tests/) [4](https://docs.docker.com/guides/reactjs/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/reactjs/configure-github-actions/) [5](https://docs.docker.com/guides/reactjs/deploy/) [Test your deployment](https://docs.docker.com/guides/reactjs/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a React.js Application =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/reactjs/containerize/#prerequisites) ------------------------------------------------------------------------------------ Before you begin, make sure the following tools are installed and available on your system: * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. > **New to Docker?** > Start with the [Docker basics](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/) > guide to get familiar with key concepts like images, containers, and Dockerfiles. * * * [Overview](https://docs.docker.com/guides/reactjs/containerize/#overview) -------------------------------------------------------------------------- This guide walks you through the complete process of containerizing a React.js application with Docker. You’ll learn how to create a production-ready Docker image using best practices that improve performance, security, scalability, and deployment efficiency. By the end of this guide, you will: * Containerize a React.js application using Docker. * Create and optimize a Dockerfile for production builds. * Use multi-stage builds to minimize image size. * Serve the application efficiently with a custom NGINX configuration. * Follow best practices for building secure and maintainable Docker images. * * * [Get the sample application](https://docs.docker.com/guides/reactjs/containerize/#get-the-sample-application) -------------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the git repository: $ git clone https://github.com/kristiyan-velkov/docker-reactjs-sample * * * [Generate a Dockerfile](https://docs.docker.com/guides/reactjs/containerize/#generate-a-dockerfile) ---------------------------------------------------------------------------------------------------- Docker provides an interactive CLI tool called `docker init` that helps scaffold the necessary configuration files for containerizing your application. This includes generating a `Dockerfile`, `.dockerignore`, `compose.yaml`, and `README.Docker.md`. To begin, navigate to the root of your project directory: $ cd docker-reactjs-sample Then run the following command: $ docker init You’ll see output similar to: Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! The CLI will prompt you with a few questions about your app setup. For consistency, please use the same responses shown in the example below when prompted: | Question | Answer | | --- | --- | | What application platform does your project use? | Node | | What version of Node do you want to use? | 24.12.0-alpine | | Which package manager do you want to use? | npm | | Do you want to run "npm run build" before starting server? | yes | | What directory is your build output to? | dist | | What command do you want to use to start the app? | npm run dev | | What port does your server listen on? | 8080 | After completion, your project directory will contain the following new files: ├── docker-reactjs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ └── README.Docker.md * * * [Build the Docker image](https://docs.docker.com/guides/reactjs/containerize/#build-the-docker-image) ------------------------------------------------------------------------------------------------------ The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, React.js is a front-end library that compiles into static assets, so we need to tailor the Dockerfile to optimize for how React applications are built and served in a production environment. ### [Step 1: Review the generated files](https://docs.docker.com/guides/reactjs/containerize/#step-1-review-the-generated-files) In this step, you’ll improve the Dockerfile and configuration files by following best practices: * Use multi-stage builds to keep the final image clean and small * Serve the app using NGINX, a fast and secure web server * Improve performance and security by only including what’s needed These updates help ensure your app is easy to deploy, fast to load, and production-ready. > Note > > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. > For full details, see the [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) > . ### [Step 2: Configure the Dockerfile file](https://docs.docker.com/guides/reactjs/containerize/#step-2-configure-the-dockerfile-file) Before creating a Dockerfile, you need to choose a base image. You can either use the [Node.js Official Image](https://hub.docker.com/_/node) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog) . Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/) . > Important > > This guide uses a stable Node.js LTS image tag that is considered secure when the guide is written. Because new releases and security patches are published regularly, the tag shown here may no longer be the safest option when you follow the guide. Always review the latest available image tags and select a secure, up-to-date version before building or deploying your application. > > Official Node.js Docker Images: [https://hub.docker.com/\_/node](https://hub.docker.com/_/node) Using Docker Hardened Images Using the Docker Official Image Docker Hardened Images (DHIs) are available for Node.js in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/node) . Docker Hardened Images are freely available to everyone with no subscription required. You can pull and use them like any other Docker image after signing in to the DHI registry. For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Node.js DHI (check the catalog for available versions): $ docker pull dhi.io/node:24-alpine3.22-dev 3. Pull the Nginx DHI (check the catalog for available versions): $ docker pull dhi.io/nginx:1.28.0-alpine3.21-dev In the following Dockerfile, the `FROM` instructions use `dhi.io/node:24-alpine3.22-dev` and `dhi.io/nginx:1.28.0-alpine3.21-dev` as the base images. # ========================================= # Stage 1: Build the React.js Application # ========================================= # Use a lightweight Node.js image for building (customizable via ARG) FROM dhi.io/node:24-alpine3.22-dev AS builder # Set the working directory inside the container WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json package-lock.json* ./ # Install project dependencies using npm ci (ensures a clean, reproducible install) RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . # Build the React.js application (outputs to /app/dist) RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files # ========================================= FROM dhi.io/nginx:1.28.0-alpine3.21-dev AS runner # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html # Use a non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic # Note: The default NGINX container now listens on port 8080 instead of 80 EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] Now you need to create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following optimized configuration: # ========================================= # Stage 1: Build the React.js Application # ========================================= ARG NODE_VERSION=24.12.0-alpine ARG NGINX_VERSION=alpine3.22 # Use a lightweight Node.js image for building (customizable via ARG) FROM node:${NODE_VERSION} AS builder # Set the working directory inside the container WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json package-lock.json* ./ # Install project dependencies using npm ci (ensures a clean, reproducible install) RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . # Build the React.js application (outputs to /app/dist) RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files # ========================================= FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html # Use a built-in non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic # Note: The default NGINX container now listens on port 8080 instead of 80 EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] > Note > > We are using nginx-unprivileged instead of the standard NGINX image to follow security best practices. Running as a non-root user in the final image: > > * Reduces the attack surface > * Aligns with Docker’s recommendations for container hardening > * Helps comply with stricter security policies in production environments ### [Step 3: Configure the .dockerignore file](https://docs.docker.com/guides/reactjs/containerize/#step-3-configure-the-dockerignore-file) The `.dockerignore` file tells Docker which files and folders to exclude when building the image. > Note > > This helps: > > * Reduce image size > * Speed up the build process > * Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. > > To learn more, visit the [.dockerignore reference](https://docs.docker.com/reference/dockerfile/#dockerignore-file) > . Copy and replace the contents of your existing `.dockerignore` with the configuration below: # Ignore dependencies and build output node_modules/ dist/ out/ .tmp/ .cache/ # Ignore Vite, Webpack, and React-specific build artifacts .vite/ .vitepress/ .eslintcache .npm/ coverage/ jest/ cypress/ cypress/screenshots/ cypress/videos/ reports/ # Ignore environment and config files (sensitive data) *.env* *.log # Ignore TypeScript build artifacts (if using TypeScript) *.tsbuildinfo # Ignore lockfiles (optional if using Docker for package installation) npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* # Ignore local development files .git/ .gitignore .vscode/ .idea/ *.swp .DS_Store Thumbs.db # Ignore Docker-related files (to avoid copying unnecessary configs) Dockerfile .dockerignore docker-compose.yml docker-compose.override.yml # Ignore build-specific cache files *.lock ### [Step 4: Create the `nginx.conf` file](https://docs.docker.com/guides/reactjs/containerize/#step-4-create-the-nginxconf-file) To serve your React.js application efficiently inside the container, you’ll configure NGINX with a custom setup. This configuration is optimized for performance, browser caching, gzip compression, and support for client-side routing. Create a file named `nginx.conf` in the root of your project directory, and add the following content: > Note > > To learn more about configuring NGINX, see the [official NGINX documentation](https://nginx.org/en/docs/) > . worker_processes auto; # Store PID in /tmp (always writable) pid /tmp/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # Disable logging to avoid permission issues access_log off; error_log /dev/stderr warn; # Optimize static file serving sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; keepalive_requests 1000; # Gzip compression for optimized delivery gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml; gzip_min_length 256; gzip_vary on; server { listen 8080; server_name localhost; # Root directory where React.js build files are placed root /usr/share/nginx/html; index index.html; # Serve React.js static files with proper caching location / { try_files $uri /index.html; } # Serve static assets with long cache expiration location ~* \.(?:ico|css|js|gif|jpe?g|png|woff2?|eot|ttf|svg|map)$ { expires 1y; access_log off; add_header Cache-Control "public, immutable"; } # Handle React.js client-side routing location /static/ { expires 1y; add_header Cache-Control "public, immutable"; } } } ### [Step 5: Build the React.js application image](https://docs.docker.com/guides/reactjs/containerize/#step-5-build-the-reactjs-application-image) With your custom configuration in place, you're now ready to build the Docker image for your React.js application. The updated setup includes: * Optimized browser caching and gzip compression * Secure, non-root logging to avoid permission issues * Support for React client-side routing by redirecting unmatched routes to `index.html` After completing the previous steps, your project directory should now contain the following files: ├── docker-reactjs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ ├── nginx.conf │ └── README.Docker.md Now that your Dockerfile is configured, you can build the Docker image for your React.js application. > Note > > The `docker build` command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the [build context](https://docs.docker.com/build/concepts/context/#what-is-a-build-context) > ). Run the following command from the root of your project: $ docker build --tag docker-reactjs-sample . What this command does: * Uses the Dockerfile in the current directory (.) * Packages the application and its dependencies into a Docker image * Tags the image as docker-reactjs-sample so you can reference it later #### [Step 6: View local images](https://docs.docker.com/guides/reactjs/containerize/#step-6--view-local-images) After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](https://docs.docker.com/desktop/use-desktop/images/) . Since you're already working in the terminal, let's use the Docker CLI. To list all locally available Docker images, run the following command: $ docker images Example Output: REPOSITORY TAG IMAGE ID CREATED SIZE docker-reactjs-sample latest f39b47a97156 14 seconds ago 75.8MB This output provides key details about your images: * **Repository** – The name assigned to the image. * **Tag** – A version label that helps identify different builds (e.g., latest). * **Image ID** – A unique identifier for the image. * **Created** – The timestamp indicating when the image was built. * **Size** – The total disk space used by the image. If the build was successful, you should see `docker-reactjs-sample` image listed. * * * [Run the containerized application](https://docs.docker.com/guides/reactjs/containerize/#run-the-containerized-application) ---------------------------------------------------------------------------------------------------------------------------- In the previous step, you created a Dockerfile for your React.js application and built a Docker image using the docker build command. Now it’s time to run that image in a container and verify that your application works as expected. Inside the `docker-reactjs-sample` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple React.js web application. Press `ctrl+c` in the terminal to stop your application. ### [Run the application in the background](https://docs.docker.com/guides/reactjs/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-reactjs-sample` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple web application preview. To confirm that the container is running, use `docker ps` command: $ docker ps This will list all active containers along with their ports, names, and status. Look for a container exposing port 8080. Example Output: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 88bced6ade95 docker-reactjs-sample-server "nginx -c /etc/nginx…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp docker-reactjs-sample-server-1 To stop the application, run: $ docker compose down > Note > > For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) > . * * * [Summary](https://docs.docker.com/guides/reactjs/containerize/#summary) ------------------------------------------------------------------------ In this guide, you learned how to containerize, build, and run a React.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup. What you accomplished: * Initialized your project using `docker init` to scaffold essential Docker configuration files. * Replaced the default `Dockerfile` with a multi-stage build that compiles the React.js application and serves the static files using Nginx. * Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. * Built your Docker image using `docker build`. * Ran the container using `docker compose up`, both in the foreground and in detached mode. * Verified that the app was running by visiting [http://localhost:8080](http://localhost:8080/) . * Learned how to stop the containerized application using `docker compose down`. You now have a fully containerized React.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency. * * * [Related resources](https://docs.docker.com/guides/reactjs/containerize/#related-resources) -------------------------------------------------------------------------------------------- Explore official references and best practices to sharpen your Docker workflow: * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) – Learn how to separate build and runtime stages. * [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. * [Build context in Docker](https://docs.docker.com/build/concepts/context/) – Learn how context affects image builds. * [`docker init` CLI reference](https://docs.docker.com/reference/cli/docker/init/) – Scaffold Docker assets automatically. * [`docker build` CLI reference](https://docs.docker.com/reference/cli/docker/image/build/) – Build Docker images from a Dockerfile. * [`docker images` CLI reference](https://docs.docker.com/reference/cli/docker/image/ls/) – Manage and inspect local Docker images. * [`docker compose up` CLI reference](https://docs.docker.com/reference/cli/docker/compose/up/) – Start and run multi-container applications. * [`docker compose down` CLI reference](https://docs.docker.com/reference/cli/docker/compose/down/) – Stop and remove containers, networks, and volumes. * * * [Next steps](https://docs.docker.com/guides/reactjs/containerize/#next-steps) ------------------------------------------------------------------------------ With your React.js application now containerized, you're ready to move on to the next step. In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine. [Use containers for React.js development »](https://docs.docker.com/guides/reactjs/develop/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Build a RAG application using Ollama and Docker](https://docs.docker.com/guides/rag-ollama/) This guide demonstrates how to use Docker to deploy Retrieval-Augmented Generation (RAG) models with Ollama. AI 20 minutes [1](https://docs.docker.com/guides/rag-ollama/containerize/) [Containerize your app](https://docs.docker.com/guides/rag-ollama/containerize/) [2](https://docs.docker.com/guides/rag-ollama/develop/) [Develop your app](https://docs.docker.com/guides/rag-ollama/develop/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for RAG development ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/rag-ollama/develop/#prerequisites) ---------------------------------------------------------------------------------- Complete [Containerize a RAG application](https://docs.docker.com/guides/rag-ollama/containerize/) . [Overview](https://docs.docker.com/guides/rag-ollama/develop/#overview) ------------------------------------------------------------------------ In this section, you'll learn how to set up a development environment to access all the services that your generative RAG application needs. This includes: * Adding a local database * Adding a local or remote LLM service > Note > > You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) > demo applications. [Add a local database](https://docs.docker.com/guides/rag-ollama/develop/#add-a-local-database) ------------------------------------------------------------------------------------------------ You can use containers to set up local services, like a database. In this section, you'll explore the database service in the `docker-compose.yaml` file. To run the database service: 1. In the cloned repository's directory, open the `docker-compose.yaml` file in an IDE or text editor. 2. In the `docker-compose.yaml` file, you'll see the following: services: qdrant: image: qdrant/qdrant container_name: qdrant ports: - "6333:6333" volumes: - qdrant_data:/qdrant/storage > Note > > To learn more about Qdrant, see the [Qdrant Official Docker Image](https://hub.docker.com/r/qdrant/qdrant) > . 3. Start the application. Inside the `winy` directory, run the following command in a terminal. $ docker compose up --build 4. Access the application. Open a browser and view the application at [http://localhost:8501](http://localhost:8501/) . You should see a simple Streamlit application. 5. Stop the application. In the terminal, press `ctrl`+`c` to stop the application. [Add a local or remote LLM service](https://docs.docker.com/guides/rag-ollama/develop/#add-a-local-or-remote-llm-service) -------------------------------------------------------------------------------------------------------------------------- The sample application supports both [Ollama](https://ollama.ai/) . This guide provides instructions for the following scenarios: * Run Ollama in a container * Run Ollama outside of a container While all platforms can use any of the previous scenarios, the performance and GPU support may vary. You can use the following guidelines to help you choose the appropriate option: * Run Ollama in a container if you're on Linux, and using a native installation of the Docker Engine, or Windows 10/11, and using Docker Desktop, you have a CUDA-supported GPU, and your system has at least 8 GB of RAM. * Run Ollama outside of a container if running Docker Desktop on a Linux Machine. Choose one of the following options for your LLM service. Run Ollama in a container Run Ollama outside of a container When running Ollama in a container, you should have a CUDA-supported GPU. While you can run Ollama in a container without a supported GPU, the performance may not be acceptable. Only Linux and Windows 11 support GPU access to containers. To run Ollama in a container and provide GPU access: 1. Install the prerequisites. * For Docker Engine on Linux, install the [NVIDIA Container Toolkilt](https://github.com/NVIDIA/nvidia-container-toolkit) . * For Docker Desktop on Windows 10/11, install the latest [NVIDIA driver](https://www.nvidia.com/Download/index.aspx) and make sure you are using the [WSL2 backend](https://docs.docker.com/desktop/features/wsl/#turn-on-docker-desktop-wsl-2) 2. The `docker-compose.yaml` file already contains the necessary instructions. In your own apps, you'll need to add the Ollama service in your `docker-compose.yaml`. The following is the updated `docker-compose.yaml`: ollama: image: ollama/ollama container_name: ollama ports: - "8000:8000" deploy: resources: reservations: devices: - driver: nvidia count: 1 capabilities: [gpu] > Note > > For more details about the Compose instructions, see [Turn on GPU access with Docker Compose](https://docs.docker.com/compose/how-tos/gpu-support/) > . 3. Once the Ollama container is up and running it is possible to use the `download_model.sh` inside the `tools` folder with this command: . ./download_model.sh Pulling an Ollama model can take several minutes. To run Ollama outside of a container: 1. [Install](https://github.com/jmorganca/ollama) and run Ollama on your host machine. 2. Pull the model to Ollama using the following command. $ ollama pull llama2 3. Remove the `ollama` service from the `docker-compose.yaml` and update properly the connection variables in `winy` service: - OLLAMA=http://ollama:11434 + OLLAMA= [Run your RAG application](https://docs.docker.com/guides/rag-ollama/develop/#run-your-rag-application) -------------------------------------------------------------------------------------------------------- At this point, you have the following services in your Compose file: * Server service for your main RAG application * Database service to store vectors in a Qdrant database * (optional) Ollama service to run the LLM service Once the application is running, open a browser and access the application at [http://localhost:8501](http://localhost:8501/) . Depending on your system and the LLM service that you chose, it may take several minutes to answer. [Summary](https://docs.docker.com/guides/rag-ollama/develop/#summary) ---------------------------------------------------------------------- In this section, you learned how to set up a development environment to provide access all the services that your GenAI application needs. Related information: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Ollama Docker image](https://hub.docker.com/r/ollama/ollama) * [GenAI Stack demo applications](https://github.com/docker/genai-stack) [Next steps](https://docs.docker.com/guides/rag-ollama/develop/#next-steps) ---------------------------------------------------------------------------- See samples of more GenAI applications in the [GenAI Stack demo applications](https://github.com/docker/genai-stack) . --- # Run ROS 2 | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Introduction to ROS 2 Development with Docker](https://docs.docker.com/guides/ros2/) This guide details how to containerize ROS 2 applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg "Python") Python Frameworks 30 minutes [1](https://docs.docker.com/guides/ros2/run-ros2/) [Run ROS 2](https://docs.docker.com/guides/ros2/run-ros2/) [2](https://docs.docker.com/guides/ros2/develop/) [Set Up ROS 2 workspace](https://docs.docker.com/guides/ros2/develop/) [3](https://docs.docker.com/guides/ros2/turtlesim-example/) [Turtlesim example](https://docs.docker.com/guides/ros2/turtlesim-example/) [« Back to all guides](https://docs.docker.com/guides/) Run ROS 2 in a container ======================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Overview](https://docs.docker.com/guides/ros2/run-ros2/#overview) ------------------------------------------------------------------- In this section, you will run ROS 2 in an isolated Docker container using official ROS 2 images, verify that ROS 2 is working, and install additional ROS 2 packages for development and testing. * * * [Run ROS 2 in a container](https://docs.docker.com/guides/ros2/run-ros2/#run-ros-2-in-a-container) --------------------------------------------------------------------------------------------------- The fastest way to get started with ROS 2 is to use the [official Docker image](https://hub.docker.com/_/ros/) . To pull an image, start a container, and open an interactive bash shell: 1. Pull and run the official ROS 2 Docker image: $ docker run -it ros:humble This guide uses the Humble distribution. You can replace `humble` with another supported distribution such as `rolling`, `jazzy`, or `iron`. > Note > > This environment is temporary and does not maintain persistence. Any files you create or packages you install will be deleted once the container is stopped or removed. 2. Verify ROS 2 is working: $ echo $ROS_DISTRO You should see output similar to: humble [Install ROS 2 packages](https://docs.docker.com/guides/ros2/run-ros2/#install-ros-2-packages) ----------------------------------------------------------------------------------------------- The official ROS 2 images include core packages. To install additional packages, use the `apt` package manager: 1. Update the package manager: $ sudo apt update 2. Install the desired package: $ sudo apt install $PACKAGE_NAME Replace `$PACKAGE_NAME` with any package you want to install. Some commonly used packages include: * `ros-humble-turtlesim` - Visualization and simulation tool * `ros-humble-rviz2` - 3D visualization tool * `ros-humble-rqt` - Qt-based ROS graphical tools * `ros-humble-demo-nodes-cpp` - C++ demo nodes * `ros-humble-demo-nodes-py` - Python demo nodes * `ros-humble-colcon-common-extensions` - Build system extensions [Summary](https://docs.docker.com/guides/ros2/run-ros2/#summary) ----------------------------------------------------------------- In this section, you pulled an official ROS 2 Docker image, launched an interactive session, and extended the container's capabilities by installing additional ROS 2 packages using apt. [Next steps](https://docs.docker.com/guides/ros2/run-ros2/#next-steps) ----------------------------------------------------------------------- In the next section, you will configure a persistent workspace to ensure your code and modifications are saved across sessions. [Build and develop a ROS 2 workspace »](https://docs.docker.com/guides/ros2/develop/) --- # Containerize | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Node.js language-specific guide](https://docs.docker.com/guides/nodejs/) This guide explains how to containerize Node.js applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/nodejs/containerize/) [Containerize](https://docs.docker.com/guides/nodejs/containerize/) [2](https://docs.docker.com/guides/nodejs/develop/) [Develop your app](https://docs.docker.com/guides/nodejs/develop/) [3](https://docs.docker.com/guides/nodejs/run-tests/) [Run your tests](https://docs.docker.com/guides/nodejs/run-tests/) [4](https://docs.docker.com/guides/nodejs/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/nodejs/configure-github-actions/) [5](https://docs.docker.com/guides/nodejs/deploy/) [Deploy your app](https://docs.docker.com/guides/nodejs/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Node.js application ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/nodejs/containerize/#prerequisites) ----------------------------------------------------------------------------------- Before you begin, make sure the following tools are installed and available on your system: * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. > **New to Docker?** > Start with the [Docker basics](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/) > guide to get familiar with key concepts like images, containers, and Dockerfiles. * * * [Overview](https://docs.docker.com/guides/nodejs/containerize/#overview) ------------------------------------------------------------------------- This guide walks you through the complete process of containerizing a Node.js application with Docker. You’ll learn how to create a production-ready Docker image using best practices that enhance performance, security, scalability, and operational efficiency. By the end of this guide, you will: * Containerize a Node.js application using Docker. * Create and optimize a Dockerfile tailored for Node.js environments. * Use multi-stage builds to separate dependencies and reduce image size. * Configure the container for secure, efficient runtime using a non-root user. * Follow best practices for building secure, lightweight, and maintainable Docker images. [Get the sample application](https://docs.docker.com/guides/nodejs/containerize/#get-the-sample-application) ------------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the git repository: $ git clone https://github.com/kristiyan-velkov/docker-nodejs-sample [Generate a Dockerfile](https://docs.docker.com/guides/nodejs/containerize/#generate-a-dockerfile) --------------------------------------------------------------------------------------------------- Docker provides an interactive CLI tool called `docker init` that helps scaffold the necessary configuration files for containerizing your application. This includes generating a `Dockerfile`, `.dockerignore`, `compose.yaml`, and `README.Docker.md`. To begin, navigate to the root of your project directory: $ cd docker-nodejs-sample Then run the following command: $ docker init You’ll see output similar to: Welcome to the Docker Init CLI This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! The CLI will prompt you with a few questions about your app setup. For consistency, use the same responses shown in the example following when prompted: | Question | Answer | | --- | --- | | What application platform does your project use? | Node | | What version of Node do you want to use? | 24.11.1-alpine | | Which package manager do you want to use? | npm | | Do you want to run "npm run build" before starting server? | yes | | What directory is your build output to? | dist | | What command do you want to use to start the app? | npm run dev | | What port does your server listen on? | 3000 | After completion, your project directory will contain the following new files: ├── docker-nodejs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ └── README.Docker.md [Create a Docker Compose file](https://docs.docker.com/guides/nodejs/containerize/#create-a-docker-compose-file) ----------------------------------------------------------------------------------------------------------------- While `docker init` generates a basic `compose.yaml` file, you'll need to create a more comprehensive configuration for this full-stack application. Replace the generated `compose.yaml` with a production-ready configuration. Create a new file named `compose.yml` in your project root: # ======================================== # Docker Compose Configuration # Modern Node.js Todo Application # ======================================== services: # ======================================== # Development Service # ======================================== app-dev: build: context: . dockerfile: Dockerfile target: development container_name: todoapp-dev ports: - '${APP_PORT:-3000}:3000' # API server - '${VITE_PORT:-5173}:5173' # Vite dev server - '${DEBUG_PORT:-9229}:9229' # Node.js debugger environment: NODE_ENV: development DOCKER_ENV: 'true' POSTGRES_HOST: db POSTGRES_PORT: 5432 POSTGRES_DB: todoapp POSTGRES_USER: todoapp POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}' volumes: - ./src:/app/src:ro - ./package.json:/app/package.json - ./vite.config.ts:/app/vite.config.ts:ro - ./tailwind.config.js:/app/tailwind.config.js:ro - ./postcss.config.js:/app/postcss.config.js:ro depends_on: db: condition: service_healthy develop: watch: - action: sync path: ./src target: /app/src ignore: - '**/*.test.*' - '**/__tests__/**' - action: rebuild path: ./package.json - action: sync path: ./vite.config.ts target: /app/vite.config.ts - action: sync path: ./tailwind.config.js target: /app/tailwind.config.js - action: sync path: ./postcss.config.js target: /app/postcss.config.js restart: unless-stopped networks: - todoapp-network # ======================================== # Production Service # ======================================== app-prod: build: context: . dockerfile: Dockerfile target: production container_name: todoapp-prod ports: - '${PROD_PORT:-8080}:3000' environment: NODE_ENV: production POSTGRES_HOST: db POSTGRES_PORT: 5432 POSTGRES_DB: todoapp POSTGRES_USER: todoapp POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-https://yourdomain.com}' depends_on: db: condition: service_healthy restart: unless-stopped deploy: resources: limits: memory: '${PROD_MEMORY_LIMIT:-2G}' cpus: '${PROD_CPU_LIMIT:-1.0}' reservations: memory: '${PROD_MEMORY_RESERVATION:-512M}' cpus: '${PROD_CPU_RESERVATION:-0.25}' security_opt: - no-new-privileges:true read_only: true tmpfs: - /tmp networks: - todoapp-network profiles: - prod # ======================================== # PostgreSQL Database Service # ======================================== db: image: postgres:18-alpine container_name: todoapp-db environment: POSTGRES_DB: '${POSTGRES_DB:-todoapp}' POSTGRES_USER: '${POSTGRES_USER:-todoapp}' POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' volumes: - postgres_data:/var/lib/postgresql ports: - '${DB_PORT:-5432}:5432' restart: unless-stopped healthcheck: test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}'] interval: 10s timeout: 5s retries: 5 start_period: 5s networks: - todoapp-network # ======================================== # Volume Configuration # ======================================== volumes: postgres_data: name: todoapp-postgres-data driver: local # ======================================== # Network Configuration # ======================================== networks: todoapp-network: name: todoapp-network driver: bridge This Docker Compose configuration includes: * **Development service** (`app-dev`): Full development environment with hot reload, debugging support, and bind mounts * **Production service** (`app-prod`): Optimized production deployment with resource limits and security hardening * **Database service** (`db`): PostgreSQL 16 with persistent storage and health checks * **Networking**: Isolated network for secure service communication * **Volumes**: Persistent storage for database data [Create environment configuration](https://docs.docker.com/guides/nodejs/containerize/#create-environment-configuration) ------------------------------------------------------------------------------------------------------------------------- Create a `.env` file to configure your application settings: $ cp .env.example .env Update the `.env` file with your preferred settings: # Application Configuration NODE_ENV=development APP_PORT=3000 VITE_PORT=5173 DEBUG_PORT=9229 # Production Configuration PROD_PORT=8080 PROD_MEMORY_LIMIT=2G PROD_CPU_LIMIT=1.0 PROD_MEMORY_RESERVATION=512M PROD_CPU_RESERVATION=0.25 # Database Configuration POSTGRES_HOST=db POSTGRES_PORT=5432 POSTGRES_DB=todoapp POSTGRES_USER=todoapp POSTGRES_PASSWORD=todoapp_password DB_PORT=5432 # Security Configuration ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 * * * [Build the Docker image](https://docs.docker.com/guides/nodejs/containerize/#build-the-docker-image) ----------------------------------------------------------------------------------------------------- The default Dockerfile generated by `docker init` provides a reliable baseline for standard Node.js applications. However, since this project is a full-stack TypeScript application that includes both a backend API and frontend React components, the Dockerfile should be customized to better support and optimize this specific architecture. ### [Review the generated files](https://docs.docker.com/guides/nodejs/containerize/#review-the-generated-files) In the following step, you’ll improve the Dockerfile and configuration files by following best practices: * Use multi-stage builds to keep the final image clean and small * Improve performance and security by only including what’s needed These updates make your app easier to deploy and faster to load. > Note > > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. > For full details, see the [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) > . ### [Step 1: Configure the Dockerfile](https://docs.docker.com/guides/nodejs/containerize/#step-1-configure-the-dockerfile) Before creating a Dockerfile, you need to choose a base image. You can either use the [Node.js Official Image](https://hub.docker.com/_/node) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog) . Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/) . > Important > > This guide uses a stable Node.js LTS image tag that is considered secure when the guide is written. Because new releases and security patches are published regularly, the tag shown here may no longer be the safest option when you follow the guide. Always review the latest available image tags and select a secure, up-to-date version before building or deploying your application. > > Official Node.js Docker Images: [https://hub.docker.com/\_/node](https://hub.docker.com/_/node) Using Docker Hardened Images Using the Docker Official Image Docker Hardened Images (DHIs) are available for Node.js in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/node) . Docker Hardened Images are freely available to everyone with no subscription required. You can pull and use them like any other Docker image after signing in to the DHI registry. For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Node.js DHI (check the catalog for available versions): $ docker pull dhi.io/node:24-alpine3.22-dev In the following Dockerfile, the `FROM` instruction uses `dhi.io/node:24-alpine3.22-dev` as the base image. # ======================================== # Optimized Multi-Stage Dockerfile # Node.js TypeScript Application (Using DHI) # ======================================== FROM dhi.io/node:24-alpine3.22-dev AS base # Set working directory WORKDIR /app # Create non-root user for security RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 -G nodejs && \ chown -R nodejs:nodejs /app # ======================================== # Dependencies Stage # ======================================== FROM base AS deps # Copy package files COPY package*.json ./ # Install production dependencies RUN --mount=type=cache,target=/root/.npm,sharing=locked \ npm ci --omit=dev && \ npm cache clean --force # Set proper ownership RUN chown -R nodejs:nodejs /app # ======================================== # Build Dependencies Stage # ======================================== FROM base AS build-deps # Copy package files COPY package*.json ./ # Install all dependencies with build optimizations RUN --mount=type=cache,target=/root/.npm,sharing=locked \ npm ci --no-audit --no-fund && \ npm cache clean --force # Create necessary directories and set permissions RUN mkdir -p /app/node_modules/.vite && \ chown -R nodejs:nodejs /app # ======================================== # Build Stage # ======================================== FROM build-deps AS build # Copy only necessary files for building (respects .dockerignore) COPY --chown=nodejs:nodejs . . # Build the application RUN npm run build # Set proper ownership RUN chown -R nodejs:nodejs /app # ======================================== # Development Stage # ======================================== FROM build-deps AS development # Set environment ENV NODE_ENV=development \ NPM_CONFIG_LOGLEVEL=warn # Copy source files COPY . . # Ensure all directories have proper permissions RUN mkdir -p /app/node_modules/.vite && \ chown -R nodejs:nodejs /app && \ chmod -R 755 /app # Switch to non-root user USER nodejs # Expose ports EXPOSE 3000 5173 9229 # Start development server CMD ["npm", "run", "dev:docker"] # ======================================== # Production Stage # ======================================== FROM dhi.io/node:24-alpine3.22-dev AS production # Set working directory WORKDIR /app # Create non-root user for security RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 -G nodejs && \ chown -R nodejs:nodejs /app # Set optimized environment variables ENV NODE_ENV=production \ NODE_OPTIONS="--max-old-space-size=256 --no-warnings" \ NPM_CONFIG_LOGLEVEL=silent # Copy production dependencies from deps stage COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules COPY --from=deps --chown=nodejs:nodejs /app/package*.json ./ # Copy built application from build stage COPY --from=build --chown=nodejs:nodejs /app/dist ./dist # Switch to non-root user for security USER nodejs # Expose port EXPOSE 3000 # Start production server CMD ["node", "dist/server.js"] # ======================================== # Test Stage # ======================================== FROM build-deps AS test # Set environment ENV NODE_ENV=test \ CI=true # Copy source files COPY --chown=nodejs:nodejs . . # Switch to non-root user USER nodejs # Run tests with coverage CMD ["npm", "run", "test:coverage"] Now you need to create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following optimized configuration: # ======================================== # Optimized Multi-Stage Dockerfile # Node.js TypeScript Application # ======================================== ARG NODE_VERSION=24.11.1-alpine FROM node:${NODE_VERSION} AS base # Set working directory WORKDIR /app # Create non-root user for security RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 -G nodejs && \ chown -R nodejs:nodejs /app # ======================================== # Dependencies Stage # ======================================== FROM base AS deps # Copy package files COPY package*.json ./ # Install production dependencies RUN --mount=type=cache,target=/root/.npm,sharing=locked \ npm ci --omit=dev && \ npm cache clean --force # Set proper ownership RUN chown -R nodejs:nodejs /app # ======================================== # Build Dependencies Stage # ======================================== FROM base AS build-deps # Copy package files COPY package*.json ./ # Install all dependencies with build optimizations RUN --mount=type=cache,target=/root/.npm,sharing=locked \ npm ci --no-audit --no-fund && \ npm cache clean --force # Create necessary directories and set permissions RUN mkdir -p /app/node_modules/.vite && \ chown -R nodejs:nodejs /app # ======================================== # Build Stage # ======================================== FROM build-deps AS build # Copy only necessary files for building (respects .dockerignore) COPY --chown=nodejs:nodejs . . # Build the application RUN npm run build # Set proper ownership RUN chown -R nodejs:nodejs /app # ======================================== # Development Stage # ======================================== FROM build-deps AS development # Set environment ENV NODE_ENV=development \ NPM_CONFIG_LOGLEVEL=warn # Copy source files COPY . . # Ensure all directories have proper permissions RUN mkdir -p /app/node_modules/.vite && \ chown -R nodejs:nodejs /app && \ chmod -R 755 /app # Switch to non-root user USER nodejs # Expose ports EXPOSE 3000 5173 9229 # Start development server CMD ["npm", "run", "dev:docker"] # ======================================== # Production Stage # ======================================== ARG NODE_VERSION=24.11.1-alpine FROM node:${NODE_VERSION} AS production # Set working directory WORKDIR /app # Create non-root user for security RUN addgroup -g 1001 -S nodejs && \ adduser -S nodejs -u 1001 -G nodejs && \ chown -R nodejs:nodejs /app # Set optimized environment variables ENV NODE_ENV=production \ NODE_OPTIONS="--max-old-space-size=256 --no-warnings" \ NPM_CONFIG_LOGLEVEL=silent # Copy production dependencies from deps stage COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules COPY --from=deps --chown=nodejs:nodejs /app/package*.json ./ # Copy built application from build stage COPY --from=build --chown=nodejs:nodejs /app/dist ./dist # Switch to non-root user for security USER nodejs # Expose port EXPOSE 3000 # Start production server CMD ["node", "dist/server.js"] # ======================================== # Test Stage # ======================================== FROM build-deps AS test # Set environment ENV NODE_ENV=test \ CI=true # Copy source files COPY --chown=nodejs:nodejs . . # Switch to non-root user USER nodejs # Run tests with coverage CMD ["npm", "run", "test:coverage"] Key features of this Dockerfile: * Multi-stage structure — Separate stages for dependencies, build, development, production, and testing to keep each phase clean and efficient. * Lean production image — Optimized layering reduces size and keeps only what’s required to run the app. * Security-minded setup — Uses a dedicated non-root user and excludes unnecessary packages. * Performance-friendly design — Effective use of caching and well-structured layers for faster builds. * Clean runtime environment — Removes files not needed in production, such as docs, tests, and build caches. * Straightforward port usage — The app runs on port 3000 internally, exposed externally as port 8080. * Memory-optimized runtime — Node.js is configured to run with a smaller memory limit than the default. ### [Step 2: Configure the .dockerignore file](https://docs.docker.com/guides/nodejs/containerize/#step-2-configure-the-dockerignore-file) The `.dockerignore` file tells Docker which files and folders to exclude when building the image. > Note > > This helps: > > * Reduce image size > * Speed up the build process > * Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. > > To learn more, visit the [.dockerignore reference](https://docs.docker.com/reference/dockerfile/#dockerignore-file) > . Copy and replace the contents of your existing `.dockerignore` with the optimized configuration: # Optimized .dockerignore for Node.js + React Todo App # Based on actual project structure # Version control .git/ .github/ .gitignore # Dependencies (installed in container) node_modules/ # Build outputs (built in container) dist/ # Environment files .env* # Development files .vscode/ *.log coverage/ .eslintcache # OS files .DS_Store Thumbs.db # Documentation *.md docs/ # Deployment configs compose.yml Taskfile.yml nodejs-sample-kubernetes.yaml # Non-essential configs (keep build configs) *.config.js !vite.config.ts !esbuild.config.js !tailwind.config.js !postcss.config.js !tsconfig.json ### [Step 3: Build the Node.js application image](https://docs.docker.com/guides/nodejs/containerize/#step-3-build-the-nodejs-application-image) After creating all the configuration files, your project directory should now contain all necessary Docker configuration files: ├── docker-nodejs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yml │ └── README.Docker.md Now you can build the Docker image for your Node.js application. > Note > > The `docker build` command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the [build context](https://docs.docker.com/build/concepts/context/#what-is-a-build-context) > ). Run the following command from the root of your project: $ docker build --target production --tag docker-nodejs-sample . What this command does: * Uses the Dockerfile in the current directory (.) * Targets the production stage of the multi-stage build * Packages the application and its dependencies into a Docker image * Tags the image as docker-nodejs-sample so you can reference it later #### [Step 4: View local images](https://docs.docker.com/guides/nodejs/containerize/#step-4-view-local-images) After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](https://docs.docker.com/desktop/use-desktop/images/) . Since you're already working in the terminal, use the Docker CLI. To list all locally available Docker images, run the following command: $ docker images Example Output: REPOSITORY TAG IMAGE ID CREATED SIZE docker-nodejs-sample latest 423525528038 14 seconds ago 237.46MB This output provides key details about your images: * **Repository** – The name assigned to the image. * **Tag** – A version label that helps identify different builds (e.g., latest). * **Image ID** – A unique identifier for the image. * **Created** – The timestamp indicating when the image was built. * **Size** – The total disk space used by the image. If the build was successful, you should see `docker-nodejs-sample` image listed. * * * [Run the containerized application](https://docs.docker.com/guides/nodejs/containerize/#run-the-containerized-application) --------------------------------------------------------------------------------------------------------------------------- In the previous step, you created a Dockerfile for your Node.js application and built a Docker image using the docker build command. Now it’s time to run that image in a container and verify that your application works as expected. Inside the `docker-nodejs-sample` directory, run the following command in a terminal. $ docker compose up app-dev --build The development application will start with both servers: * **API Server**: [http://localhost:3000](http://localhost:3000/) - Express.js backend with REST API * **Frontend**: [http://localhost:5173](http://localhost:5173/) - Vite dev server with React frontend * **Health Check**: [http://localhost:3000/health](http://localhost:3000/health) - Application health status For production deployment, you can use: $ docker compose up app-prod --build Which serves the full-stack app at [http://localhost:8080](http://localhost:8080/) with the Express server running on port 3000 internally, mapped to port 8080 externally. You should see a modern Todo List application with React 19 and a fully functional REST API. Press `CTRL + C` in the terminal to stop your application. ### [Run the application in the background](https://docs.docker.com/guides/nodejs/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-nodejs-sample` directory, run the following command in a terminal. $ docker compose up app-dev --build -d Open a browser and view the application at [http://localhost:3000](http://localhost:3000/) (API) or [http://localhost:5173](http://localhost:5173/) (frontend). You should see the Todo application running. To confirm that the container is running, use `docker ps` command: $ docker ps This will list all active containers along with their ports, names, and status. Look for a container exposing ports 3000, 5173, and 9229 for the development app. Example Output: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 93f3faee32c3 docker-nodejs-sample-app-dev "docker-entrypoint.s…" 33 seconds ago Up 31 seconds 0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp, 0.0.0.0:5173->5173/tcp, [::]:5173->5173/tcp, 0.0.0.0:9230->9229/tcp, [::]:9230->9229/tcp todoapp-dev ### [Run different profiles](https://docs.docker.com/guides/nodejs/containerize/#run-different-profiles) You can run different configurations using Docker Compose profiles: # Run production $ docker compose up app-prod -d # Run tests $ docker compose up app-test -d To stop the application, run: $ docker compose down > Note > > For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) > . * * * [Summary](https://docs.docker.com/guides/nodejs/containerize/#summary) ----------------------------------------------------------------------- In this guide, you learned how to containerize, build, and run a Node.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup. What you accomplished: * Initialized your project using `docker init` to scaffold essential Docker configuration files. * Created a `compose.yml` file with development, production, and database services. * Set up environment configuration with a `.env` file for flexible deployment settings. * Replaced the default `Dockerfile` with a multi-stage build optimized for TypeScript and React. * Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. * Built your Docker image using `docker build`. * Ran the container using `docker compose up`, both in the foreground and in detached mode. * Verified that the app was running by visiting [http://localhost:8080](http://localhost:8080/) (production) or [http://localhost:3000](http://localhost:3000/) (development). * Learned how to stop the containerized application using `docker compose down`. You now have a fully containerized Node.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency. * * * [Related resources](https://docs.docker.com/guides/nodejs/containerize/#related-resources) ------------------------------------------------------------------------------------------- Explore official references and best practices to sharpen your Docker workflow: * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) – Learn how to separate build and runtime stages. * [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. * [Build context in Docker](https://docs.docker.com/build/concepts/context/) – Learn how context affects image builds. * [`docker init` CLI reference](https://docs.docker.com/reference/cli/docker/init/) – Scaffold Docker assets automatically. * [`docker build` CLI reference](https://docs.docker.com/reference/cli/docker/image/build/) – Build Docker images from a Dockerfile. * [`docker images` CLI reference](https://docs.docker.com/reference/cli/docker/image/ls/) – Manage and inspect local Docker images. * [`docker compose up` CLI reference](https://docs.docker.com/reference/cli/docker/compose/up/) – Start and run multi-container applications. * [`docker compose down` CLI reference](https://docs.docker.com/reference/cli/docker/compose/down/) – Stop and remove containers, networks, and volumes. * * * [Next steps](https://docs.docker.com/guides/nodejs/containerize/#next-steps) ----------------------------------------------------------------------------- With your Node.js application now containerized, you're ready to move on to the next step. In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine. [Use containers for Node.js development »](https://docs.docker.com/guides/nodejs/develop/) --- # Containerize | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Containerize a Next.js application](https://docs.docker.com/guides/nextjs/) This guide explains how to containerize Next.js applications, set up development and testing in containers, automate builds with GitHub Actions, and deploy to Kubernetes. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Frameworks 20 minutes [1](https://docs.docker.com/guides/nextjs/containerize/) [Containerize](https://docs.docker.com/guides/nextjs/containerize/) [2](https://docs.docker.com/guides/nextjs/develop/) [Develop your app](https://docs.docker.com/guides/nextjs/develop/) [3](https://docs.docker.com/guides/nextjs/run-tests/) [Run your tests and lint](https://docs.docker.com/guides/nextjs/run-tests/) [4](https://docs.docker.com/guides/nextjs/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/nextjs/configure-github-actions/) [5](https://docs.docker.com/guides/nextjs/deploy/) [Test your deployment](https://docs.docker.com/guides/nextjs/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Next.js Application ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/nextjs/containerize/#prerequisites) ----------------------------------------------------------------------------------- Before you begin, make sure the following tools are installed and available on your system: * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. > **New to Docker?** > Start with the [Docker basics](https://docs.docker.com/get-started/docker-concepts/the-basics/what-is-a-container/) > guide to get familiar with key concepts like images, containers, and Dockerfiles. * * * [Overview](https://docs.docker.com/guides/nextjs/containerize/#overview) ------------------------------------------------------------------------- This guide walks you through containerizing a Next.js application with Docker. You'll learn how to create a production-ready Docker image using best practices that improve performance, security, scalability, and deployment efficiency. By the end of this guide, you will: * Containerize a Next.js application using Docker. * Create and optimize a Dockerfile for production builds. * Use multi-stage builds to minimize image size. * Leverage Next.js standalone or export output for efficient containerization. * Follow best practices for building secure and maintainable Docker images. * * * [Get the sample application](https://docs.docker.com/guides/nextjs/containerize/#get-the-sample-application) ------------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the git repository: $ git clone https://github.com/kristiyan-velkov/docker-nextjs-sample * * * [Generate a Dockerfile](https://docs.docker.com/guides/nextjs/containerize/#generate-a-dockerfile) --------------------------------------------------------------------------------------------------- Docker provides an interactive CLI tool called `docker init` that helps scaffold the necessary configuration files for containerizing your application. This includes generating a `Dockerfile`, `.dockerignore`, `compose.yaml`, and `README.Docker.md`. To begin, navigate to the project directory: $ cd docker-nextjs-sample Then run the following command: $ docker init You'll see output similar to: Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! The CLI will prompt you with a few questions about your app setup. For consistency, please use the same responses shown in the example below when prompted: | Question | Answer | | --- | --- | | What application platform does your project use? | Node | | What version of Node do you want to use? | 24.14.0-alpine | | Which package manager do you want to use? | npm | | Do you want to run "npm run build" before starting server? | yes | | What directory is your build output to? | .next | | What command do you want to use to start the app? | npm run start | | What port does your server listen on? | 3000 | After completion, your project directory will contain the following new files: ├── docker-nextjs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ └── README.Docker.md * * * [Build the Docker image](https://docs.docker.com/guides/nextjs/containerize/#build-the-docker-image) ----------------------------------------------------------------------------------------------------- The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, Next.js has specific requirements for production deployments. This guide shows two approaches: **standalone** output (Node.js server) and **export** output (static files with NGINX). ### [Step 1: Review the generated files](https://docs.docker.com/guides/nextjs/containerize/#step-1-review-the-generated-files) In this step, you'll add a Dockerfile and configuration files by following best practices: * Use multi-stage builds to keep the final image clean and small * **Standalone**: Node.js runs the Next.js server; **Export**: NGINX serves static files from the export * Improve performance and security by only including what's needed These updates help ensure your app is easy to deploy, fast to load, and production-ready. > Note > > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. > For full details, see the [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) > . ### [Step 2: Configure Next.js output and Dockerfile](https://docs.docker.com/guides/nextjs/containerize/#step-2-configure-nextjs-output-and-dockerfile) Before creating a Dockerfile, choose a base image: the [Node.js Official Image](https://hub.docker.com/_/node) or a [Docker Hardened Image (DHI)](https://hub.docker.com/hardened-images/catalog) from the Hardened Image catalog. Choosing DHI gives you a production-ready, lightweight, and secure image. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/) . > Important > > This guide uses stable Node.js LTS image tags that are considered secure when the guide is written. Because new releases and security patches are published regularly, always review the [official Node.js Docker images](https://hub.docker.com/_/node) > and select a secure, up-to-date version before building or deploying. * * * #### [2.1 Next.js with standalone output](https://docs.docker.com/guides/nextjs/containerize/#21-nextjs-with-standalone-output) **Standalone output** (`output: "standalone"`) makes Next.js build a self-contained output that includes only the files and dependencies needed to run the application. A single `node server.js` can serve the app, which is ideal for Docker and supports server-side rendering, API routes, and incremental static regeneration. For details, see the [Next.js output configuration documentation](https://nextjs.org/docs/app/api-reference/config/next-config-js/output) (including the "standalone" option). The container runs the Next.js server with Node.js on **port 3000**. **Configure Next.js** — Open or create `next.config.ts` in your project root: import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "standalone", }; export default nextConfig; Choose either a Docker Hardened Image or the Docker Official Image, then create or replace your `Dockerfile` with the content from the selected tab below. Using Docker Hardened Images Using the Docker Official Image Docker Hardened Images (DHIs) are available for Node.js in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/node) . For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Node.js DHI (check the catalog for available versions): $ docker pull dhi.io/node:24-alpine3.22-dev 3. Replace the generated Dockerfile with the following. The `FROM` instructions use `dhi.io/node:24-alpine3.22-dev`. Check the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog) for the latest versions and update the image tags as needed for security and compatibility. # ============================================ # Stage 1: Dependencies Installation Stage # ============================================ # IMPORTANT: Docker Hardened Image (DHI) Version Maintenance # This Dockerfile uses dhi.io/node. Regularly validate and update to the latest DHI versions in the catalog for security and compatibility. FROM dhi.io/node:24-alpine3.22-dev AS dependencies # Set working directory WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ # Install project dependencies with frozen lockfile for reproducible builds RUN --mount=type=cache,target=/root/.npm \ --mount=type=cache,target=/usr/local/share/.cache/yarn \ --mount=type=cache,target=/root/.local/share/pnpm/store \ if [ -f package-lock.json ]; then \ npm ci --no-audit --no-fund; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn install --frozen-lockfile --production=false; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm install --frozen-lockfile; \ else \ echo "No lockfile found." && exit 1; \ fi # ============================================ # Stage 2: Build Next.js application in standalone mode # ============================================ FROM dhi.io/node:24-alpine3.22-dev AS builder # Set working directory WORKDIR /app # Copy project dependencies from dependencies stage COPY --from=dependencies /app/node_modules ./node_modules # Copy application source code COPY . . ENV NODE_ENV=production # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. # ENV NEXT_TELEMETRY_DISABLED=1 # Build Next.js application # If you want to speed up Docker rebuilds, you can cache the build artifacts # by adding: --mount=type=cache,target=/app/.next/cache # This caches the .next/cache directory across builds, but it also prevents # .next/cache/fetch-cache from being included in the final image, meaning # cached fetch responses from the build won't be available at runtime. RUN if [ -f package-lock.json ]; then \ npm run build; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn build; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm build; \ else \ echo "No lockfile found." && exit 1; \ fi # ============================================ # Stage 3: Run Next.js application # ============================================ FROM dhi.io/node:24-alpine3.22-dev AS runner # Set working directory WORKDIR /app # Set production environment variables ENV NODE_ENV=production ENV PORT=3000 ENV HOSTNAME="0.0.0.0" # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the run time. # ENV NEXT_TELEMETRY_DISABLED=1 # Copy production assets COPY --from=builder --chown=node:node /app/public ./public # Set the correct permission for prerender cache RUN mkdir .next RUN chown node:node .next # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=builder --chown=node:node /app/.next/standalone ./ COPY --from=builder --chown=node:node /app/.next/static ./.next/static # If you want to persist the fetch cache generated during the build so that # cached responses are available immediately on startup, uncomment this line: # COPY --from=builder --chown=node:node /app/.next/cache ./.next/cache # Switch to non-root user for security best practices USER node # Expose port 3000 to allow HTTP traffic EXPOSE 3000 # Start Next.js standalone server CMD ["node", "server.js"] Create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following (uses `node`): # ============================================ # Stage 1: Dependencies Installation Stage # ============================================ ARG NODE_VERSION=24.14.0-slim FROM node:${NODE_VERSION} AS dependencies # Set working directory WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ # Install project dependencies with frozen lockfile for reproducible builds RUN --mount=type=cache,target=/root/.npm \ --mount=type=cache,target=/usr/local/share/.cache/yarn \ --mount=type=cache,target=/root/.local/share/pnpm/store \ if [ -f package-lock.json ]; then \ npm ci --no-audit --no-fund; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn install --frozen-lockfile --production=false; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm install --frozen-lockfile; \ else \ echo "No lockfile found." && exit 1; \ fi # ============================================ # Stage 2: Build Next.js application in standalone mode # ============================================ FROM node:${NODE_VERSION} AS builder # Set working directory WORKDIR /app # Copy project dependencies from dependencies stage COPY --from=dependencies /app/node_modules ./node_modules # Copy application source code COPY . . ENV NODE_ENV=production # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. # ENV NEXT_TELEMETRY_DISABLED=1 # Build Next.js application # If you want to speed up Docker rebuilds, you can cache the build artifacts # by adding: --mount=type=cache,target=/app/.next/cache # This caches the .next/cache directory across builds, but it also prevents # .next/cache/fetch-cache from being included in the final image, meaning # cached fetch responses from the build won't be available at runtime. RUN if [ -f package-lock.json ]; then \ npm run build; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn build; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm build; \ else \ echo "No lockfile found." && exit 1; \ fi # ============================================ # Stage 3: Run Next.js application # ============================================ FROM node:${NODE_VERSION} AS runner # Set working directory WORKDIR /app # Set production environment variables ENV NODE_ENV=production ENV PORT=3000 ENV HOSTNAME="0.0.0.0" # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the run time. # ENV NEXT_TELEMETRY_DISABLED=1 # Copy production assets COPY --from=builder --chown=node:node /app/public ./public # Set the correct permission for prerender cache RUN mkdir .next RUN chown node:node .next # Automatically leverage output traces to reduce image size # https://nextjs.org/docs/advanced-features/output-file-tracing COPY --from=builder --chown=node:node /app/.next/standalone ./ COPY --from=builder --chown=node:node /app/.next/static ./.next/static # If you want to persist the fetch cache generated during the build so that # cached responses are available immediately on startup, uncomment this line: # COPY --from=builder --chown=node:node /app/.next/cache ./.next/cache # Switch to non-root user for security best practices USER node # Expose port 3000 to allow HTTP traffic EXPOSE 3000 # Start Next.js standalone server CMD ["node", "server.js"] > Note > > This Dockerfile uses three stages: **dependencies**, **builder**, and **runner**. The final image runs `node server.js` and listens on port 3000. * * * #### [2.2 Next.js with export output](https://docs.docker.com/guides/nextjs/containerize/#22-nextjs-with-export-output) **Output export** (`output: "export"`) makes Next.js build a fully static site at build time. It generates HTML, CSS, and JavaScript into an `out` directory that can be served by any static host or CDN—no Node.js server at runtime. Use this when you don't need server-side rendering or API routes. For details, see the [Next.js output configuration documentation](https://nextjs.org/docs/app/api-reference/config/next-config-js/output) . **Configure Next.js** — Open `next.config.ts` in your project root and add the following code: import type { NextConfig } from "next"; const nextConfig: NextConfig = { output: "export", trailingSlash: true, images: { unoptimized: true, }, }; export default nextConfig; Choose either a Docker Hardened Image or the Docker Official Image, then replace your `Dockerfile` with the content from the selected tab below. Using Docker Hardened Images Using the Docker Official Image Docker Hardened Images (DHIs) are available for Node.js and NGINX in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog) . For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Node.js DHI (check the catalog for available versions): $ docker pull dhi.io/node:24-alpine3.22-dev 3. Pull the Nginx DHI (check the catalog for available versions): $ docker pull dhi.io/nginx:1.28.0-alpine3.21-dev 4. Replace the generated Dockerfile with the following. The `FROM` instructions use Docker Hardened Images: `dhi.io/node:24-alpine3.22-dev` and `dhi.io/nginx:1.28.0-alpine3.21-dev`. Check the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog) for the latest versions and update the image tags as needed for security and compatibility. # ============================================ # Stage 1: Dependencies Installation Stage # ============================================ # IMPORTANT: Docker Hardened Image (DHI) Version Maintenance # This Dockerfile uses dhi.io/node and dhi.io/nginx. Regularly validate and update to the latest DHI versions in the catalog for security and compatibility. FROM dhi.io/node:24-alpine3.22-dev AS dependencies # Set the working directory WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ # Install project dependencies with frozen lockfile for reproducible builds RUN --mount=type=cache,target=/root/.npm \ --mount=type=cache,target=/usr/local/share/.cache/yarn \ --mount=type=cache,target=/root/.local/share/pnpm/store \ if [ -f package-lock.json ]; then \ npm ci --no-audit --no-fund; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn install --frozen-lockfile --production=false; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm install --frozen-lockfile; \ else \ echo "No lockfile found." && exit 1; \ fi # ============================================ # Stage 2: Build Next.js Application # ============================================ FROM dhi.io/node:24-alpine3.22-dev AS builder # Set the working directory WORKDIR /app # Copy project dependencies from dependencies stage COPY --from=dependencies /app/node_modules ./node_modules # Copy application source code COPY . . ENV NODE_ENV=production # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. # ENV NEXT_TELEMETRY_DISABLED=1 # Build Next.js application RUN --mount=type=cache,target=/app/.next/cache \ if [ -f package-lock.json ]; then \ npm run build; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn build; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm build; \ else \ echo "No lockfile found." && exit 1; \ fi # ========================================= # Stage 3: Serve Static Files with Nginx # ========================================= FROM dhi.io/nginx:1.28.0-alpine3.21-dev AS runner # Set the working directory WORKDIR /app # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the run time. # ENV NEXT_TELEMETRY_DISABLED=1 # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --chown=nginx:nginx --from=builder /app/out /usr/share/nginx/html # Non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] Create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following (uses `node` and `nginxinc/nginx-unprivileged`): # ============================================ # Stage 1: Dependencies Installation Stage # ============================================ ARG NODE_VERSION=24.14.0-slim ARG NGINXINC_IMAGE_TAG=alpine3.22 FROM node:${NODE_VERSION} AS dependencies # Set the working directory WORKDIR /app # Copy package-related files first to leverage Docker's caching mechanism COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ # Install project dependencies with frozen lockfile for reproducible builds RUN --mount=type=cache,target=/root/.npm \ --mount=type=cache,target=/usr/local/share/.cache/yarn \ --mount=type=cache,target=/root/.local/share/pnpm/store \ if [ -f package-lock.json ]; then \ npm ci --no-audit --no-fund; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn install --frozen-lockfile --production=false; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm install --frozen-lockfile; \ else \ echo "No lockfile found." && exit 1; \ fi # ============================================ # Stage 2: Build Next.js Application # ============================================ FROM node:${NODE_VERSION} AS builder # Set the working directory WORKDIR /app # Copy project dependencies from dependencies stage COPY --from=dependencies /app/node_modules ./node_modules # Copy application source code COPY . . ENV NODE_ENV=production # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the build. # ENV NEXT_TELEMETRY_DISABLED=1 # Build Next.js application RUN --mount=type=cache,target=/app/.next/cache \ if [ -f package-lock.json ]; then \ npm run build; \ elif [ -f yarn.lock ]; then \ corepack enable yarn && yarn build; \ elif [ -f pnpm-lock.yaml ]; then \ corepack enable pnpm && pnpm build; \ else \ echo "No lockfile found." && exit 1; \ fi # ========================================= # Stage 3: Serve Static Files with Nginx # ========================================= FROM nginxinc/nginx-unprivileged:${NGINXINC_IMAGE_TAG} AS runner # Set the working directory WORKDIR /app # Next.js collects completely anonymous telemetry data about general usage. # Learn more here: https://nextjs.org/telemetry # Uncomment the following line in case you want to disable telemetry during the run time. # ENV NEXT_TELEMETRY_DISABLED=1 # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory COPY --from=builder /app/out /usr/share/nginx/html # Non-root user for security best practices USER nginx # Expose port 8080 to allow HTTP traffic EXPOSE 8080 # Start Nginx directly with custom config ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] > Note > > We use [nginx-unprivileged](https://hub.docker.com/r/nginxinc/nginx-unprivileged) > instead of the standard NGINX image to run as a non-root user, following security best practices. 1. **Create `nginx.conf`** (required for export output only) — Create a file named `nginx.conf` in the root of your project: # Minimal Nginx config for static Next.js app worker_processes 1; # Store PID in /tmp (always writable) pid /tmp/nginx.pid; events { worker_connections 1024; } http { include /etc/nginx/mime.types; default_type application/octet-stream; # Disable logging to avoid permission issues access_log off; error_log /dev/stderr; # Optimize static file serving sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; # Gzip compression gzip on; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; gzip_min_length 256; server { listen 8080; server_name localhost; # Serve static files root /usr/share/nginx/html; index index.html; # Handle Next.js static export routing # See: https://nextjs.org/docs/app/guides/static-exports#deploying location / { try_files $uri $uri.html $uri/ =404; } # This is necessary when `trailingSlash: false` (default). # You can omit this when `trailingSlash: true` in next.config. # Handles nested routes like /blog/post -> /blog/post.html location ~ ^/(.+)/$ { rewrite ^/(.+)/$ /$1.html break; } # Serve Next.js static assets location ~ ^/_next/ { try_files $uri =404; expires 1y; add_header Cache-Control "public, immutable"; } # Optional 404 handling error_page 404 /404.html; location = /404.html { internal; } } } > Note > > Export uses **port 8080**. For more details, see the [Next.js output configuration](https://nextjs.org/docs/app/api-reference/config/next-config-js/output) > and [NGINX documentation](https://nginx.org/en/docs/) > . ### [Step 3: Configure the .dockerignore file](https://docs.docker.com/guides/nextjs/containerize/#step-3-configure-the-dockerignore-file) The `.dockerignore` file tells Docker which files and folders to exclude when building the image. > Note > > This helps: > > * Reduce image size > * Speed up the build process > * Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. > > To learn more, visit the [.dockerignore reference](https://docs.docker.com/reference/dockerfile/#dockerignore-file) > . Copy and replace the contents of your existing `.dockerignore` with the configuration below: # Dependencies (installed inside the image, never copy from host) node_modules/ .pnp/ .pnp.js .pnpm-store/ # Next.js build output (generated during the image build) .next/ out/ dist/ build/ .vercel/ # Testing (not needed in the production image) coverage/ .nyc_output/ __tests__/ __mocks__/ jest/ cypress/ playwright-report/ test-results/ .vitest/ # Environment files (avoid leaking secrets into the build context) .env .env* .env.local .env.development.local .env.test.local .env.production.local # Debug and log files npm-debug.log* yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* *.log # IDE and editor files .vscode/ .idea/ .cursor/ .cursorrules .copilot/ *.swp *.swo *~ # Git .git/ .gitignore .gitattributes # Docker files (reduce build context; not needed inside the image) Dockerfile* .dockerignore docker-compose*.yml compose*.yaml # Documentation (not needed in the image) *.md docs/ # CI/CD (not needed in the image) .github/ .gitlab-ci.yml .travis.yml .circleci/ Jenkinsfile # TypeScript and build metadata *.tsbuildinfo # Cache and temporary directories .cache/ .parcel-cache/ .eslintcache .stylelintcache .turbo/ .tmp/ .temp/ # Sensitive or dev-only config (optional; omit if your build needs these) .pem .editorconfig .prettierrc* .eslintrc* .stylelintrc* .babelrc* *.iml # OS-specific files .DS_Store ._* .Spotlight-V100 .Trashes ehthumbs.db Thumbs.db Desktop.ini ### [Step 4: Build the Next.js application image](https://docs.docker.com/guides/nextjs/containerize/#step-4-build-the-nextjs-application-image) With your custom configuration in place, you're now ready to build the Docker image. Use the Dockerfile you chose in Step 3 (standalone or export). The setup includes: * Multi-stage builds for optimized image size * Standalone: Node.js server on port 3000; Export: NGINX serving static files on port 8080 * Non-root user for enhanced security * Proper file permissions and ownership After completing the previous steps, your project directory should contain at least the following files (export also requires `nginx.conf`): ├── docker-nextjs-sample/ │ ├── Dockerfile │ ├── .dockerignore │ ├── compose.yaml │ ├── next.config.ts │ └── README.Docker.md Now that your Dockerfile is configured, you can build the Docker image for your Next.js application. > Note > > The `docker build` command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the [build context](https://docs.docker.com/build/concepts/context/#what-is-a-build-context) > ). Run the following command from the root of your project: $ docker build --tag nextjs-sample . What this command does: * Uses the Dockerfile in the current directory (.) * Packages the application and its dependencies into a Docker image * Tags the image as nextjs-sample so you can reference it later ### [Step 5: View local images](https://docs.docker.com/guides/nextjs/containerize/#step-5-view-local-images) After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](https://docs.docker.com/desktop/use-desktop/images/) . Since you're already working in the terminal, let's use the Docker CLI. To list all locally available Docker images, run the following command: $ docker images Example Output: REPOSITORY TAG IMAGE ID CREATED SIZE nextjs-sample latest 8c5fc80f098e 14 seconds ago 130MB This output provides key details about your images: * **Repository** – The name assigned to the image. * **Tag** – A version label that helps identify different builds (e.g., latest). * **Image ID** – A unique identifier for the image. * **Created** – The timestamp indicating when the image was built. * **Size** – The total disk space used by the image. If the build was successful, you should see `nextjs-sample` image listed. * * * [Run the containerized application](https://docs.docker.com/guides/nextjs/containerize/#run-the-containerized-application) --------------------------------------------------------------------------------------------------------------------------- In the previous step, you created a Dockerfile for your Next.js application and built a Docker image using the docker build command. Now it's time to run that image in a container and verify that your application works as expected. Run the following command in a terminal. Use the port that matches your setup: **standalone** uses port 3000, **export** uses port 8080. $ docker run -p 3000:3000 nextjs-sample For **export** output, use port 8080 instead: $ docker run -p 8080:8080 nextjs-sample Open a browser and view the application: [http://localhost:3000](http://localhost:3000/) for **standalone** or [http://localhost:8080](http://localhost:8080/) for **export**. You should see your Next.js web application. Press `ctrl+c` in the terminal to stop your application. ### [Run the application in the background](https://docs.docker.com/guides/nextjs/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option and `--name` to give the container a name so you can stop it later: $ docker run -d -p 3000:3000 --name nextjs-app nextjs-sample For **export** output, use port 8080: $ docker run -d -p 8080:8080 --name nextjs-app nextjs-sample Open a browser and view the application: [http://localhost:3000](http://localhost:3000/) for **standalone** or [http://localhost:8080](http://localhost:8080/) for **export**. You should see your web application. To confirm that the container is running, use the `docker ps` command: $ docker ps This will list all active containers along with their ports, names, and status. Look for a container exposing port 3000 (standalone) or 8080 (export). Example Output: CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f49b74736a9d nextjs-sample "node server.js" About a minute ago Up About a minute 0.0.0.0:3000->3000/tcp nextjs-app To stop the application, run: $ docker stop nextjs-app > Note > > For more information about running containers, see the [`docker run` CLI reference](https://docs.docker.com/reference/cli/docker/container/run/) > and the [`docker stop` CLI reference](https://docs.docker.com/reference/cli/docker/container/stop/) > . * * * [Summary](https://docs.docker.com/guides/nextjs/containerize/#summary) ----------------------------------------------------------------------- In this guide, you learned how to containerize, build, and run a Next.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup. What you accomplished: * Initialized your project using `docker init` to scaffold essential Docker configuration files. * Configured Next.js for either standalone output (Node.js server) or export output (static files with NGINX). * Added a multi-stage Dockerfile for your chosen approach: standalone (port 3000) or export (port 8080, with `nginx.conf`). * Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. * Built your Docker image using `docker build`. * Ran the container using `docker run` with the image name `nextjs-sample`, both in the foreground and in detached mode. * Verified that the app was running by visiting [http://localhost:3000](http://localhost:3000/) (standalone) or [http://localhost:8080](http://localhost:8080/) (export). * Learned how to stop the containerized application using `docker stop nextjs-app`. You now have a fully containerized Next.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency. * * * [Related resources](https://docs.docker.com/guides/nextjs/containerize/#related-resources) ------------------------------------------------------------------------------------------- Explore official references and best practices to sharpen your Docker workflow: * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) – Learn how to separate build and runtime stages. * [Best practices for writing Dockerfiles](https://docs.docker.com/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. * [Build context in Docker](https://docs.docker.com/build/concepts/context/) – Learn how context affects image builds. * [Next.js output configuration](https://nextjs.org/docs/app/api-reference/config/next-config-js/output) – Learn about Next.js production optimization (standalone and export). * [Next.js with Docker (standalone)](https://github.com/vercel/next.js/tree/canary/examples/with-docker) – Official Next.js example: standalone output with Node.js. * [Next.js with Docker (export)](https://github.com/vercel/next.js/tree/canary/examples/with-docker-export-output) – Official Next.js example: static export with Nginx or serve. * [`docker init` CLI reference](https://docs.docker.com/reference/cli/docker/init/) – Scaffold Docker assets automatically. * [`docker build` CLI reference](https://docs.docker.com/reference/cli/docker/image/build/) – Build Docker images from a Dockerfile. * [`docker images` CLI reference](https://docs.docker.com/reference/cli/docker/image/ls/) – Manage and inspect local Docker images. * [`docker run` CLI reference](https://docs.docker.com/reference/cli/docker/container/run/) – Run a command in a new container. * [`docker stop` CLI reference](https://docs.docker.com/reference/cli/docker/container/stop/) – Stop one or more running containers. * * * [Next steps](https://docs.docker.com/guides/nextjs/containerize/#next-steps) ----------------------------------------------------------------------------- With your Next.js application now containerized, you're ready to move on to the next step. In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine. [Use containers for Next.js development »](https://docs.docker.com/guides/nextjs/develop/) --- # The H2 problem | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Replace H2 with a real database for testing](https://docs.docker.com/guides/testcontainers-java-replace-h2/) Replace your H2 in-memory test database with a real PostgreSQL instance using the Testcontainers special JDBC URL — a one-line change. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-java-replace-h2/problem-with-h2/) [The H2 problem](https://docs.docker.com/guides/testcontainers-java-replace-h2/problem-with-h2/) [2](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/) [JDBC URL approach](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/) [3](https://docs.docker.com/guides/testcontainers-java-replace-h2/junit-extension-approach/) [JUnit 5 extension](https://docs.docker.com/guides/testcontainers-java-replace-h2/junit-extension-approach/) [« Back to all guides](https://docs.docker.com/guides/) The problem with H2 for testing =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * A common practice is to use lightweight databases like H2 or HSQL as in-memory databases for testing while using PostgreSQL, MySQL, or Oracle in production. This approach has significant drawbacks: * The test database might not support all features of your production database. * SQL syntax might not be compatible between H2 and your production database. * Tests passing with H2 don't guarantee they'll work in production. [Example: PostgreSQL-specific syntax](https://docs.docker.com/guides/testcontainers-java-replace-h2/problem-with-h2/#example-postgresql-specific-syntax) --------------------------------------------------------------------------------------------------------------------------------------------------------- Consider implementing an "upsert" — insert a product only if it doesn't already exist. In PostgreSQL, you can use: INSERT INTO products(id, code, name) VALUES(?,?,?) ON CONFLICT DO NOTHING; This query doesn't work with H2 by default: Caused by: org.h2.jdbc.JdbcSQLException: Syntax error in SQL statement "INSERT INTO products (id, code, name) VALUES (?, ?, ?) ON[*] CONFLICT DO NOTHING"; You can run H2 in PostgreSQL compatibility mode, but not all features are supported. The inverse is also true — H2 supports `ROWNUM()` which PostgreSQL doesn't. Testing with a different database than production means you can't trust your test results and must verify after deployment, defeating the purpose of automated tests. [The Spring Boot test using H2](https://docs.docker.com/guides/testcontainers-java-replace-h2/problem-with-h2/#the-spring-boot-test-using-h2) ---------------------------------------------------------------------------------------------------------------------------------------------- A typical H2-based test looks like this: @DataJpaTest class ProductRepositoryTest { @Autowired ProductRepository productRepository; @Test @Sql("classpath:/sql/seed-data.sql") void shouldGetAllProducts() { List products = productRepository.findAll(); assertEquals(2, products.size()); } } Spring Boot uses H2 automatically when it's on the classpath. The test passes, but it doesn't catch PostgreSQL-specific issues. [Replace H2 with the Testcontainers JDBC URL »](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/) --- # Prerequisites for Setting Up Laravel with Docker Compose | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Develop and Deploy Laravel applications with Docker Compose](https://docs.docker.com/guides/frameworks/laravel/) Learn how to efficiently set up Laravel development and production environments using Docker Compose. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/php/php-original.svg "PHP") PHP Frameworks 30 minutes [1](https://docs.docker.com/guides/frameworks/laravel/prerequisites/) [Prerequisites for Setting Up Laravel with Docker Compose](https://docs.docker.com/guides/frameworks/laravel/prerequisites/) [2](https://docs.docker.com/guides/frameworks/laravel/production-setup/) [Laravel Production Setup with Docker Compose](https://docs.docker.com/guides/frameworks/laravel/production-setup/) [3](https://docs.docker.com/guides/frameworks/laravel/development-setup/) [Laravel Development Setup with Docker Compose](https://docs.docker.com/guides/frameworks/laravel/development-setup/) [4](https://docs.docker.com/guides/frameworks/laravel/common-questions/) [Common Questions on Using Laravel with Docker](https://docs.docker.com/guides/frameworks/laravel/common-questions/) Resources: * [Laravel](https://laravel.com/) * [Docker Compose](https://docs.docker.com/compose/) * [Use Compose in production](https://docs.docker.com/compose/how-tos/production/) * [Repository with examples](https://github.com/dockersamples/laravel-docker-examples) [« Back to all guides](https://docs.docker.com/guides/) Prerequisites for Setting Up Laravel with Docker Compose ======================================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Before you begin setting up Laravel with Docker Compose, make sure you meet the following prerequisites: [Docker and Docker Compose](https://docs.docker.com/guides/frameworks/laravel/prerequisites/#docker-and-docker-compose) ------------------------------------------------------------------------------------------------------------------------ You need Docker and Docker Compose installed on your system. Docker allows you to containerize applications, and Docker Compose helps you manage multi-container applications. * Docker: Make sure Docker is installed and running on your machine. Refer to the [Docker installation guide](https://docs.docker.com/get-docker/) to install Docker. * Docker Compose: Docker Compose is included with Docker Desktop, but you can also follow the [Docker Compose installation guide](https://docs.docker.com/compose/install/) if needed. [Basic understanding of Docker and containers](https://docs.docker.com/guides/frameworks/laravel/prerequisites/#basic-understanding-of-docker-and-containers) -------------------------------------------------------------------------------------------------------------------------------------------------------------- A fundamental understanding of Docker and how containers work will be helpful. If you're new to Docker, consider reviewing the [Docker Overview](https://docs.docker.com/get-started/overview/) to familiarize yourself with containerization concepts. [Basic knowledge of Laravel](https://docs.docker.com/guides/frameworks/laravel/prerequisites/#basic-knowledge-of-laravel) -------------------------------------------------------------------------------------------------------------------------- This guide assumes you have a basic understanding of Laravel and PHP. Familiarity with Laravel’s command-line tools, such as [Artisan](https://laravel.com/docs/12.x/artisan) , and its project structure is important for following the instructions. * Laravel CLI: You should be comfortable using Laravel’s command-line tool (`artisan`). * Laravel Project Structure: Familiarize yourself with Laravel’s folder structure (`app`, `config`, `routes`, `tests`, etc.). [Laravel Production Setup with Docker Compose »](https://docs.docker.com/guides/frameworks/laravel/production-setup/) --- # Why Testcontainers Cloud? | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Mastering Testcontainers Cloud by Docker: streamlining integration testing with containers](https://docs.docker.com/guides/testcontainers-cloud/) Automate, scale, and optimize testing workflows with Testcontainers Cloud Product demo 12 minutes [1](https://docs.docker.com/guides/testcontainers-cloud/why/) [Why Testcontainers Cloud?](https://docs.docker.com/guides/testcontainers-cloud/why/) [2](https://docs.docker.com/guides/testcontainers-cloud/demo-local/) [Setting up Testcontainers Cloud by Docker](https://docs.docker.com/guides/testcontainers-cloud/demo-local/) [3](https://docs.docker.com/guides/testcontainers-cloud/demo-ci/) [Configuring Testcontainers Cloud in the CI Pipeline](https://docs.docker.com/guides/testcontainers-cloud/demo-ci/) [4](https://docs.docker.com/guides/testcontainers-cloud/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/testcontainers-cloud/common-questions/) Resources: * [Testcontainers Guides](https://testcontainers.com/guides) * [Testcontainers Best Practices](https://www.docker.com/blog/testcontainers-best-practices/) * [Simple local development with Testcontainers Desktop](https://testcontainers.com/guides/simple-local-development-with-testcontainers-desktop/) * [Streamlining Local Development with Dev Containers and Testcontainers Cloud](https://www.docker.com/blog/streamlining-local-development-with-dev-containers-and-testcontainers-cloud/) * [Running Testcontainers Tests Using GitHub Actions and Testcontainers Cloud](https://www.docker.com/blog/running-testcontainers-tests-using-github-actions/) * [Testcontainers Cloud on the Docker Blog](https://www.docker.com/search/?_sf_s=testcontainers%20cloud) [« Back to all guides](https://docs.docker.com/guides/) Why Testcontainers Cloud? ========================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * Testcontainers Cloud is a powerful cloud-based solution designed to optimize integration testing with Testcontainers by offloading container management to the cloud. It helps developers and teams overcome the limitations of traditional local and CI-based testing, ensuring consistent environments, faster test execution, and scalable workflows. Whether you're new to Testcontainers or looking to enhance your existing setup, Testcontainers Cloud offers a seamless way to manage containerized tests, improving efficiency and reliability in your development pipeline. Testcontainers Cloud provides several benefits: * **Offloading to the Cloud:** Frees up local resources by shifting container management to the cloud, keeping your laptop responsive. * **Consistent Testing Environments:** Ensures that tests run in isolated, reliable environments, reducing inconsistencies across platforms from Dev to CI. * **Scalability:** Allows running large numbers of containers simultaneously without being limited by local or CI resources. * **Faster CI/CD Pipelines:** Reduces configuration bottlenecks and speeds up build times by offloading containers to multiple on-demand cloud workers with the Turbo-mode feature. Testcontainers Cloud streamlines integration testing by offloading container management to the cloud, ensuring consistent environments and faster test execution resulting in reduced resource strain, making it an essential tool for improving the stability of your Testcontainers-based workflows. [Setting up Testcontainers Cloud by Docker »](https://docs.docker.com/guides/testcontainers-cloud/demo-local/) --- # Why Docker Build Cloud? | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Docker Build Cloud: Reclaim your time with fast, multi-architecture builds](https://docs.docker.com/guides/docker-build-cloud/) Build applications up to 39x faster using cloud-based resources, shared team cache, and native multi-architecture support. Product demo 10 minutes [1](https://docs.docker.com/guides/docker-build-cloud/why/) [Why Docker Build Cloud?](https://docs.docker.com/guides/docker-build-cloud/why/) [2](https://docs.docker.com/guides/docker-build-cloud/dev/) [Demo: set up and use Docker Build Cloud in development](https://docs.docker.com/guides/docker-build-cloud/dev/) [3](https://docs.docker.com/guides/docker-build-cloud/ci/) [Demo: Using Docker Build Cloud in CI](https://docs.docker.com/guides/docker-build-cloud/ci/) [4](https://docs.docker.com/guides/docker-build-cloud/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/docker-build-cloud/common-questions/) Resources: * [Product page](https://www.docker.com/products/build-cloud/) * [Docker Build Cloud overview](https://docs.docker.com/build-cloud/) * [Subscriptions and features](https://www.docker.com/pricing?ref=Docs&refAction=DocsGuidesBuildCloud) * [Using Docker Build Cloud](https://docs.docker.com/build-cloud/usage/) [« Back to all guides](https://docs.docker.com/guides/) Why Docker Build Cloud? ======================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * Docker Build Cloud is a service that lets you build container images faster, both locally and in CI. Builds run on cloud infrastructure optimally dimensioned for your workloads, with no configuration required. The service uses a remote build cache, ensuring fast builds anywhere and for all team members. Docker Build Cloud provides several benefits over local builds: * Improved build speed * Shared build cache * Native multi-platform builds There’s no need to worry about managing builders or infrastructure — simply connect to your builders and start building. Each cloud builder provisioned to an organization is completely isolated to a single Amazon EC2 instance, with a dedicated EBS volume for build cache and encryption in transit. That means there are no shared processes or data between cloud builders. [Demo: set up and use Docker Build Cloud in development »](https://docs.docker.com/guides/docker-build-cloud/dev/) --- # Set Up ROS 2 workspace | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Introduction to ROS 2 Development with Docker](https://docs.docker.com/guides/ros2/) This guide details how to containerize ROS 2 applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg "Python") Python Frameworks 30 minutes [1](https://docs.docker.com/guides/ros2/run-ros2/) [Run ROS 2](https://docs.docker.com/guides/ros2/run-ros2/) [2](https://docs.docker.com/guides/ros2/develop/) [Set Up ROS 2 workspace](https://docs.docker.com/guides/ros2/develop/) [3](https://docs.docker.com/guides/ros2/turtlesim-example/) [Turtlesim example](https://docs.docker.com/guides/ros2/turtlesim-example/) [« Back to all guides](https://docs.docker.com/guides/) Build and develop a ROS 2 workspace =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Overview](https://docs.docker.com/guides/ros2/develop/#overview) ------------------------------------------------------------------ In this section, you will set up a ROS 2 workspace using Docker and development containers, review the workspace layout, open the workspace in Visual Studio Code, and edit and build ROS 2 projects inside the container. * * * [Get the sample ROS 2 workspace](https://docs.docker.com/guides/ros2/develop/#get-the-sample-ros-2-workspace) -------------------------------------------------------------------------------------------------------------- A consistent workspace simplifies managing ROS 2 projects and build artifacts across different distributions. 1. Open a terminal and clone the sample workspace repository: $ git clone https://github.com/shakirth-anisha/docker-ros2-workspace.git $ cd docker-ros2-workspace Moving forward, Linux users can use the `ws_linux` folder, and macOS users can use `ws_mac`. 2. Verify the workspace structure: ws_linux/ ├── compose.yml ├── Dockerfile └── src/ ├── package1/ └── package2/ ws_mac/ ├── compose.yml ├── Dockerfile └── src/ ├── package1/ └── package2/ 3. Explore the workspace layout * `compose.yml` : Defines how Docker Compose builds and runs the ROS 2 container, including mounts, environment variables, and networking settings. * `Dockerfile` : Builds the ROS 2 development image. It uses an official ROS 2 base image, creates a non-root development user, and installs required system and ROS 2 dependencies. * `src` : Contains all ROS 2 packages. This directory is mounted into the container as the active workspace. [Open and build the container](https://docs.docker.com/guides/ros2/develop/#open-and-build-the-container) ---------------------------------------------------------------------------------------------------------- 1. Execute the following commands to build and start the container: For Linux: $ cd ws_linux $ docker compose up -d $ docker compose exec ros2 /bin/bash For macOS: $ cd ws_mac $ docker compose up -d $ docker compose exec ros2 /bin/bash This command builds the Docker image defined in your `Dockerfile` and starts the container in the background. > Note > > Building the image may take several minutes during the first run as the CLI pulls the base ROS 2 image and installs required dependencies. Subsequent starts will be significantly faster. 2. Once the container is running, execute commands inside it using `exec`: $ docker compose exec ros2 /bin/bash 3. Inside the container terminal, verify the environment: $ echo $ROS_VERSION $ which colcon All commands should execute successfully inside the container. [Switch ROS 2 distributions](https://docs.docker.com/guides/ros2/develop/#switch-ros-2-distributions) ------------------------------------------------------------------------------------------------------ Update the base image in your `Dockerfile`, changing from `humble` to another distribution like `rolling`, `jazzy`, or `iron`. [Summary](https://docs.docker.com/guides/ros2/develop/#summary) ---------------------------------------------------------------- In this section, you learned how to create a structured workspace, write a Dockerfile with development tools, and configure a Docker Compose setup. Your ROS 2 development environment is now ready with a consistent, reproducible setup across any machine. [Next steps](https://docs.docker.com/guides/ros2/develop/#next-steps) ---------------------------------------------------------------------- In the next section, you'll run a complete end-to-end example with Turtlesim. [Run a complete example with Turtlesim »](https://docs.docker.com/guides/ros2/turtlesim-example/) --- # Immediate setup & data persistence | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [PostgreSQL specific guide](https://docs.docker.com/guides/postgresql/) This guide explains how to containerize PostgreSQL databases using Docker. Databases 20 minutes [1](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/) [Immediate setup & data persistence](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/) [2](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/) [Advanced Configuration and Initialization](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/) [3](https://docs.docker.com/guides/postgresql/networking-and-connectivity/) [Networking and connectivity](https://docs.docker.com/guides/postgresql/networking-and-connectivity/) [4](https://docs.docker.com/guides/postgresql/companions-for-postgresql/) [Companions for PostgreSQL](https://docs.docker.com/guides/postgresql/companions-for-postgresql/) [« Back to all guides](https://docs.docker.com/guides/) Immediate setup & data persistence ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * This guide gets you from zero to a running PostgreSQL container in under five minutes, then explains how to keep your data safe across container restarts and removals. [Overview](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#overview) ----------------------------------------------------------------------------------------------------- Running PostgreSQL in Docker requires understanding one critical concept: containers are ephemeral, but your data shouldn't be. This guide covers: * Starting PostgreSQL with a single command * Understanding why containers lose data by default * Configuring volumes for persistent storage * Translating your setup to Docker Compose [Quick start (minimal viable container)](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#quick-start-minimal-viable-container) --------------------------------------------------------------------------------------------------------------------------------------------------------------- > Note > > [Docker Hardened Images (DHIs)](https://docs.docker.com/dhi/) > are minimal, secure, and production-ready container base and application images maintained by Docker. DHIs are recommended whenever it is possible for better security. They are designed to reduce vulnerabilities and simplify compliance, freely available to everyone with no subscription required, no usage restrictions, and no vendor lock-in. Run PostgreSQL immediately with this single command: Using DHIs Using DOIs You must authenticate to dhi.io before you can pull Docker Hardened Images. Run `docker login dhi.io` to authenticate. docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -d dhi.io/postgres:18 $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -d postgres:18 ### [Understanding the flags](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#understanding-the-flags) | Flag | Purpose | | --- | --- | | `--rm` | Automatically removes the container when it stops | | `--name postgres-dev` | Assigns a memorable name instead of a random string | | `-e POSTGRES_PASSWORD=...` | Sets the superuser password (required) | | `-p 5432:5432` | Maps host port 5432 to container port 5432 | | `-d` | Runs the container in the background (detached mode) | Verify the container is running: $ docker ps --filter name=postgres-dev CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES a1b2c3d4e5f6 postgres:18 "docker-entrypoint.s…" Up 2 seconds 0.0.0.0:5432->5432/tcp postgres-dev Connect using `psql` from inside the container: $ docker exec -it postgres-dev psql -U postgres psql (18.0) Type "help" for help. postgres=# You now have a working PostgreSQL instance. But there's a problem—stop this container and your data disappears. [The data persistence problem](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#the-data-persistence-problem) --------------------------------------------------------------------------------------------------------------------------------------------- Containers use an ephemeral filesystem. When a container is removed, everything inside it, including your database files, is deleted. Demonstrate this yourself: Using DHIs Using DOIs $ docker exec postgres-dev psql -U postgres -c "CREATE DATABASE testdb;" CREATE DATABASE $ docker exec postgres-dev psql -U postgres -c "\l" | grep testdb testdb | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | $ docker stop postgres-dev postgres-dev $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -d dhi.io/postgres:18 $ docker exec postgres-dev psql -U postgres -c "\l" | grep testdb (no output - database is gone) $ docker exec postgres-dev psql -U postgres -c "CREATE DATABASE testdb;" CREATE DATABASE $ docker exec postgres-dev psql -U postgres -c "\l" | grep testdb testdb | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | $ docker stop postgres-dev postgres-dev $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -d postgres:18 $ docker exec postgres-dev psql -U postgres -c "\l" | grep testdb (no output - database is gone) Your `testdb` database vanished because the new container started with a fresh filesystem. This is expected behavior—and exactly why volumes exist. [Named volumes](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#named-volumes) --------------------------------------------------------------------------------------------------------------- Named volumes are Docker-managed storage locations that persist independently of containers. Docker handles the filesystem location, permissions, and lifecycle. Create a container with a named volume: Using DHIs Using DOIs You must authenticate to dhi.io before you can pull Docker Hardened Images. Run `docker login dhi.io` to authenticate. $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -v postgres_data:/var/lib/postgresql \ -d dhi.io/postgres:18 $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -v postgres_data:/var/lib/postgresql \ -d postgres:18 The `-v postgres_data:/var/lib/postgresql` flag mounts a named volume called `postgres_data` to PostgreSQL's data directory. If the volume doesn't exist, Docker creates it automatically. > Note > > PostgreSQL 18+ stores data in a version-specific subdirectory under `/var/lib/postgresql`. Mounting at this level (rather than `/var/lib/postgresql/data`) allows for easier upgrades using `pg_upgrade --link`. ### [Verify persistence works](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#verify-persistence-works) To verify data persistence, repeat the previous test, but this time with the named volume attached in place. Using DHIs Using DOIs $ docker exec postgres-dev psql -U postgres -c "CREATE DATABASE testdb;" CREATE DATABASE $ docker stop postgres-dev postgres-dev $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -v postgres_data:/var/lib/postgresql \ -d dhi.io/postgres:18 $ docker exec postgres-dev psql -U postgres -c "\l" | grep testdb testdb | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | $ docker exec postgres-dev psql -U postgres -c "CREATE DATABASE testdb;" CREATE DATABASE $ docker stop postgres-dev postgres-dev $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -v postgres_data:/var/lib/postgresql \ -d postgres:18 $ docker exec postgres-dev psql -U postgres -c "\l" | grep testdb testdb | postgres | UTF8 | libc | en_US.utf8 | en_US.utf8 | | | If you see `testdb` in the output, persistence works: The database survived because the volume preserved the data directory. ### [Managing volumes](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#managing-volumes) List all volumes: $ docker volume ls --filter name=postgres_data DRIVER VOLUME NAME local postgres_data Inspect a volume to see its details: $ docker volume inspect postgres_data [\ {\ "CreatedAt": "2025-01-05T10:30:00Z",\ "Driver": "local",\ "Labels": null,\ "Mountpoint": "/var/lib/docker/volumes/postgres_data/_data",\ "Name": "postgres_data",\ "Options": null,\ "Scope": "local"\ }\ ] Remove an unused volume (warning: this deletes all data): $ docker volume rm postgres_data [Bind mounts (alternative)](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#bind-mounts-alternative) ------------------------------------------------------------------------------------------------------------------------------------- Bind mounts map a specific host directory to a container path. Unlike named volumes, you control exactly where data lives on the host filesystem. Create a directory on your host machine to store Postgres data. Using DHIs Using DOIs mkdir -p ~/postgres-data && sudo chown -R 999:999 ~/postgres-data Run Postgres using a bind mount. docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -v ~/postgres-data:/var/lib/postgresql \ -d dhi.io/postgres:18 $ mkdir -p ~/postgres-data Run Postgres using a bind mount. $ docker run --rm --name postgres-dev \ -e POSTGRES_PASSWORD=mysecretpassword \ -p 5432:5432 \ -v ~/postgres-data:/var/lib/postgresql \ -d postgres:18 ### [When to use bind mounts](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#when-to-use-bind-mounts) Bind mounts are useful when you need direct filesystem access to the data directory for backup scripts that read files directly, when integrating with host-level monitoring tools, or when specific permission requirements exist. For most development and production scenarios, named volumes are simpler and less error-prone. ### [Common bind mount issues](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#common-bind-mount-issues) Permission errors are the most frequent problem with bind mounts. PostgreSQL runs as user `postgres` (UID 999) inside the container. If your host directory has restrictive permissions, the container fails to start. Check logs if the container exits immediately: $ docker logs postgres-dev [Docker Compose configuration](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#docker-compose-configuration) --------------------------------------------------------------------------------------------------------------------------------------------- Docker Compose captures your entire configuration in a file, making setups reproducible and easier to manage as complexity grows. Create a `compose.yaml` file: services: db: image: postgres:18 container_name: postgres-dev environment: POSTGRES_PASSWORD: mysecretpassword ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql volumes: postgres_data: Start the database: $ docker compose up -d Stop and remove containers (volume persists): $ docker compose down Alternatively, you can stop, remove containers, and delete the volume: $ docker compose down -v This compose file becomes the foundation for adding initialization scripts, performance tuning, and companion services covered in subsequent guides. ### [Environment variables reference](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#environment-variables-reference) The official PostgreSQL image supports these environment variables: | Variable | Required | Description | | --- | --- | --- | | `POSTGRES_PASSWORD` | Yes | Superuser password | | `POSTGRES_USER` | No | Superuser name (default: `postgres`) | | `POSTGRES_DB` | No | Default database name (default: value of `POSTGRES_USER`) | [Next steps](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/#next-steps) --------------------------------------------------------------------------------------------------------- With persistent storage configured, you're ready to customize PostgreSQL further. The next chapter of the guide covers: * Automated schema creation with initialization scripts * Performance tuning for containerized workloads * Timezone and locale configuration [Advanced Configuration and Initialization »](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/) --- # Setting up roles and permissions in Docker | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Mastering user and access management](https://docs.docker.com/guides/admin-user-management/) Simplify user access while ensuring security and efficiency in Docker. Administration 20 minutes [1](https://docs.docker.com/guides/admin-user-management/setup/) [Setting up roles and permissions in Docker](https://docs.docker.com/guides/admin-user-management/setup/) [2](https://docs.docker.com/guides/admin-user-management/onboard/) [Onboarding and managing roles and permissions in Docker](https://docs.docker.com/guides/admin-user-management/onboard/) [3](https://docs.docker.com/guides/admin-user-management/audit-and-monitor/) [Monitoring and insights](https://docs.docker.com/guides/admin-user-management/audit-and-monitor/) Resources: * [Overview of Administration in Docker](https://docs.docker.com/admin/) * [Single sign-on](https://docs.docker.com/security/for-admins/single-sign-on/) * [Onboard your organization](https://docs.docker.com/admin/organization/onboard/) * [Roles and permissions](https://docs.docker.com/security/for-admins/roles-and-permissions/) * [Insights](https://docs.docker.com/admin/organization/insights/) * [Activity logs](https://docs.docker.com/admin/organization/activity-logs/) [« Back to all guides](https://docs.docker.com/guides/) Setting up roles and permissions in Docker ========================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * With the right configurations, you can ensure your developers have easy access to necessary resources while preventing unauthorized access. This page guides you through identifying Docker users so you can allocate subscription seats efficiently within your Docker organization, and assigning roles to align with your organization's structure. [Identify your Docker users and accounts](https://docs.docker.com/guides/admin-user-management/setup/#identify-your-docker-users-and-accounts) ----------------------------------------------------------------------------------------------------------------------------------------------- Before setting up roles and permissions, it's important to have a clear understanding of who in your organization requires Docker access. Focus on gathering a comprehensive view of active users, their roles within projects, and how they interact with Docker resources. This process can be supported by tools like device management software or manual assessments. Encourage all users to update their Docker accounts to use organizational email addresses, ensuring seamless integration with your subscription. For steps on how you can do this, see [step 1 of onboarding your organization](https://docs.docker.com/admin/organization/onboard/) . [Assign roles strategically](https://docs.docker.com/guides/admin-user-management/setup/#assign-roles-strategically) --------------------------------------------------------------------------------------------------------------------- When you invite members to join your Docker organization, you assign them a role. Docker's predefined roles offer flexibility for various organizational needs. Assigning roles effectively ensures a balance of accessibility and security. * Member: Non-administrative role. Members can view other members that are in the same organization. * Editor: Partial administrative access to the organization. Editors can create, edit, and delete repositories. They can also edit an existing team's access permissions. * Owner: Full organization administrative access. Owners can manage organization repositories, teams, members, settings, and billing. For more information, see [Roles and permissions](https://docs.docker.com/enterprise/security/roles-and-permissions/) . ### [Enhance with teams](https://docs.docker.com/guides/admin-user-management/setup/#enhance-with-teams) Teams in Docker provide a structured way to manage member access and they provide an additional level of permissions. They simplify permission management and enable consistent application of policies. * Organize users into teams aligned with projects, departments, or functional roles. This approach helps streamline resource allocation and ensures clarity in access control. * Assign permissions at the team level rather than individually. For instance, a development team might have "Read & Write" access to certain repositories, while a QA team has "Read-only" access. * As teams grow or responsibilities shift, you can easily update permissions or add new members, maintaining consistency without reconfiguring individual settings. For more information, see [Create and manage a team](https://docs.docker.com/admin/organization/manage-a-team/) . ### [Example scenarios](https://docs.docker.com/guides/admin-user-management/setup/#example-scenarios) * Development teams: Assign the member role to developers, granting access to the repositories needed for coding and testing. * Team leads: Assign the editor role to team leads for resource management and repository control within their teams. * Organizational oversight: Restrict the organization owner or company owner roles to a select few trusted individuals responsible for billing and security settings. ### [Best practices](https://docs.docker.com/guides/admin-user-management/setup/#best-practices) * Apply the principle of least privilege. Assign users only the minimum permissions necessary for their roles. * Conduct regular reviews of role assignments to ensure they align with evolving team structures and organizational responsibilities. [Onboarding and managing roles and permissions in Docker »](https://docs.docker.com/guides/admin-user-management/onboard/) --- # Run containers | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Go language-specific guide](https://docs.docker.com/guides/golang/) This guide teaches you how to containerize Go applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg "Go") Go 30 minutes [1](https://docs.docker.com/guides/golang/build-images/) [Build images](https://docs.docker.com/guides/golang/build-images/) [2](https://docs.docker.com/guides/golang/run-containers/) [Run containers](https://docs.docker.com/guides/golang/run-containers/) [3](https://docs.docker.com/guides/golang/develop/) [Develop your app](https://docs.docker.com/guides/golang/develop/) [4](https://docs.docker.com/guides/golang/run-tests/) [Run your tests](https://docs.docker.com/guides/golang/run-tests/) [5](https://docs.docker.com/guides/golang/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/golang/configure-ci-cd/) [6](https://docs.docker.com/guides/golang/deploy/) [Test your deployment](https://docs.docker.com/guides/golang/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Run your Go image as a container ================================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/golang/run-containers/#prerequisites) ------------------------------------------------------------------------------------- Work through the steps to containerize a Go application in [Build your Go image](https://docs.docker.com/guides/golang/build-images/) . [Overview](https://docs.docker.com/guides/golang/run-containers/#overview) --------------------------------------------------------------------------- In the previous module you created a `Dockerfile` for your example application and then you created your Docker image using the command `docker build`. Now that you have the image, you can run that image and see if your application is running correctly. A container is a normal operating system process except that this process is isolated and has its own file system, its own networking, and its own isolated process tree separate from the host. To run an image inside of a container, you use the `docker run` command. It requires one parameter and that's the image name. Start your image and make sure it's running correctly. Run the following command in your terminal. $ docker run docker-gs-ping ____ __ / __/___/ / ___ / _// __/ _ \/ _ \ /___/\__/_//_/\___/ v4.10.2 High performance, minimalist Go web framework https://echo.labstack.com ____________________________________O/_______ O\ ⇨ http server started on [::]:8080 When you run this command, you’ll notice that you weren't returned to the command prompt. This is because your application is a REST server and will run in a loop waiting for incoming requests without returning control back to the OS until you stop the container. Make a GET request to the server using the curl command. $ curl http://localhost:8080/ curl: (7) Failed to connect to localhost port 8080: Connection refused Your curl command failed because the connection to your server was refused. Meaning that you weren't able to connect to localhost on port 8080. This is expected because your container is running in isolation which includes networking. Stop the container and restart with port 8080 published on your local network. To stop the container, press ctrl-c. This will return you to the terminal prompt. To publish a port for your container, you’ll use the `--publish` flag (`-p` for short) on the `docker run` command. The format of the `--publish` command is `[host_port]:[container_port]`. So if you wanted to expose port `8080` inside the container to port `3000` outside the container, you would pass `3000:8080` to the `--publish` flag. Start the container and expose port `8080` to port `8080` on the host. $ docker run --publish 8080:8080 docker-gs-ping Now, rerun the curl command. $ curl http://localhost:8080/ Hello, Docker! <3 Success! You were able to connect to the application running inside of your container on port 8080. Switch back to the terminal where your container is running and you should see the `GET` request logged to the console. Press `ctrl-c` to stop the container. [Run in detached mode](https://docs.docker.com/guides/golang/run-containers/#run-in-detached-mode) --------------------------------------------------------------------------------------------------- This is great so far, but your sample application is a web server and you shouldn't have to have your terminal connected to the container. Docker can run your container in detached mode in the background. To do this, you can use the `--detach` or `-d` for short. Docker will start your container the same as before but this time will detach from the container and return you to the terminal prompt. $ docker run -d -p 8080:8080 docker-gs-ping d75e61fcad1e0c0eca69a3f767be6ba28a66625ce4dc42201a8a323e8313c14e Docker started your container in the background and printed the container ID on the terminal. Again, make sure that your container is running. Run the same `curl` command: $ curl http://localhost:8080/ Hello, Docker! <3 [List containers](https://docs.docker.com/guides/golang/run-containers/#list-containers) ----------------------------------------------------------------------------------------- Since you ran your container in the background, how do you know if your container is running or what other containers are running on your machine? Well, to see a list of containers running on your machine, run `docker ps`. This is similar to how the ps command is used to see a list of processes on a Linux machine. $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d75e61fcad1e docker-gs-ping "/docker-gs-ping" 41 seconds ago Up 40 seconds 0.0.0.0:8080->8080/tcp inspiring_ishizaka The `ps` command tells you a bunch of stuff about your running containers. You can see the container ID, the image running inside the container, the command that was used to start the container, when it was created, the status, ports that are exposed, and the names of the container. You are probably wondering where the name of your container is coming from. Since you didn’t provide a name for the container when you started it, Docker generated a random name. You'll fix this in a minute but first you need to stop the container. To stop the container, run the `docker stop` command, passing the container's name or ID. $ docker stop inspiring_ishizaka inspiring_ishizaka Now rerun the `docker ps` command to see a list of running containers. $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [Stop, start, and name containers](https://docs.docker.com/guides/golang/run-containers/#stop-start-and-name-containers) ------------------------------------------------------------------------------------------------------------------------- Docker containers can be started, stopped and restarted. When you stop a container, it's not removed but the status is changed to stopped and the process inside of the container is stopped. When you ran the `docker ps` command, the default output is to only show running containers. If you pass the `--all` or `-a` for short, you will see all containers on your system, including stopped containers and running containers. $ docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d75e61fcad1e docker-gs-ping "/docker-gs-ping" About a minute ago Exited (2) 23 seconds ago inspiring_ishizaka f65dbbb9a548 docker-gs-ping "/docker-gs-ping" 3 minutes ago Exited (2) 2 minutes ago wizardly_joliot aade1bf3d330 docker-gs-ping "/docker-gs-ping" 3 minutes ago Exited (2) 3 minutes ago magical_carson 52d5ce3c15f0 docker-gs-ping "/docker-gs-ping" 9 minutes ago Exited (2) 3 minutes ago gifted_mestorf If you’ve been following along, you should see several containers listed. These are containers that you started and stopped but haven't removed yet. Restart the container that you have just stopped. Locate the name of the container and replace the name of the container in the following `restart` command: $ docker restart inspiring_ishizaka Now, list all the containers again using the `ps` command: $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d75e61fcad1e docker-gs-ping "/docker-gs-ping" 2 minutes ago Up 5 seconds 0.0.0.0:8080->8080/tcp inspiring_ishizaka f65dbbb9a548 docker-gs-ping "/docker-gs-ping" 4 minutes ago Exited (2) 2 minutes ago wizardly_joliot aade1bf3d330 docker-gs-ping "/docker-gs-ping" 4 minutes ago Exited (2) 4 minutes ago magical_carson 52d5ce3c15f0 docker-gs-ping "/docker-gs-ping" 10 minutes ago Exited (2) 4 minutes ago gifted_mestorf Notice that the container you just restarted has been started in detached mode and has port `8080` exposed. Also, note that the status of the container is `Up X seconds`. When you restart a container, it will be started with the same flags or commands that it was originally started with. Stop and remove all of your containers and take a look at fixing the random naming issue. Stop the container you just started. Find the name of your running container and replace the name in the following command with the name of the container on your system: $ docker stop inspiring_ishizaka inspiring_ishizaka Now that all of your containers are stopped, remove them. When a container is removed, it's no longer running nor is it in the stopped state. Instead, the process inside the container is terminated and the metadata for the container is removed. To remove a container, run the `docker rm` command passing the container name. You can pass multiple container names to the command in one command. Again, make sure you replace the containers names in the following command with the container names from your system: $ docker rm inspiring_ishizaka wizardly_joliot magical_carson gifted_mestorf inspiring_ishizaka wizardly_joliot magical_carson gifted_mestorf Run the `docker ps --all` command again to verify that all containers are gone. Now, address the pesky random name issue. Standard practice is to name your containers for the simple reason that it's easier to identify what's running in the container and what application or service it's associated with. Just like good naming conventions for variables in your code makes it simpler to read. So goes naming your containers. To name a container, you must pass the `--name` flag to the `run` command: $ docker run -d -p 8080:8080 --name rest-server docker-gs-ping 3bbc6a3102ea368c8b966e1878a5ea9b1fc61187afaac1276c41db22e4b7f48f $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3bbc6a3102ea docker-gs-ping "/docker-gs-ping" 25 seconds ago Up 24 seconds 0.0.0.0:8080->8080/tcp rest-server Now, you can easily identify your container based on the name. [Next steps](https://docs.docker.com/guides/golang/run-containers/#next-steps) ------------------------------------------------------------------------------- In this module, you learned how to run containers and publish ports. You also learned to manage the lifecycle of containers. You then learned the importance of naming your containers so that they're more easily identifiable. In the next module, you’ll learn how to run a database in a container and connect it to your application. [Use containers for Go development »](https://docs.docker.com/guides/golang/develop/) --- # Run containers | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Rust language-specific guide](https://docs.docker.com/guides/rust/) This guide covers how to containerize Rust applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/rust/rust-original.svg "Rust") Rust Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/rust/build-images/) [Build images](https://docs.docker.com/guides/rust/build-images/) [2](https://docs.docker.com/guides/rust/run-containers/) [Run containers](https://docs.docker.com/guides/rust/run-containers/) [3](https://docs.docker.com/guides/rust/develop/) [Develop your app](https://docs.docker.com/guides/rust/develop/) [4](https://docs.docker.com/guides/rust/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/rust/configure-ci-cd/) [5](https://docs.docker.com/guides/rust/deploy/) [Test your deployment](https://docs.docker.com/guides/rust/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Run your Rust image as a container ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisite](https://docs.docker.com/guides/rust/run-containers/#prerequisite) --------------------------------------------------------------------------------- You have completed [Build your Rust image](https://docs.docker.com/guides/rust/build-images/) and you have built an image. [Overview](https://docs.docker.com/guides/rust/run-containers/#overview) ------------------------------------------------------------------------- A container is a normal operating system process except that Docker isolates this process so that it has its own file system, its own networking, and its own isolated process tree separate from the host. To run an image inside of a container, you use the `docker run` command. The `docker run` command requires one parameter which is the name of the image. [Run an image](https://docs.docker.com/guides/rust/run-containers/#run-an-image) --------------------------------------------------------------------------------- Use `docker run` to run the image you built in [Build your Rust image](https://docs.docker.com/guides/rust/build-images/) . $ docker run docker-rust-image-dhi After running this command, you’ll notice that you weren't returned to the command prompt. This is because your application is a server that runs in a loop waiting for incoming requests without returning control back to the OS until you stop the container. Open a new terminal then make a request to the server using the `curl` command. $ curl http://localhost:8000 You should see output like the following. curl: (7) Failed to connect to localhost port 8000 after 2236 ms: Couldn't connect to server As you can see, your `curl` command failed. This means you weren't able to connect to the localhost on port 8000. This is normal because your container is running in isolation which includes networking. Stop the container and restart with port 8000 published on your local network. To stop the container, press ctrl-c. This will return you to the terminal prompt. To publish a port for your container, you’ll use the `--publish` flag (`-p` for short) on the `docker run` command. The format of the `--publish` command is `[host port]:[container port]`. So, if you wanted to expose port 8000 inside the container to port 3001 outside the container, you would pass `3001:8000` to the `--publish` flag. You didn't specify a port when running the application in the container and the default is 8000. If you want your previous request going to port 8000 to work, you can map the host's port 3001 to the container's port 8000: $ docker run --publish 3001:8000 docker-rust-image-dhi Now, rerun the curl command. Remember to open a new terminal. $ curl http://localhost:3001 You should see output like the following. Hello, Docker! Success! You were able to connect to the application running inside of your container on port 8000. Switch back to the terminal where your container is running and stop it. Press ctrl-c to stop the container. [Run in detached mode](https://docs.docker.com/guides/rust/run-containers/#run-in-detached-mode) ------------------------------------------------------------------------------------------------- This is great so far, but your sample application is a web server and you don't have to be connected to the container. Docker can run your container in detached mode or in the background. To do this, you can use the `--detach` or `-d` for short. Docker starts your container the same as before but this time will "detach" from the container and return you to the terminal prompt. $ docker run -d -p 3001:8000 docker-rust-image-dhi 3e4830e7f01304811d97dd3469d47a0c7a916a8b6c28ce0ef19c6f689a521144 Docker started your container in the background and printed the Container ID on the terminal. Again, make sure that your container is running properly. Run the curl command again. $ curl http://localhost:3001 You should see output like the following. Hello, Docker! [List containers](https://docs.docker.com/guides/rust/run-containers/#list-containers) --------------------------------------------------------------------------------------- Since you ran your container in the background, how do you know if your container is running or what other containers are running on your machine? Well, to see a list of containers running on your machine, run `docker ps`. This is similar to how you use the ps command in Linux to see a list of processes. You should see output like the following. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3e4830e7f013 docker-rust-image-dhi "/server" 23 seconds ago Up 22 seconds 0.0.0.0:3001->8000/tcp, [::]:3001->8000/tcp youthful_lamport The `docker ps` command provides a bunch of information about your running containers. You can see the container ID, the image running inside the container, the command that was used to start the container, when it was created, the status, ports that were exposed, and the name of the container. You are probably wondering where the name of your container is coming from. Since you didn’t provide a name for the container when you started it, Docker generated a random name. You’ll fix this in a minute, but first you need to stop the container. To stop the container, run the `docker stop` command which does just that, stops the container. You need to pass the name of the container or you can use the container ID. $ docker stop youthful_lamport youthful_lamport Now, rerun the `docker ps` command to see a list of running containers. $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES [Stop, start, and name containers](https://docs.docker.com/guides/rust/run-containers/#stop-start-and-name-containers) ----------------------------------------------------------------------------------------------------------------------- You can start, stop, and restart Docker containers. When you stop a container, it's not removed, but the status is changed to stopped and the process inside the container is stopped. When you ran the `docker ps` command in the previous module, the default output only shows running containers. When you pass the `--all` or `-a` for short, you see all containers on your machine, irrespective of their start or stop status. $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3e4830e7f013 docker-rust-image-dhi "/server" About a minute ago Exited (0) 28 seconds ago youthful_lamport 60009b7eaf40 docker-rust-image-dhi "/server" 2 minutes ago Exited (0) About a minute ago sharp_noyce 152e1d7d9eea docker-rust-image-dhi "/server ." 4 minutes ago Exited (0) 2 minutes ago magical_bhabha You should now see several containers listed. These are containers that you started and stopped but you haven't removed. Restart the container that you just stopped. Locate the name of the container you just stopped and replace the name of the container in following restart command. $ docker restart youthful_lamport Now list all the containers again using the `docker ps --all` command. $ docker ps --all CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 3e4830e7f013 docker-rust-image-dhi "/server" 3 minutes ago Up 7 seconds 0.0.0.0:3001->8000/tcp, [::]:3001->8000/tcp youthful_lamport 60009b7eaf40 docker-rust-image-dhi "/server" 4 minutes ago Exited (0) 3 minutes ago sharp_noyce 152e1d7d9eea docker-rust-image-dhi "/server ." 5 minutes ago Exited (0) 4 minutes ago magical_bhabha Notice that the container you just restarted has been started in detached mode. Also, observe the status of the container is "Up X seconds". When you restart a container, it starts with the same flags or commands that it was originally started with. Now, stop and remove all of your containers and take a look at fixing the random naming issue. Stop the container you just started. Find the name of your running container and replace the name in the following command with the name of the container on your system. $ docker stop youthful_lamport youthful_lamport Now that you have stopped all of your containers, remove them. When you remove a container, it's no longer running, nor is it in the stopped status, but the process inside the container has been stopped and the metadata for the container has been removed. To remove a container, run the `docker rm` command with the container name. You can pass multiple container names to the command using a single command. Again, replace the container names in the following command with the container names from your system. $ docker rm youthful_lamport friendly_montalcini tender_bose youthful_lamport sharp_noyce magical_bhabha Run the `docker ps --all` command again to see that Docker removed all containers. Now, it's time to address the random naming issue. Standard practice is to name your containers for the simple reason that it's easier to identify what's running in the container and what application or service it's associated with. To name a container, you just need to pass the `--name` flag to the `docker run` command. $ docker run -d -p 3001:8000 --name docker-rust-container docker-rust-image-dhi 1aa5d46418a68705c81782a58456a4ccdb56a309cb5e6bd399478d01eaa5cdda $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 219b2e3c7c38 docker-rust-image-dhi "/server" 6 seconds ago Up 5 seconds 0.0.0.0:3001->8000/tcp, [::]:3001->8000/tcp docker-rust-container That’s better! You can now easily identify your container based on the name. [Summary](https://docs.docker.com/guides/rust/run-containers/#summary) ----------------------------------------------------------------------- In this section, you took a look at running containers. You also took a look at managing containers by starting, stopping, and restarting them. And finally, you looked at naming your containers so they are more easily identifiable. Related information: * [docker run CLI reference](https://docs.docker.com/reference/cli/docker/container/run/) [Next steps](https://docs.docker.com/guides/rust/run-containers/#next-steps) ----------------------------------------------------------------------------- In the next section, you’ll learn how to run a database in a container and connect it to a Rust application. [Develop your Rust application »](https://docs.docker.com/guides/rust/develop/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Monitor a Golang application with Prometheus and Grafana](https://docs.docker.com/guides/go-prometheus-monitoring/) Learn how to containerize a Golang application and monitor it with Prometheus and Grafana. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg "Go") Go 45 minutes [1](https://docs.docker.com/guides/go-prometheus-monitoring/application/) [Understand the application](https://docs.docker.com/guides/go-prometheus-monitoring/application/) [2](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/) [Containerize your app](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/) [3](https://docs.docker.com/guides/go-prometheus-monitoring/compose/) [Connecting services with Docker Compose](https://docs.docker.com/guides/go-prometheus-monitoring/compose/) [4](https://docs.docker.com/guides/go-prometheus-monitoring/develop/) [Develop your app](https://docs.docker.com/guides/go-prometheus-monitoring/develop/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Golang application ================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Containerization helps you bundle the application and its dependencies into a single package called a container. This package can run on any platform without worrying about the environment. In this section, you will learn how to containerize a Golang application using Docker. To containerize a Golang application, you first need to create a Dockerfile. The Dockerfile contains instructions to build and run the application in a container. Also, when creating a Dockerfile, you can follow different sets of best practices to optimize the image size and make it more secure. [Creating a Dockerfile](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/#creating-a-dockerfile) --------------------------------------------------------------------------------------------------------------------- Create a new file named `Dockerfile` in the root directory of your Golang application. The Dockerfile contains instructions to build and run the application in a container. The following is a Dockerfile for a Golang application. You will also find this file in the `go-prometheus-monitoring` directory. # Use the official Golang image as the base FROM golang:1.24-alpine AS builder # Set environment variables ENV CGO_ENABLED=0 \ GOOS=linux \ GOARCH=amd64 # Set working directory inside the container WORKDIR /build # Copy go.mod and go.sum files for dependency installation COPY go.mod go.sum ./ # Download dependencies RUN go mod download # Copy the entire application source COPY . . # Build the Go binary RUN go build -o /app . # Final lightweight stage FROM alpine:3.21 AS final # Copy the compiled binary from the builder stage COPY --from=builder /app /bin/app # Expose the application's port EXPOSE 8000 # Run the application CMD ["bin/app"] [Understanding the Dockerfile](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/#understanding-the-dockerfile) ----------------------------------------------------------------------------------------------------------------------------------- The Dockerfile consists of two stages: 1. **Build stage**: This stage uses the official Golang image as the base and sets the necessary environment variables. It also sets the working directory inside the container, copies the `go.mod` and `go.sum` files for dependency installation, downloads the dependencies, copies the entire application source, and builds the Go binary. You use the `golang:1.24-alpine` image as the base image for the build stage. The `CGO_ENABLED=0` environment variable disables CGO, which is useful for building static binaries. You also set the `GOOS` and `GOARCH` environment variables to `linux` and `amd64`, respectively, to build the binary for the Linux platform. 2. **Final stage**: This stage uses the official Alpine image as the base and copies the compiled binary from the build stage. It also exposes the application's port and runs the application. You use the `alpine:3.21` image as the base image for the final stage. You copy the compiled binary from the build stage to the final image. You expose the application's port using the `EXPOSE` instruction and run the application using the `CMD` instruction. Apart from the multi-stage build, the Dockerfile also follows best practices such as using the official images, setting the working directory, and copying only the necessary files to the final image. You can further optimize the Dockerfile by other best practices. [Build the Docker image and run the application](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/#build-the-docker-image-and-run-the-application) ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- One you have the Dockerfile, you can build the Docker image and run the application in a container. To build the Docker image, run the following command in the terminal: $ docker build -t go-api:latest . After building the image, you can run the application in a container using the following command: $ docker run -p 8000:8000 go-api:latest The application will start running inside the container, and you can access it at `http://localhost:8000`. You can also check the running containers using the `docker ps` command. $ docker ps [Summary](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/#summary) ----------------------------------------------------------------------------------------- In this section, you learned how to containerize a Golang application using a Dockerfile. You created a multi-stage Dockerfile to build and run the application in a container. You also learned about best practices to optimize the Docker image size and make it more secure. Related information: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [.dockerignore file](https://docs.docker.com/reference/dockerfile/#dockerignore-file) [Next steps](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/#next-steps) ----------------------------------------------------------------------------------------------- In the next section, you will learn how to use Docker Compose to connect and run multiple services together to monitor a Golang application with Prometheus and Grafana. [Connecting services with Docker Compose »](https://docs.docker.com/guides/go-prometheus-monitoring/compose/) --- # Customize workflow | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [How to build an AI-powered code quality workflow with SonarQube and E2B](https://docs.docker.com/guides/github-sonarqube-sandbox/) Build AI-powered code quality workflows using E2B sandboxes with Docker's MCP catalog to automate GitHub and SonarQube integration. DevOps 40 minutes [1](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/) [Build workflow](https://docs.docker.com/guides/github-sonarqube-sandbox/workflow/) [2](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/) [Customize workflow](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/) [3](https://docs.docker.com/guides/github-sonarqube-sandbox/troubleshoot/) [Troubleshoot](https://docs.docker.com/guides/github-sonarqube-sandbox/troubleshoot/) Resources: * [E2B Documentation](https://e2b.dev/docs) * [Docker MCP Catalog](https://hub.docker.com/mcp) * [Sandboxes](https://docs.docker.com/ai/mcp-catalog-and-toolkit/sandboxes/) [« Back to all guides](https://docs.docker.com/guides/) Customize a code quality check workflow ======================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Now that you understand the basics of automating code quality workflows with GitHub and SonarQube in E2B sandboxes, you can customize the workflow for your needs. [Focus on specific quality issues](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/#focus-on-specific-quality-issues) ---------------------------------------------------------------------------------------------------------------------------------------- Modify the prompt to prioritize certain issue types: TypeScript Python const prompt = `Using SonarQube and GitHub MCP tools: Focus only on: - Security vulnerabilities (CRITICAL priority) - Bugs (HIGH priority) - Skip code smells for this iteration Analyze "${repoPath}" and fix the highest priority issues first.`; prompt = f"""Using SonarQube and GitHub MCP tools: Focus only on: - Security vulnerabilities (CRITICAL priority) - Bugs (HIGH priority) - Skip code smells for this iteration Analyze "{repo_path}" and fix the highest priority issues first.""" [Integrate with CI/CD](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/#integrate-with-cicd) --------------------------------------------------------------------------------------------------------------- Add this workflow to GitHub Actions to run automatically on pull requests: TypeScript Python name: Automated quality checks on: pull_request: types: [opened, synchronize] jobs: quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-node@v5 with: node-version: "24" - run: npm install - run: npx tsx 06-quality-gated-pr.ts env: E2B_API_KEY: ${{ secrets.E2B_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} GITHUB_OWNER: ${{ github.repository_owner }} GITHUB_REPO: ${{ github.event.repository.name }} SONARQUBE_ORG: your-org-key name: Automated quality checks on: pull_request: types: [opened, synchronize] jobs: quality: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - uses: actions/setup-python@v6 with: python-version: "3.14" - run: pip install e2b python-dotenv - run: python 06_quality_gated_pr.py env: E2B_API_KEY: ${{ secrets.E2B_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONARQUBE_TOKEN: ${{ secrets.SONARQUBE_TOKEN }} GITHUB_OWNER: ${{ github.repository_owner }} GITHUB_REPO: ${{ github.event.repository.name }} SONARQUBE_ORG: your-org-key [Filter by file patterns](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/#filter-by-file-patterns) ---------------------------------------------------------------------------------------------------------------------- Target specific parts of your codebase: TypeScript Python const prompt = `Analyze code quality but only consider: - Files in src/**/*.js - Exclude test files (*.test.js, *.spec.js) - Exclude build artifacts in dist/ Focus on production code only.`; prompt = """Analyze code quality but only consider: - Files in src/**/*.js - Exclude test files (*.test.js, *.spec.js) - Exclude build artifacts in dist/ Focus on production code only.""" [Set quality thresholds](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/#set-quality-thresholds) -------------------------------------------------------------------------------------------------------------------- Define when PRs should be created: TypeScript Python const prompt = `Quality gate thresholds: - Only create PR if: * Bug count decreases by at least 1 * No new security vulnerabilities introduced * Code coverage does not decrease * Technical debt reduces by at least 15 minutes If changes do not meet these thresholds, explain why and skip PR creation.`; prompt = """Quality gate thresholds: - Only create PR if: * Bug count decreases by at least 1 * No new security vulnerabilities introduced * Code coverage does not decrease * Technical debt reduces by at least 15 minutes If changes do not meet these thresholds, explain why and skip PR creation.""" [Next steps](https://docs.docker.com/guides/github-sonarqube-sandbox/customize/#next-steps) -------------------------------------------------------------------------------------------- Learn how to troubleshoot common issues. [Troubleshoot code quality workflows »](https://docs.docker.com/guides/github-sonarqube-sandbox/troubleshoot/) --- # Automate your builds with GitHub Actions | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Ruby on Rails language-specific guide](https://docs.docker.com/guides/ruby/) This guide explains how to containerize Ruby on Rails applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/ruby/ruby-original.svg "Ruby") Ruby Frameworks 20 minutes [1](https://docs.docker.com/guides/ruby/containerize/) [Containerize your app](https://docs.docker.com/guides/ruby/containerize/) [2](https://docs.docker.com/guides/ruby/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/ruby/configure-github-actions/) [3](https://docs.docker.com/guides/ruby/develop/) [Develop your app](https://docs.docker.com/guides/ruby/develop/) [4](https://docs.docker.com/guides/ruby/deploy/) [Test your deployment](https://docs.docker.com/guides/ruby/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Automate your builds with GitHub Actions ======================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/ruby/configure-github-actions/#prerequisites) --------------------------------------------------------------------------------------------- Complete all the previous sections of this guide, starting with [Containerize a Ruby on Rails application](https://docs.docker.com/guides/ruby/containerize/) . You must have a [GitHub](https://github.com/signup) account and a verified [Docker](https://hub.docker.com/signup) account to complete this section. If you didn't create a [GitHub repository](https://github.com/new) for your project yet, it is time to do it. After creating the repository, don't forget to [add a remote](https://docs.github.com/en/get-started/getting-started-with-git/managing-remote-repositories) and ensure you can commit and [push your code](https://docs.github.com/en/get-started/using-git/pushing-commits-to-a-remote-repository#about-git-push) to GitHub. 1. In your project's GitHub repository, open **Settings**, and go to **Secrets and variables** > **Actions**. 2. Under the **Variables** tab, create a new **Repository variable** named `DOCKER_USERNAME` and your Docker ID as a value. 3. Create a new [Personal Access Token (PAT)](https://docs.docker.com/security/access-tokens/#create-an-access-token) for Docker Hub. You can name this token `docker-tutorial`. Make sure access permissions include Read and Write. 4. Add the PAT as a **Repository secret** in your GitHub repository, with the name `DOCKERHUB_TOKEN`. [Overview](https://docs.docker.com/guides/ruby/configure-github-actions/#overview) ----------------------------------------------------------------------------------- GitHub Actions is a CI/CD (Continuous Integration and Continuous Deployment) automation tool built into GitHub. It allows you to define custom workflows for building, testing, and deploying your code when specific events occur (e.g., pushing code, creating a pull request, etc.). A workflow is a YAML-based automation script that defines a sequence of steps to be executed when triggered. Workflows are stored in the `.github/workflows/` directory of a repository. In this section, you'll learn how to set up and use GitHub Actions to build your Docker image as well as push it to Docker Hub. You will complete the following steps: 1. Define the GitHub Actions workflow. 2. Run the workflow. [1\. Define the GitHub Actions workflow](https://docs.docker.com/guides/ruby/configure-github-actions/#1-define-the-github-actions-workflow) --------------------------------------------------------------------------------------------------------------------------------------------- You can create a GitHub Actions workflow by creating a YAML file in the `.github/workflows/` directory of your repository. To do this use your favorite text editor or the GitHub web interface. The following steps show you how to create a workflow file using the GitHub web interface. If you prefer to use the GitHub web interface, follow these steps: 1. Go to your repository on GitHub and then select the **Actions** tab. 2. Select **set up a workflow yourself**. This takes you to a page for creating a new GitHub Actions workflow file in your repository. By default, the file is created under `.github/workflows/main.yml`, let's change it name to `build.yml`. If you prefer to use your text editor, create a new file named `build.yml` in the `.github/workflows/` directory of your repository. Add the following content to the file: name: Build and push Docker image on: push: branches: - main jobs: build_and_push: runs-on: ubuntu-latest steps: - name: Login to Docker Hub uses: docker/login-action@v4 with: username: ${{ vars.DOCKER_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Set up Docker Buildx uses: docker/setup-buildx-action@v4 - name: Build and push uses: docker/build-push-action@v7 with: push: true tags: ${{ vars.DOCKER_USERNAME }}/${{ github.event.repository.name }}:latest Each GitHub Actions workflow includes one or several jobs. Each job consists of steps. Each step can either run a set of commands or use already [existing actions](https://github.com/marketplace?type=actions) . The action above has three steps: 1. [**Login to Docker Hub**](https://github.com/docker/login-action) : Action logs in to Docker Hub using the Docker ID and Personal Access Token (PAT) you created earlier. 2. [**Set up Docker Buildx**](https://github.com/docker/setup-buildx-action) : Action sets up Docker [Buildx](https://github.com/docker/buildx) , a CLI plugin that extends the capabilities of the Docker CLI. 3. [**Build and push**](https://github.com/docker/build-push-action) : Action builds and pushes the Docker image to Docker Hub. The `tags` parameter specifies the image name and tag. The `latest` tag is used in this example. [2\. Run the workflow](https://docs.docker.com/guides/ruby/configure-github-actions/#2-run-the-workflow) --------------------------------------------------------------------------------------------------------- Commit the changes and push them to the `main` branch. This workflow is runs every time you push changes to the `main` branch. You can find more information about workflow triggers [in the GitHub documentation](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows) . Go to the **Actions** tab of you GitHub repository. It displays the workflow. Selecting the workflow shows you the breakdown of all the steps. When the workflow is complete, go to your [repositories on Docker Hub](https://hub.docker.com/repositories) . If you see the new repository in that list, it means the GitHub Actions workflow successfully pushed the image to Docker Hub. [Summary](https://docs.docker.com/guides/ruby/configure-github-actions/#summary) --------------------------------------------------------------------------------- In this section, you learned how to set up a GitHub Actions workflow for your Ruby on Rails application. Related information: * [Introduction to GitHub Actions](https://docs.docker.com/guides/gha/) * [Docker Build GitHub Actions](https://docs.docker.com/build/ci/github-actions/) * [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) [Next steps](https://docs.docker.com/guides/ruby/configure-github-actions/#next-steps) --------------------------------------------------------------------------------------- In the next section, you'll learn how you can develop your application using containers. [Use containers for Ruby on Rails development »](https://docs.docker.com/guides/ruby/develop/) --- # Demo: set up and use Docker Build Cloud in development | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Docker Build Cloud: Reclaim your time with fast, multi-architecture builds](https://docs.docker.com/guides/docker-build-cloud/) Build applications up to 39x faster using cloud-based resources, shared team cache, and native multi-architecture support. Product demo 10 minutes [1](https://docs.docker.com/guides/docker-build-cloud/why/) [Why Docker Build Cloud?](https://docs.docker.com/guides/docker-build-cloud/why/) [2](https://docs.docker.com/guides/docker-build-cloud/dev/) [Demo: set up and use Docker Build Cloud in development](https://docs.docker.com/guides/docker-build-cloud/dev/) [3](https://docs.docker.com/guides/docker-build-cloud/ci/) [Demo: Using Docker Build Cloud in CI](https://docs.docker.com/guides/docker-build-cloud/ci/) [4](https://docs.docker.com/guides/docker-build-cloud/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/docker-build-cloud/common-questions/) Resources: * [Product page](https://www.docker.com/products/build-cloud/) * [Docker Build Cloud overview](https://docs.docker.com/build-cloud/) * [Subscriptions and features](https://www.docker.com/pricing?ref=Docs&refAction=DocsGuidesBuildCloud) * [Using Docker Build Cloud](https://docs.docker.com/build-cloud/usage/) [« Back to all guides](https://docs.docker.com/guides/) Demo: set up and use Docker Build Cloud in development ====================================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * With Docker Build Cloud, you can easily shift the build workload from local machines to the cloud, helping you achieve faster build times, especially for multi-platform builds. In this demo, you'll see: * How to setup the builder locally * How to use Docker Build Cloud with Docker Compose * How the image cache speeds up builds for others on your team [Demo: Using Docker Build Cloud in CI »](https://docs.docker.com/guides/docker-build-cloud/ci/) --- # JDBC URL approach | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Replace H2 with a real database for testing](https://docs.docker.com/guides/testcontainers-java-replace-h2/) Replace your H2 in-memory test database with a real PostgreSQL instance using the Testcontainers special JDBC URL — a one-line change. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-java-replace-h2/problem-with-h2/) [The H2 problem](https://docs.docker.com/guides/testcontainers-java-replace-h2/problem-with-h2/) [2](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/) [JDBC URL approach](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/) [3](https://docs.docker.com/guides/testcontainers-java-replace-h2/junit-extension-approach/) [JUnit 5 extension](https://docs.docker.com/guides/testcontainers-java-replace-h2/junit-extension-approach/) [« Back to all guides](https://docs.docker.com/guides/) Replace H2 with the Testcontainers JDBC URL =========================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Replacing H2 with a real PostgreSQL database requires two test properties: @DataJpaTest @TestPropertySource(properties = { "spring.test.database.replace=none", "spring.datasource.url=jdbc:tc:postgresql:16-alpine:///db" }) class ProductRepositoryWithJdbcUrlTest { @Autowired ProductRepository productRepository; @Test @Sql("classpath:/sql/seed-data.sql") void shouldGetAllProducts() { List products = productRepository.findAll(); assertEquals(2, products.size()); } } That's it — two properties and your tests run against a real PostgreSQL database. [How the special JDBC URL works](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/#how-the-special-jdbc-url-works) -------------------------------------------------------------------------------------------------------------------------------------------------- A standard PostgreSQL JDBC URL looks like: jdbc:postgresql://localhost:5432/postgres The Testcontainers special JDBC URL inserts `tc:` after `jdbc:`: jdbc:tc:postgresql:///db The hostname, port, and database name are ignored — Testcontainers manages them automatically. You can specify the Docker image tag after the database name: jdbc:tc:postgresql:16-alpine:///db This creates a container from the `postgres:16-alpine` image. [Initialize the database with a script](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/#initialize-the-database-with-a-script) ---------------------------------------------------------------------------------------------------------------------------------------------------------------- Pass `TC_INITSCRIPT` to run an SQL script when the container starts: jdbc:tc:postgresql:16-alpine:///db?TC_INITSCRIPT=sql/init-db.sql Testcontainers runs the script automatically. For production applications, use a database migration tool like Flyway or Liquibase instead. The special JDBC URL also works for MySQL, MariaDB, PostGIS, YugabyteDB, CockroachDB, and other databases with Testcontainers JDBC support. [Testing JdbcTemplate-based repositories](https://docs.docker.com/guides/testcontainers-java-replace-h2/jdbc-url-approach/#testing-jdbctemplate-based-repositories) -------------------------------------------------------------------------------------------------------------------------------------------------------------------- The same approach works for `JdbcTemplate`\-based repositories. Use `@JdbcTest` instead of `@DataJpaTest`: @JdbcTest @TestPropertySource(properties = { "spring.test.database.replace=none", "spring.datasource.url=jdbc:tc:postgresql:16-alpine:///db?TC_INITSCRIPT=sql/init-db.sql" }) class JdbcProductRepositoryTest { @Autowired private JdbcTemplate jdbcTemplate; private JdbcProductRepository productRepository; @BeforeEach void setUp() { productRepository = new JdbcProductRepository(jdbcTemplate); } @Test @Sql("/sql/seed-data.sql") void shouldGetAllProducts() { List products = productRepository.getAllProducts(); assertEquals(2, products.size()); } } [Use the JUnit 5 extension for more control »](https://docs.docker.com/guides/testcontainers-java-replace-h2/junit-extension-approach/) --- # Execute commands | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Configuration of services running in a container](https://docs.docker.com/guides/testcontainers-java-service-configuration/) Learn how to initialize and configure Docker containers for testing by copying files into containers and executing commands inside them. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-java-service-configuration/copy-files/) [Copy files](https://docs.docker.com/guides/testcontainers-java-service-configuration/copy-files/) [2](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/) [Execute commands](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/) [« Back to all guides](https://docs.docker.com/guides/) Execute commands inside containers ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Some Docker containers provide CLI tools for performing actions. You can use `container.execInContainer(String...)` to run any available command inside a running container. [Example: Create an S3 bucket in LocalStack](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/#example-create-an-s3-bucket-in-localstack) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ The [LocalStack](https://localstack.cloud/) module emulates AWS services. To test S3 file uploads, create a bucket before running tests: package com.testcontainers.demo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; import java.io.IOException; import java.net.URI; import java.util.List; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.testcontainers.containers.localstack.LocalStackContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; import software.amazon.awssdk.auth.credentials.AwsBasicCredentials; import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider; import software.amazon.awssdk.regions.Region; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.Bucket; @Testcontainers class LocalStackTest { static final String bucketName = "mybucket"; @Container static LocalStackContainer localStack = new LocalStackContainer( DockerImageName.parse("localstack/localstack:3.4.0") ); @BeforeAll static void beforeAll() throws IOException, InterruptedException { localStack.execInContainer("awslocal", "s3", "mb", "s3://" + bucketName); org.testcontainers.containers.Container.ExecResult execResult = localStack.execInContainer("awslocal", "s3", "ls"); String stdout = execResult.getStdout(); int exitCode = execResult.getExitCode(); assertTrue(stdout.contains(bucketName)); assertEquals(0, exitCode); } @Test void shouldListBuckets() { URI s3Endpoint = localStack.getEndpointOverride(S3); StaticCredentialsProvider credentialsProvider = StaticCredentialsProvider.create( AwsBasicCredentials.create( localStack.getAccessKey(), localStack.getSecretKey() ) ); S3Client s3 = S3Client .builder() .endpointOverride(s3Endpoint) .credentialsProvider(credentialsProvider) .region(Region.of(localStack.getRegion())) .build(); List s3Buckets = s3 .listBuckets() .buckets() .stream() .map(Bucket::name) .toList(); assertTrue(s3Buckets.contains(bucketName)); } } The `execInContainer("awslocal", "s3", "mb", "s3://mybucket")` call runs the `awslocal` CLI tool (provided by the LocalStack image) to create an S3 bucket. You can capture the output and exit code from any command: Container.ExecResult execResult = localStack.execInContainer("awslocal", "s3", "ls"); String stdout = execResult.getStdout(); int exitCode = execResult.getExitCode(); > Note > > The `withCopyFileToContainer()` and `execInContainer()` methods are inherited from `GenericContainer`, so they're available for all Testcontainers modules. [Summary](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/#summary) --------------------------------------------------------------------------------------------------------------- * Use `withCopyFileToContainer()` to place initialization files inside containers before they start. * Use `execInContainer()` to run commands inside running containers for setup tasks like creating buckets, topics, or queues. [Further reading](https://docs.docker.com/guides/testcontainers-java-service-configuration/exec-in-container/#further-reading) ------------------------------------------------------------------------------------------------------------------------------- * [Getting started with Testcontainers for Java](https://docs.docker.com/guides/testcontainers-java-getting-started/) * [Testcontainers Postgres module](https://java.testcontainers.org/modules/databases/postgres/) * [Testcontainers LocalStack module](https://java.testcontainers.org/modules/localstack/) --- # Why Docker Scout? | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Securing your software supply chain with Docker Scout](https://docs.docker.com/guides/docker-scout/) Enhance container security by automating vulnerability detection and remediation. Product demo 20 minutes [1](https://docs.docker.com/guides/docker-scout/why/) [Why Docker Scout?](https://docs.docker.com/guides/docker-scout/why/) [2](https://docs.docker.com/guides/docker-scout/demo/) [Demo](https://docs.docker.com/guides/docker-scout/demo/) [3](https://docs.docker.com/guides/docker-scout/s3c/) [Software supply chain security](https://docs.docker.com/guides/docker-scout/s3c/) [4](https://docs.docker.com/guides/docker-scout/sbom/) [Software Bill of Materials](https://docs.docker.com/guides/docker-scout/sbom/) [5](https://docs.docker.com/guides/docker-scout/attestations/) [Attestations](https://docs.docker.com/guides/docker-scout/attestations/) [6](https://docs.docker.com/guides/docker-scout/remediation/) [Remediation](https://docs.docker.com/guides/docker-scout/remediation/) [7](https://docs.docker.com/guides/docker-scout/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/docker-scout/common-questions/) Resources: * [Docker Scout overview](https://docs.docker.com/scout/) * [Docker Scout quickstart](https://docs.docker.com/scout/quickstart/) * [Install Docker Scout](https://docs.docker.com/scout/install/) [« Back to all guides](https://docs.docker.com/guides/) Why Docker Scout? ================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * Organizations face significant challenges from data breaches, including financial losses, operational disruptions, and long-term damage to brand reputation and customer trust. Docker Scout addresses critical problems such as identifying insecure container images, preventing security breaches, and reducing the risk of operational downtime due to vulnerabilities. Docker Scout provides several benefits: * Secure and trusted content * A system of record for your Software Development Lifecycle (SDLC) * Continuous security posture improvement Docker Scout offers automated vulnerability detection and remediation, helping organizations identify and fix security issues in container images early in the development process. It also integrates with popular development tools like Docker Desktop and GitHub Actions, providing seamless security management and compliance checks within existing workflows. [Docker Scout demo »](https://docs.docker.com/guides/docker-scout/demo/) --- # Why Docker Compose? | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Defining and running multi-container applications with Docker Compose](https://docs.docker.com/guides/docker-compose/) Simplify the process of defining, configuring, and running multi-container Docker applications. Product demo 10 minutes [1](https://docs.docker.com/guides/docker-compose/why/) [Why Docker Compose?](https://docs.docker.com/guides/docker-compose/why/) [2](https://docs.docker.com/guides/docker-compose/setup/) [Demo: set up and use Docker Compose](https://docs.docker.com/guides/docker-compose/setup/) [3](https://docs.docker.com/guides/docker-compose/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/docker-compose/common-questions/) Resources: * [Overview of Docker Compose CLI](https://docs.docker.com/reference/cli/docker/compose/) * [Overview of Docker Compose](https://docs.docker.com/compose/) * [How Compose works](https://docs.docker.com/compose/intro/compose-application-model/) * [Using profiles with Compose](https://docs.docker.com/compose/how-tos/profiles/) * [Control startup and shutdown order with Compose](https://docs.docker.com/compose/how-tos/startup-order/) * [Compose Build Specification](https://docs.docker.com/compose/compose-file/build/) [« Back to all guides](https://docs.docker.com/guides/) Why Docker Compose? =================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * Docker Compose is an essential tool for defining and running multi-container Docker applications. Docker Compose simplifies the Docker experience, making it easier for developers to create, manage, and deploy applications by using YAML files to configure application services. Docker Compose provides several benefits: * Lets you define multi-container applications in a single YAML file. * Ensures consistent environments across development, testing, and production. * Manages the startup and linking of multiple containers effortlessly. * Streamlines development workflows and reduces setup time. * Ensures that each service runs in its own container, avoiding conflicts. [Demo: set up and use Docker Compose »](https://docs.docker.com/guides/docker-compose/setup/) --- # Advanced Configuration and Initialization | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [PostgreSQL specific guide](https://docs.docker.com/guides/postgresql/) This guide explains how to containerize PostgreSQL databases using Docker. Databases 20 minutes [1](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/) [Immediate setup & data persistence](https://docs.docker.com/guides/postgresql/immediate-setup-and-data-persistence/) [2](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/) [Advanced Configuration and Initialization](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/) [3](https://docs.docker.com/guides/postgresql/networking-and-connectivity/) [Networking and connectivity](https://docs.docker.com/guides/postgresql/networking-and-connectivity/) [4](https://docs.docker.com/guides/postgresql/companions-for-postgresql/) [Companions for PostgreSQL](https://docs.docker.com/guides/postgresql/companions-for-postgresql/) [« Back to all guides](https://docs.docker.com/guides/) Advanced Configuration and Initialization ========================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * With persistent storage configured in the previous section, you're ready to customize PostgreSQL for real-world use. This guide covers advanced configuration techniques for running PostgreSQL in Docker containers, including automated database initialization, performance tuning, and timezone configuration. [Overview](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#overview) ---------------------------------------------------------------------------------------------------------- While PostgreSQL containers can be started quickly with default settings, production environments require customized configurations. This guide explains how to: * Automate database, schema, and user creation during container startup * Tune PostgreSQL performance parameters for containerized workloads * Configure timezone and locale settings [Initialization scripts](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#initialization-scripts) -------------------------------------------------------------------------------------------------------------------------------------- The official PostgreSQL Docker image supports running initialization scripts automatically when the container starts for the first time. Any files placed in the `/docker-entrypoint-initdb.d/` directory are executed in alphabetical order. ### [How initialization works](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#how-initialization-works) When the container starts, it checks whether the PostgreSQL data directory is empty. If the directory already contains data, PostgreSQL starts immediately without running any initialization. If the directory is empty, the container runs `initdb` to create a new database cluster, then executes all scripts in `/docker-entrypoint-initdb.d/` in alphabetical order before starting PostgreSQL. ### [Supported file formats](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#supported-file-formats) | Format | Description | | --- | --- | | `.sql` | SQL commands executed directly | | `.sql.gz` | Gzip-compressed SQL files | | `.sh` | Shell scripts executed with bash | > Important > > Initialization scripts only run when the PostgreSQL data directory (`/var/lib/postgresql/data`) is empty. If you mount a volume containing existing data, initialization is skipped. This behavior prevents overwriting existing databases. [Mounting initialization scripts](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#mounting-initialization-scripts) -------------------------------------------------------------------------------------------------------------------------------------------------------- Use Docker Compose to mount initialization scripts into the container. First, create a project directory: $ mkdir -p postgres-project/init-db $ cd postgres-project Create a `compose.yaml` file: services: db: image: postgres:18 volumes: - ./init-db:/docker-entrypoint-initdb.d - postgres_data:/var/lib/postgresql environment: POSTGRES_PASSWORD: mysecretpassword volumes: postgres_data: All scripts in the `./init-db` directory execute when the container starts for the first time. This is great for bootstrapping databases. [Initialization script example](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#initialization-script-example) ---------------------------------------------------------------------------------------------------------------------------------------------------- Create a file named `init.sql` in your `init-db` directory: CREATE TABLE users ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, name VARCHAR(100) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); This script runs automatically when the container starts for the first time, creating your initial database schema. > Note > > Ensure initialization scripts have proper read permissions. If you encounter "Permission denied" errors, run `chmod 644 init-db/*.sql` to make the files readable by the container. [Performance tuning](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#performance-tuning) ------------------------------------------------------------------------------------------------------------------------------ Default PostgreSQL settings are conservative to work on systems with limited resources. For production workloads, you should tune these parameters based on your container's allocated resources. ### [Method 1: Custom configuration file](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#method-1-custom-configuration-file) For complete control, mount a custom `postgresql.conf` file. First, extract the default configuration: $ docker run -i --rm postgres:18 cat /usr/share/postgresql/postgresql.conf.sample > my-postgres.conf Edit `my-postgres.conf` with your desired settings, then mount it in your Compose file: services: db: image: postgres:18 volumes: - ./my-postgres.conf:/etc/postgresql/postgresql.conf - ./init-db:/docker-entrypoint-initdb.d - postgres_data:/var/lib/postgresql command: postgres -c config_file=/etc/postgresql/postgresql.conf environment: POSTGRES_PASSWORD: mysecretpassword volumes: postgres_data: [Key configuration parameters](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#key-configuration-parameters) -------------------------------------------------------------------------------------------------------------------------------------------------- The following tables list important `postgresql.conf` parameters for containerized PostgreSQL deployments. ### [Connection settings](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#connection-settings) | Parameter | Description | Default | | --- | --- | --- | | `listen_addresses` | IP addresses to listen on | `localhost` | | `port` | TCP port number | `5432` | | `max_connections` | Maximum concurrent connections | `100` | ### [Memory settings](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#memory-settings) | Parameter | Description | Recommended starting value | | --- | --- | --- | | `shared_buffers` | Shared memory for caching | 25% of container memory | | `work_mem` | Memory per query operation | 4MB - 64MB | | `maintenance_work_mem` | Memory for VACUUM, CREATE INDEX | 64MB - 256MB | | `effective_cache_size` | Planner's cache size estimate | 50-75% of container memory | #### [Docker memory limits](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#docker-memory-limits) When tuning memory parameters, set explicit memory limits on your container using `deploy.resources.limits.memory` in Compose or `--memory` with `docker run`. Without limits, PostgreSQL sees the host's total RAM and may allocate more than intended. For example, if your container should use 4GB maximum, set `shared_buffers` to approximately 1GB (25%). ### [I/O settings](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#io-settings) | Parameter | Description | Recommended starting value | | --- | --- | --- | | `effective_io_concurrency` | Concurrent disk I/O operations | `200` for SSDs, `2` for HDDs | ### [Timeout settings](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#timeout-settings) | Parameter | Description | Default | | --- | --- | --- | | `statement_timeout` | Max time for any statement | `0` (disabled) | | `lock_timeout` | Max time to wait for a lock | `0` (disabled) | | `deadlock_timeout` | Time before checking for deadlock | `1s` | | `transaction_timeout` | Max time for a transaction | `0` (disabled) | > Note > > Setting `shared_buffers` too high in a container can exceed kernel shared memory limits. Use no more than 25-30% of the container's memory limit. [Timezone and locale configuration](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#timezone-and-locale-configuration) ------------------------------------------------------------------------------------------------------------------------------------------------------------ Proper localization ensures timestamps and sorting behave correctly for your application's users. services: db: image: postgres:18 volumes: - postgres_data:/var/lib/postgresql - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro environment: POSTGRES_PASSWORD: mysecretpassword TZ: America/New_York volumes: postgres_data: Alternatively, set the timezone using a PostgreSQL command-line parameter: services: db: image: postgres:18 command: ["postgres", "-c", "timezone=America/New_York"] environment: POSTGRES_PASSWORD: mysecretpassword ### [Setting the locale](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#setting-the-locale) Specify locale settings during database initialization using the `POSTGRES_INITDB_ARGS` environment variable: services: db: image: postgres:18 volumes: - postgres_data:/var/lib/postgresql environment: POSTGRES_PASSWORD: mysecretpassword POSTGRES_INITDB_ARGS: "--encoding=UTF8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8" volumes: postgres_data: This affects collation (sorting) and character processing behavior. Changing this variable after database creation has no effect—it only applies during the first run when the data directory is initialized. [Connecting to the database](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#connecting-to-the-database) ---------------------------------------------------------------------------------------------------------------------------------------------- You can interact with PostgreSQL running in a container even without `psql` installed on your host machine. ### [Interactive shell](https://docs.docker.com/guides/postgresql/advanced-configuration-and-initialization/#interactive-shell) Open a `psql` session inside the container: $ docker exec -it postgres-container psql -U postgres Connect to a specific database: $ docker exec -it postgres-container psql -U postgres -d mydb [Networking and connectivity »](https://docs.docker.com/guides/postgresql/networking-and-connectivity/) --- # Understand the application | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Monitor a Golang application with Prometheus and Grafana](https://docs.docker.com/guides/go-prometheus-monitoring/) Learn how to containerize a Golang application and monitor it with Prometheus and Grafana. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg "Go") Go 45 minutes [1](https://docs.docker.com/guides/go-prometheus-monitoring/application/) [Understand the application](https://docs.docker.com/guides/go-prometheus-monitoring/application/) [2](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/) [Containerize your app](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/) [3](https://docs.docker.com/guides/go-prometheus-monitoring/compose/) [Connecting services with Docker Compose](https://docs.docker.com/guides/go-prometheus-monitoring/compose/) [4](https://docs.docker.com/guides/go-prometheus-monitoring/develop/) [Develop your app](https://docs.docker.com/guides/go-prometheus-monitoring/develop/) [« Back to all guides](https://docs.docker.com/guides/) Building the application ======================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/go-prometheus-monitoring/application/#prerequisites) ---------------------------------------------------------------------------------------------------- * You have a [Git client](https://git-scm.com/downloads) . The examples in this section use a command-line based Git client, but you can use any client. You will be creating a Golang server with some endpoints to simulate a real-world application. Then you will expose metrics from the server using Prometheus. [Getting the sample application](https://docs.docker.com/guides/go-prometheus-monitoring/application/#getting-the-sample-application) -------------------------------------------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/dockersamples/go-prometheus-monitoring.git Once you cloned you will see the following content structure inside `go-prometheus-monitoring` directory, go-prometheus-monitoring ├── CONTRIBUTING.md ├── Docker │ ├── grafana.yml │ └── prometheus.yml ├── dashboard.json ├── Dockerfile ├── LICENSE ├── README.md ├── compose.yaml ├── go.mod ├── go.sum └── main.go * **main.go** - The entry point of the application. * **go.mod and go.sum** - Go module files. * **Dockerfile** - Dockerfile used to build the app. * **Docker/** - Contains the Docker Compose configuration files for Grafana and Prometheus. * **compose.yaml** - Compose file to launch everything (Golang app, Prometheus, and Grafana). * **dashboard.json** - Grafana dashboard configuration file. * **Dockerfile** - Dockerfile used to build the Golang app. * **compose.yaml** - Docker Compose file to launch everything (Golang app, Prometheus, and Grafana). * Other files are for licensing and documentation purposes. [Understanding the application](https://docs.docker.com/guides/go-prometheus-monitoring/application/#understanding-the-application) ------------------------------------------------------------------------------------------------------------------------------------ The following is the complete logic of the application you will find in `main.go`. package main import ( "strconv" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) // Define metrics var ( HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "api_http_request_total", Help: "Total number of requests processed by the API", }, []string{"path", "status"}) HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "api_http_request_error_total", Help: "Total number of errors returned by the API", }, []string{"path", "status"}) ) // Custom registry (without default Go metrics) var customRegistry = prometheus.NewRegistry() // Register metrics with custom registry func init() { customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal) } func main() { router := gin.Default() // Register /metrics before middleware router.GET("/metrics", PrometheusHandler()) router.Use(RequestMetricsMiddleware()) router.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Up and running!", }) }) router.GET("/v1/users", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello from /v1/users", }) }) router.Run(":8000") } // Custom metrics handler with custom registry func PrometheusHandler() gin.HandlerFunc { h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{}) return func(c *gin.Context) { h.ServeHTTP(c.Writer, c.Request) } } // Middleware to record incoming requests metrics func RequestMetricsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { path := c.Request.URL.Path c.Next() status := c.Writer.Status() if status < 400 { HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc() } else { HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc() } } } In this part of the code, you have imported the required packages `gin`, `prometheus`, and `promhttp`. Then you have defined a couple of variables, `HttpRequestTotal` and `HttpRequestErrorTotal` are Prometheus counter metrics, and `customRegistry` is a custom registry that will be used to register these metrics. The name of the metric is a string that you can use to identify the metric. The help string is a string that will be shown when you query the `/metrics` endpoint to understand the metric. The reason you are using the custom registry is so avoid the default Go metrics that are registered by default by the Prometheus client. Then using the `init` function you are registering the metrics with the custom registry. import ( "strconv" "github.com/gin-gonic/gin" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/promhttp" ) // Define metrics var ( HttpRequestTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "api_http_request_total", Help: "Total number of requests processed by the API", }, []string{"path", "status"}) HttpRequestErrorTotal = prometheus.NewCounterVec(prometheus.CounterOpts{ Name: "api_http_request_error_total", Help: "Total number of errors returned by the API", }, []string{"path", "status"}) ) // Custom registry (without default Go metrics) var customRegistry = prometheus.NewRegistry() // Register metrics with custom registry func init() { customRegistry.MustRegister(HttpRequestTotal, HttpRequestErrorTotal) } In the `main` function, you have created a new instance of the `gin` framework and created three routes. You can see the health endpoint that is on path `/health` that will return a JSON with `{"message": "Up and running!"}` and the `/v1/users` endpoint that will return a JSON with `{"message": "Hello from /v1/users"}`. The third route is for the `/metrics` endpoint that will return the metrics in the Prometheus format. Then you have `RequestMetricsMiddleware` middleware, it will be called for every request made to the API. It will record the incoming requests metrics like status codes and paths. Finally, you are running the gin application on port 8000. func main() { router := gin.Default() // Register /metrics before middleware router.GET("/metrics", PrometheusHandler()) router.Use(RequestMetricsMiddleware()) router.GET("/health", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Up and running!", }) }) router.GET("/v1/users", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "Hello from /v1/users", }) }) router.Run(":8000") } Now comes the middleware function `RequestMetricsMiddleware`. This function is called for every request made to the API. It increments the `HttpRequestTotal` counter (different counter for different paths and status codes) if the status code is less than or equal to 400. If the status code is greater than 400, it increments the `HttpRequestErrorTotal` counter (different counter for different paths and status codes). The `PrometheusHandler` function is the custom handler that will be called for the `/metrics` endpoint. It will return the metrics in the Prometheus format. // Custom metrics handler with custom registry func PrometheusHandler() gin.HandlerFunc { h := promhttp.HandlerFor(customRegistry, promhttp.HandlerOpts{}) return func(c *gin.Context) { h.ServeHTTP(c.Writer, c.Request) } } // Middleware to record incoming requests metrics func RequestMetricsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { path := c.Request.URL.Path c.Next() status := c.Writer.Status() if status < 400 { HttpRequestTotal.WithLabelValues(path, strconv.Itoa(status)).Inc() } else { HttpRequestErrorTotal.WithLabelValues(path, strconv.Itoa(status)).Inc() } } } That's it, this was the complete gist of the application. Now it's time to run and test if the app is registering metrics correctly. [Running the application](https://docs.docker.com/guides/go-prometheus-monitoring/application/#running-the-application) ------------------------------------------------------------------------------------------------------------------------ Make sure you are still inside `go-prometheus-monitoring` directory in the terminal, and run the following command. Install the dependencies by running `go mod tidy` and then build and run the application by running `go run main.go`. Then visit `http://localhost:8000/health` or `http://localhost:8000/v1/users`. You should see the output `{"message": "Up and running!"}` or `{"message": "Hello from /v1/users"}`. If you are able to see this then your app is successfully up and running. Now, check your application's metrics by accessing the `/metrics` endpoint. Open `http://localhost:8000/metrics` in your browser. You should see similar output to the following. # HELP api_http_request_error_total Total number of errors returned by the API # TYPE api_http_request_error_total counter api_http_request_error_total{path="/",status="404"} 1 api_http_request_error_total{path="//v1/users",status="404"} 1 api_http_request_error_total{path="/favicon.ico",status="404"} 1 # HELP api_http_request_total Total number of requests processed by the API # TYPE api_http_request_total counter api_http_request_total{path="/health",status="200"} 2 api_http_request_total{path="/v1/users",status="200"} 1 In the terminal, press `ctrl` + `c` to stop the application. > Note > > If you don't want to run the application locally, and want to run it in a Docker container, skip to next page where you create a Dockerfile and containerize the application. [Summary](https://docs.docker.com/guides/go-prometheus-monitoring/application/#summary) ---------------------------------------------------------------------------------------- In this section, you learned how to create a Golang app to register metrics with Prometheus. By implementing middleware functions, you were able to increment the counters based on the request path and status codes. [Next steps](https://docs.docker.com/guides/go-prometheus-monitoring/application/#next-steps) ---------------------------------------------------------------------------------------------- In the next section, you'll learn how to containerize your application. [Containerize a Golang application »](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/) --- # Lifecycle callbacks | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testcontainers container lifecycle management using JUnit 5](https://docs.docker.com/guides/testcontainers-java-lifecycle/) Learn different approaches to manage container lifecycle with Testcontainers using JUnit 5 lifecycle callbacks, extension annotations, and the singleton containers pattern. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-lifecycle/lifecycle-callbacks/) [Lifecycle callbacks](https://docs.docker.com/guides/testcontainers-java-lifecycle/lifecycle-callbacks/) [3](https://docs.docker.com/guides/testcontainers-java-lifecycle/extension-annotations/) [Extension annotations](https://docs.docker.com/guides/testcontainers-java-lifecycle/extension-annotations/) [4](https://docs.docker.com/guides/testcontainers-java-lifecycle/singleton-containers/) [Singleton containers](https://docs.docker.com/guides/testcontainers-java-lifecycle/singleton-containers/) [« Back to all guides](https://docs.docker.com/guides/) JUnit 5 lifecycle callbacks =========================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * When testing with Testcontainers, you want to start the required containers before executing any tests and remove them afterwards. You can use JUnit 5 `@BeforeAll` and `@AfterAll` lifecycle callback methods for this: package com.testcontainers.demo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.postgresql.PostgreSQLContainer; class CustomerServiceWithLifeCycleCallbacksTest { static PostgreSQLContainer postgres = new PostgreSQLContainer( "postgres:16-alpine" ); CustomerService customerService; @BeforeAll static void startContainers() { postgres.start(); } @AfterAll static void stopContainers() { postgres.stop(); } @BeforeEach void setUp() { customerService = new CustomerService( postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword() ); customerService.deleteAllCustomers(); } @Test void shouldCreateCustomer() { customerService.createCustomer(new Customer(1L, "George")); Optional customer = customerService.getCustomer(1L); assertTrue(customer.isPresent()); assertEquals(1L, customer.get().id()); assertEquals("George", customer.get().name()); } @Test void shouldGetCustomers() { customerService.createCustomer(new Customer(1L, "George")); customerService.createCustomer(new Customer(2L, "John")); List customers = customerService.getAllCustomers(); assertEquals(2, customers.size()); } } Here's what the code does: * `PostgreSQLContainer` is declared as a **static field**. The container starts before all tests and stops after all tests in this class. * `@BeforeAll` starts the container, `@AfterAll` stops it. * `@BeforeEach` initializes `CustomerService` with the container's JDBC parameters and deletes all rows to give each test a clean database. Key observations: * Because the container is a **static field**, it's shared across all test methods in the class. You can declare it as a non-static field and use `@BeforeEach`/`@AfterEach` to start a new container per test, but this isn't recommended as it's resource-intensive. * Even without explicitly stopping the container in `@AfterAll`, Testcontainers uses the [Ryuk container](https://github.com/testcontainers/moby-ryuk) to clean up containers automatically when the JVM exits. [JUnit 5 extension annotations »](https://docs.docker.com/guides/testcontainers-java-lifecycle/extension-annotations/) --- # Setting up Testcontainers Cloud by Docker | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Mastering Testcontainers Cloud by Docker: streamlining integration testing with containers](https://docs.docker.com/guides/testcontainers-cloud/) Automate, scale, and optimize testing workflows with Testcontainers Cloud Product demo 12 minutes [1](https://docs.docker.com/guides/testcontainers-cloud/why/) [Why Testcontainers Cloud?](https://docs.docker.com/guides/testcontainers-cloud/why/) [2](https://docs.docker.com/guides/testcontainers-cloud/demo-local/) [Setting up Testcontainers Cloud by Docker](https://docs.docker.com/guides/testcontainers-cloud/demo-local/) [3](https://docs.docker.com/guides/testcontainers-cloud/demo-ci/) [Configuring Testcontainers Cloud in the CI Pipeline](https://docs.docker.com/guides/testcontainers-cloud/demo-ci/) [4](https://docs.docker.com/guides/testcontainers-cloud/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/testcontainers-cloud/common-questions/) Resources: * [Testcontainers Guides](https://testcontainers.com/guides) * [Testcontainers Best Practices](https://www.docker.com/blog/testcontainers-best-practices/) * [Simple local development with Testcontainers Desktop](https://testcontainers.com/guides/simple-local-development-with-testcontainers-desktop/) * [Streamlining Local Development with Dev Containers and Testcontainers Cloud](https://www.docker.com/blog/streamlining-local-development-with-dev-containers-and-testcontainers-cloud/) * [Running Testcontainers Tests Using GitHub Actions and Testcontainers Cloud](https://www.docker.com/blog/running-testcontainers-tests-using-github-actions/) * [Testcontainers Cloud on the Docker Blog](https://www.docker.com/search/?_sf_s=testcontainers%20cloud) [« Back to all guides](https://docs.docker.com/guides/) Setting up Testcontainers Cloud by Docker ========================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * This demo shows the process of setting up Testcontainers Cloud by Docker to work in your local development environment using the Testcontainers Desktop application. By the end of this walkthrough, you'll have Testcontainers Cloud by Docker up and running, ready to offload container management from your local machine to the cloud for more efficient testing. * Install and configure Testcontainers Cloud and the CLI to seamlessly integrate with your local development environment. * Set up and configure the Testcontainers Desktop application to monitor and manage cloud-based containers during local tests. * Create and run integration tests using Testcontainers that leverage cloud-based container resources. * Monitor and manage containers efficiently, understanding how Testcontainers Cloud automates cleanup and ensures consistent testing environments. * Review options for monitoring and troubleshooting in the Testcontainers Cloud Dashboard. [Configuring Testcontainers Cloud in the CI Pipeline »](https://docs.docker.com/guides/testcontainers-cloud/demo-ci/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Deno language-specific guide](https://docs.docker.com/guides/deno/) Learn how to containerize JavaScript applications with the Deno runtime using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Docker Hardened Images 10 minutes [1](https://docs.docker.com/guides/deno/containerize/) [Containerize your app](https://docs.docker.com/guides/deno/containerize/) [2](https://docs.docker.com/guides/deno/develop/) [Develop your app](https://docs.docker.com/guides/deno/develop/) [3](https://docs.docker.com/guides/deno/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/deno/configure-ci-cd/) [4](https://docs.docker.com/guides/deno/deploy/) [Test your deployment](https://docs.docker.com/guides/deno/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for Deno development =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/deno/develop/#prerequisites) ---------------------------------------------------------------------------- Complete [Containerize a Deno application](https://docs.docker.com/guides/deno/containerize/) . [Overview](https://docs.docker.com/guides/deno/develop/#overview) ------------------------------------------------------------------ In this section, you'll learn how to set up a development environment for your containerized application. This includes: * Configuring Compose to automatically update your running Compose services as you edit and save your code [Get the sample application](https://docs.docker.com/guides/deno/develop/#get-the-sample-application) ------------------------------------------------------------------------------------------------------ Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/dockersamples/docker-deno.git && cd docker-deno [Automatically update services](https://docs.docker.com/guides/deno/develop/#automatically-update-services) ------------------------------------------------------------------------------------------------------------ Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Open your `compose.yml` file in an IDE or text editor and then add the Compose Watch instructions. The following example shows how to add Compose Watch to your `compose.yml` file. | | | | --- | --- | | 1
2
3
4
5
6
7
8
9
10
11
12 | services:
server:
image: deno-server
build:
context: .
dockerfile: Dockerfile
ports:
- "8000:8000"
develop:
watch:
- action: rebuild
path: . | Run the following command to run your application with Compose Watch. $ docker compose watch Now, if you modify your `server.ts` you will see the changes in real time without re-building the image. To test it out, open the `server.ts` file in your favorite text editor and change the message from `{"Status" : "OK"}` to `{"Status" : "Updated"}`. Save the file and refresh your browser at `http://localhost:8000`. You should see the updated message. Press `ctrl+c` in the terminal to stop your application. [Summary](https://docs.docker.com/guides/deno/develop/#summary) ---------------------------------------------------------------- In this section, you also learned how to use Compose Watch to automatically rebuild and run your container when you update your code. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose file watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) [Next steps](https://docs.docker.com/guides/deno/develop/#next-steps) ---------------------------------------------------------------------- In the next section, you'll take a look at how to set up a CI/CD pipeline using GitHub Actions. [Configure CI/CD for your Deno application »](https://docs.docker.com/guides/deno/configure-ci-cd/) --- # Demo: set up and use Docker Compose | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Defining and running multi-container applications with Docker Compose](https://docs.docker.com/guides/docker-compose/) Simplify the process of defining, configuring, and running multi-container Docker applications. Product demo 10 minutes [1](https://docs.docker.com/guides/docker-compose/why/) [Why Docker Compose?](https://docs.docker.com/guides/docker-compose/why/) [2](https://docs.docker.com/guides/docker-compose/setup/) [Demo: set up and use Docker Compose](https://docs.docker.com/guides/docker-compose/setup/) [3](https://docs.docker.com/guides/docker-compose/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/docker-compose/common-questions/) Resources: * [Overview of Docker Compose CLI](https://docs.docker.com/reference/cli/docker/compose/) * [Overview of Docker Compose](https://docs.docker.com/compose/) * [How Compose works](https://docs.docker.com/compose/intro/compose-application-model/) * [Using profiles with Compose](https://docs.docker.com/compose/how-tos/profiles/) * [Control startup and shutdown order with Compose](https://docs.docker.com/compose/how-tos/startup-order/) * [Compose Build Specification](https://docs.docker.com/compose/compose-file/build/) [« Back to all guides](https://docs.docker.com/guides/) Demo: set up and use Docker Compose =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * This Docker Compose demo shows how to orchestrate a multi-container application environment, streamlining development and deployment processes. * Compare Docker Compose to the `docker run` command * Configure a multi-container web app using a Compose file * Run a multi-container web app using one command [Common challenges and questions »](https://docs.docker.com/guides/docker-compose/common-questions/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [PDF analysis and chat](https://docs.docker.com/guides/genai-pdf-bot/) Learn how to build a PDF bot for parsing PDF documents and generating responses using Docker and generative AI. AI 20 minutes [1](https://docs.docker.com/guides/genai-pdf-bot/containerize/) [Containerize your app](https://docs.docker.com/guides/genai-pdf-bot/containerize/) [2](https://docs.docker.com/guides/genai-pdf-bot/develop/) [Develop your app](https://docs.docker.com/guides/genai-pdf-bot/develop/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for generative AI development ============================================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/genai-pdf-bot/develop/#prerequisites) ------------------------------------------------------------------------------------- Complete [Containerize a generative AI application](https://docs.docker.com/guides/genai-pdf-bot/containerize/) . [Overview](https://docs.docker.com/guides/genai-pdf-bot/develop/#overview) --------------------------------------------------------------------------- In this section, you'll learn how to set up a development environment to access all the services that your generative AI (GenAI) application needs. This includes: * Adding a local database * Adding a local or remote LLM service > Note > > You can see more samples of containerized GenAI applications in the [GenAI Stack](https://github.com/docker/genai-stack) > demo applications. [Add a local database](https://docs.docker.com/guides/genai-pdf-bot/develop/#add-a-local-database) --------------------------------------------------------------------------------------------------- You can use containers to set up local services, like a database. In this section, you'll update the `compose.yaml` file to define a database service. In addition, you'll specify an environment variables file to load the database connection information rather than manually entering the information every time. To run the database service: 1. In the cloned repository's directory, rename `env.example` file to `.env`. This file contains the environment variables that the containers will use. 2. In the cloned repository's directory, open the `compose.yaml` file in an IDE or text editor. 3. In the `compose.yaml` file, add the following: * Add instructions to run a Neo4j database * Specify the environment file under the server service in order to pass in the environment variables for the connection The following is the updated `compose.yaml` file. All comments have been removed. services: server: build: context: . ports: - 8000:8000 env_file: - .env depends_on: database: condition: service_healthy database: image: neo4j:5.11 ports: - "7474:7474" - "7687:7687" environment: - NEO4J_AUTH=${NEO4J_USERNAME}/${NEO4J_PASSWORD} healthcheck: test: ["CMD-SHELL", "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1"] interval: 5s timeout: 3s retries: 5 > Note > > To learn more about Neo4j, see the [Neo4j Official Docker Image](https://hub.docker.com/_/neo4j) > . 4. Run the application. Inside the `docker-genai-sample` directory, run the following command in a terminal. $ docker compose up --build 5. Access the application. Open a browser and view the application at [http://localhost:8000](http://localhost:8000/) . You should see a simple Streamlit application. Note that asking questions to a PDF will cause the application to fail because the LLM service specified in the `.env` file isn't running yet. 6. Stop the application. In the terminal, press `ctrl`+`c` to stop the application. [Add a local or remote LLM service](https://docs.docker.com/guides/genai-pdf-bot/develop/#add-a-local-or-remote-llm-service) ----------------------------------------------------------------------------------------------------------------------------- The sample application supports both [Ollama](https://ollama.ai/) and [OpenAI](https://openai.com/) . This guide provides instructions for the following scenarios: * Run Ollama in a container * Run Ollama outside of a container * Use OpenAI While all platforms can use any of the previous scenarios, the performance and GPU support may vary. You can use the following guidelines to help you choose the appropriate option: * Run Ollama in a container if you're on Linux, and using a native installation of the Docker Engine, or Windows 10/11, and using Docker Desktop, you have a CUDA-supported GPU, and your system has at least 8 GB of RAM. * Run Ollama outside of a container if you're on an Apple silicon Mac. * Use OpenAI if the previous two scenarios don't apply to you. Choose one of the following options for your LLM service. Run Ollama in a container Run Ollama outside of a container Use OpenAI When running Ollama in a container, you should have a CUDA-supported GPU. While you can run Ollama in a container without a supported GPU, the performance may not be acceptable. Only Linux and Windows 11 support GPU access to containers. To run Ollama in a container and provide GPU access: 1. Install the prerequisites. * For Docker Engine on Linux, install the [NVIDIA Container Toolkit](https://github.com/NVIDIA/nvidia-container-toolkit) . * For Docker Desktop on Windows 10/11, install the latest [NVIDIA driver](https://www.nvidia.com/Download/index.aspx) and make sure you are using the [WSL2 backend](https://docs.docker.com/desktop/features/wsl/#turn-on-docker-desktop-wsl-2) 2. Add the Ollama service and a volume in your `compose.yaml`. The following is the updated `compose.yaml`: services: server: build: context: . ports: - 8000:8000 env_file: - .env depends_on: database: condition: service_healthy database: image: neo4j:5.11 ports: - "7474:7474" - "7687:7687" environment: - NEO4J_AUTH=${NEO4J_USERNAME}/${NEO4J_PASSWORD} healthcheck: test: [\ "CMD-SHELL",\ "wget --no-verbose --tries=1 --spider localhost:7474 || exit 1",\ ] interval: 5s timeout: 3s retries: 5 ollama: image: ollama/ollama:latest ports: - "11434:11434" volumes: - ollama_volume:/root/.ollama deploy: resources: reservations: devices: - driver: nvidia count: all capabilities: [gpu] volumes: ollama_volume: > Note > > For more details about the Compose instructions, see [Turn on GPU access with Docker Compose](https://docs.docker.com/compose/how-tos/gpu-support/) > . 3. Add the ollama-pull service to your `compose.yaml` file. This service uses the `docker/genai:ollama-pull` image, based on the GenAI Stack's [pull\_model.Dockerfile](https://github.com/docker/genai-stack/blob/main/pull_model.Dockerfile) . The service will automatically pull the model for your Ollama container. The following is the updated section of the `compose.yaml` file: services: server: build: context: . ports: - 8000:8000 env_file: - .env depends_on: database: condition: service_healthy ollama-pull: condition: service_completed_successfully ollama-pull: image: docker/genai:ollama-pull env_file: - .env # ... To run Ollama outside of a container: 1. [Install](https://github.com/jmorganca/ollama) and run Ollama on your host machine. 2. Update the `OLLAMA_BASE_URL` value in your `.env` file to `http://host.docker.internal:11434`. 3. Pull the model to Ollama using the following command. $ ollama pull llama2 > Important > > Using OpenAI requires an [OpenAI account](https://platform.openai.com/login) > . OpenAI is a third-party hosted service and charges may apply. 1. Update the `LLM` value in your `.env` file to `gpt-3.5`. 2. Uncomment and update the `OPENAI_API_KEY` value in your `.env` file to your [OpenAI API key](https://help.openai.com/en/articles/4936850-where-do-i-find-my-api-key) . [Run your GenAI application](https://docs.docker.com/guides/genai-pdf-bot/develop/#run-your-genai-application) --------------------------------------------------------------------------------------------------------------- At this point, you have the following services in your Compose file: * Server service for your main GenAI application * Database service to store vectors in a Neo4j database * (optional) Ollama service to run the LLM * (optional) Ollama-pull service to automatically pull the model for the Ollama service To run all the services, run the following command in your `docker-genai-sample` directory: $ docker compose up --build If your Compose file has the ollama-pull service, it may take several minutes for the ollama-pull service to pull the model. The ollama-pull service will continuously update the console with its status. After pulling the model, the ollama-pull service container will stop and you can access the application. Once the application is running, open a browser and access the application at [http://localhost:8000](http://localhost:8000/) . Upload a PDF file, for example the [Docker CLI Cheat Sheet](https://docs.docker.com/get-started/docker_cheatsheet.pdf) , and ask a question about the PDF. Depending on your system and the LLM service that you chose, it may take several minutes to answer. If you are using Ollama and the performance isn't acceptable, try using OpenAI. [Summary](https://docs.docker.com/guides/genai-pdf-bot/develop/#summary) ------------------------------------------------------------------------- In this section, you learned how to set up a development environment to provide access all the services that your GenAI application needs. Related information: * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Ollama Docker image](https://hub.docker.com/r/ollama/ollama) * [Neo4j Official Docker Image](https://hub.docker.com/_/neo4j) * [GenAI Stack demo applications](https://github.com/docker/genai-stack) [Next steps](https://docs.docker.com/guides/genai-pdf-bot/develop/#next-steps) ------------------------------------------------------------------------------- See samples of more GenAI applications in the [GenAI Stack demo applications](https://github.com/docker/genai-stack) . --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for .NET](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/) Learn how to create a .NET application and test database interactions using Testcontainers for .NET with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg "C#") C# Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Add Testcontainers dependencies](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/write-tests/#add-testcontainers-dependencies) ----------------------------------------------------------------------------------------------------------------------------------------------------- Add the Testcontainers PostgreSQL module to the test project: $ dotnet add ./CustomerService.Tests/CustomerService.Tests.csproj package Testcontainers.PostgreSql [Write the test](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/write-tests/#write-the-test) ------------------------------------------------------------------------------------------------------------------- Create `CustomerServiceTest.cs` in the test project: using Testcontainers.PostgreSql; namespace Customers.Tests; public sealed class CustomerServiceTest : IAsyncLifetime { private readonly PostgreSqlContainer _postgres = new PostgreSqlBuilder() .WithImage("postgres:16-alpine") .Build(); public Task InitializeAsync() { return _postgres.StartAsync(); } public Task DisposeAsync() { return _postgres.DisposeAsync().AsTask(); } [Fact] public void ShouldReturnTwoCustomers() { // Given var customerService = new CustomerService(new DbConnectionProvider(_postgres.GetConnectionString())); // When customerService.Create(new Customer(1, "George")); customerService.Create(new Customer(2, "John")); var customers = customerService.GetCustomers(); // Then Assert.Equal(2, customers.Count()); } } Here's what the test does: * Declares a `PostgreSqlContainer` using the `PostgreSqlBuilder` with the `postgres:16-alpine` Docker image. * Implements `IAsyncLifetime` for container lifecycle management: * `InitializeAsync()` starts the container before the test runs. * `DisposeAsync()` stops and removes the container after the test finishes. * `ShouldReturnTwoCustomers()` creates a `CustomerService` with connection details from the container, inserts two customers, fetches all customers, and asserts the count. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-dotnet-getting-started/run-tests/) --- # Demo | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Securing your software supply chain with Docker Scout](https://docs.docker.com/guides/docker-scout/) Enhance container security by automating vulnerability detection and remediation. Product demo 20 minutes [1](https://docs.docker.com/guides/docker-scout/why/) [Why Docker Scout?](https://docs.docker.com/guides/docker-scout/why/) [2](https://docs.docker.com/guides/docker-scout/demo/) [Demo](https://docs.docker.com/guides/docker-scout/demo/) [3](https://docs.docker.com/guides/docker-scout/s3c/) [Software supply chain security](https://docs.docker.com/guides/docker-scout/s3c/) [4](https://docs.docker.com/guides/docker-scout/sbom/) [Software Bill of Materials](https://docs.docker.com/guides/docker-scout/sbom/) [5](https://docs.docker.com/guides/docker-scout/attestations/) [Attestations](https://docs.docker.com/guides/docker-scout/attestations/) [6](https://docs.docker.com/guides/docker-scout/remediation/) [Remediation](https://docs.docker.com/guides/docker-scout/remediation/) [7](https://docs.docker.com/guides/docker-scout/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/docker-scout/common-questions/) Resources: * [Docker Scout overview](https://docs.docker.com/scout/) * [Docker Scout quickstart](https://docs.docker.com/scout/quickstart/) * [Install Docker Scout](https://docs.docker.com/scout/install/) [« Back to all guides](https://docs.docker.com/guides/) Docker Scout demo ================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * Docker Scout has powerful features for enhancing containerized application security and ensuring a robust software supply chain. * Define vulnerability remediation * Discuss why remediation is essential to maintain the security and integrity of containerized applications * Discuss common vulnerabilities * Implement remediation techniques: updating base images, applying patches, removing unnecessary packages * Verify and validate remediation efforts using Docker Scout [Software supply chain security »](https://docs.docker.com/guides/docker-scout/s3c/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [R language-specific guide](https://docs.docker.com/guides/r/) This guide details how to containerize R applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/r/r-original.svg "R") R 10 minutes [1](https://docs.docker.com/guides/r/containerize/) [Containerize your app](https://docs.docker.com/guides/r/containerize/) [2](https://docs.docker.com/guides/r/develop/) [Develop your app](https://docs.docker.com/guides/r/develop/) [3](https://docs.docker.com/guides/r/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/r/configure-ci-cd/) [4](https://docs.docker.com/guides/r/deploy/) [Test your deployment](https://docs.docker.com/guides/r/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for R development ================================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/r/develop/#prerequisites) ------------------------------------------------------------------------- Complete [Containerize a R application](https://docs.docker.com/guides/r/containerize/) . [Overview](https://docs.docker.com/guides/r/develop/#overview) --------------------------------------------------------------- In this section, you'll learn how to set up a development environment for your containerized application. This includes: * Adding a local database and persisting data * Configuring Compose to automatically update your running Compose services as you edit and save your code [Get the sample application](https://docs.docker.com/guides/r/develop/#get-the-sample-application) --------------------------------------------------------------------------------------------------- You'll need to clone a new repository to get a sample application that includes logic to connect to the database. Change to a directory where you want to clone the repository and run the following command. $ git clone https://github.com/mfranzon/r-docker-dev.git [Configure the application to use the database](https://docs.docker.com/guides/r/develop/#configure-the-application-to-use-the-database) ----------------------------------------------------------------------------------------------------------------------------------------- To try the connection between the Shiny application and the local database you have to modify the `Dockerfile` changing the `COPY` instruction: -COPY src/ . +COPY src_db/ . [Add a local database and persist data](https://docs.docker.com/guides/r/develop/#add-a-local-database-and-persist-data) ------------------------------------------------------------------------------------------------------------------------- You can use containers to set up local services, like a database. In this section, you'll update the `compose.yaml` file to define a database service and a volume to persist data. In the cloned repository's directory, open the `compose.yaml` file in an IDE or text editor. In the `compose.yaml` file, you need to un-comment the properties for configuring the database. You must also mount the database password file and set an environment variable on the `shiny-app` service pointing to the location of the file in the container. The following is the updated `compose.yaml` file. services: shiny-app: build: context: . dockerfile: Dockerfile ports: - 3838:3838 environment: - POSTGRES_PASSWORD_FILE=/run/secrets/db-password depends_on: db: condition: service_healthy secrets: - db-password db: image: postgres:18 restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt > Note > > To learn more about the instructions in the Compose file, see [Compose file reference](https://docs.docker.com/reference/compose-file/) > . Before you run the application using Compose, notice that this Compose file specifies a `password.txt` file to hold the database's password. You must create this file as it's not included in the source repository. In the cloned repository's directory, create a new directory named `db` and inside that directory create a file named `password.txt` that contains the password for the database. Using your favorite IDE or text editor, add the following contents to the `password.txt` file. mysecretpassword Save and close the `password.txt` file. You should now have the following contents in your `r-docker-dev` directory. ├── r-docker-dev/ │ ├── db/ │ │ └── password.txt │ ├── src/ │ │ └── app.R │ ├── src_db/ │ │ └── app_db.R │ ├── requirements.txt │ ├── .dockerignore │ ├── compose.yaml │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md Now, run the following `docker compose up` command to start your application. $ docker compose up --build Now test your DB connection opening a browser at: http://localhost:3838 You should see a pop-up message: DB CONNECTED Press `ctrl+c` in the terminal to stop your application. [Automatically update services](https://docs.docker.com/guides/r/develop/#automatically-update-services) --------------------------------------------------------------------------------------------------------- Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Lines 15 to 18 in the `compose.yaml` file contain properties that trigger Docker to rebuild the image when a file in the current working directory is changed: | | | | --- | --- | | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41 | services:
shiny-app:
build:
context: .
dockerfile: Dockerfile
ports:
- 3838:3838
environment:
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
depends_on:
db:
condition: service_healthy
secrets:
- db-password
develop:
watch:
- action: rebuild
path: .
db:
image: postgres:18
restart: always
user: postgres
secrets:
- db-password
volumes:
- db-data:/var/lib/postgresql
environment:
- POSTGRES_DB=example
- POSTGRES_PASSWORD_FILE=/run/secrets/db-password
expose:
- 5432
healthcheck:
test: ["CMD", "pg_isready"]
interval: 10s
timeout: 5s
retries: 5
volumes:
db-data:
secrets:
db-password:
file: db/password.txt | Run the following command to run your application with Compose Watch. $ docker compose watch Now, if you modify your `app.R` you will see the changes in real time without re-building the image! Press `ctrl+c` in the terminal to stop your application. [Summary](https://docs.docker.com/guides/r/develop/#summary) ------------------------------------------------------------- In this section, you took a look at setting up your Compose file to add a local database and persist data. You also learned how to use Compose Watch to automatically rebuild and run your container when you update your code. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose file watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) [Next steps](https://docs.docker.com/guides/r/develop/#next-steps) ------------------------------------------------------------------- In the next section, you'll take a look at how to set up a CI/CD pipeline using GitHub Actions. [Configure CI/CD for your R application »](https://docs.docker.com/guides/r/configure-ci-cd/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Go](https://docs.docker.com/guides/testcontainers-go-getting-started/) Learn how to create a Go application and test database interactions using Testcontainers for Go with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg "Go") Go Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-go-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-go-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-go-getting-started/test-suites/) [Test suites](https://docs.docker.com/guides/testcontainers-go-getting-started/test-suites/) [4](https://docs.docker.com/guides/testcontainers-go-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-go-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * You have the `Repository` implementation ready, but for testing you need a PostgreSQL database. You can use testcontainers-go to spin up a Postgres database in a Docker container and run your tests against that database. [Set up the test database](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/#set-up-the-test-database) ----------------------------------------------------------------------------------------------------------------------------------- In real applications you might use a database migration tool, but for this guide, use a script to initialize the database. Create a `testdata/init-db.sql` file to create the `CUSTOMERS` table and insert sample data: CREATE TABLE IF NOT EXISTS customers (id serial, name varchar(255), email varchar(255)); INSERT INTO customers(name, email) VALUES ('John', 'john@gmail.com'); [Understand the testcontainers-go API](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/#understand-the-testcontainers-go-api) ----------------------------------------------------------------------------------------------------------------------------------------------------------- The testcontainers-go library provides the generic `Container` abstraction that can run any containerized service. To further simplify, testcontainers-go provides technology-specific modules that reduce boilerplate and provide a functional options pattern to construct the container instance. For example, `PostgresContainer` provides `WithDatabase()`, `WithUsername()`, `WithPassword()`, and other functions to set various properties of Postgres containers. [Write the test](https://docs.docker.com/guides/testcontainers-go-getting-started/write-tests/#write-the-test) --------------------------------------------------------------------------------------------------------------- Create the `customer/repo_test.go` file and implement the test: package customer import ( "context" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/postgres" ) func TestCustomerRepository(t *testing.T) { ctx := context.Background() ctr, err := postgres.Run(ctx, "postgres:16-alpine", postgres.WithInitScripts(filepath.Join("..", "testdata", "init-db.sql")), postgres.WithDatabase("test-db"), postgres.WithUsername("postgres"), postgres.WithPassword("postgres"), postgres.BasicWaitStrategies(), ) testcontainers.CleanupContainer(t, ctr) require.NoError(t, err) connStr, err := ctr.ConnectionString(ctx, "sslmode=disable") require.NoError(t, err) customerRepo, err := NewRepository(ctx, connStr) require.NoError(t, err) c, err := customerRepo.CreateCustomer(ctx, Customer{ Name: "Henry", Email: "henry@gmail.com", }) assert.NoError(t, err) assert.NotNil(t, c) customer, err := customerRepo.GetCustomerByEmail(ctx, "henry@gmail.com") assert.NoError(t, err) assert.NotNil(t, customer) assert.Equal(t, "Henry", customer.Name) assert.Equal(t, "henry@gmail.com", customer.Email) } Here's what the test does: * Calls `postgres.Run()` with the `postgres:16-alpine` Docker image as the first argument. This is the v0.41.0 API — the image is a required positional parameter instead of an option. * Configures initialization scripts using `WithInitScripts(...)` so that the `CUSTOMERS` table is created and sample data is inserted after the database starts. * Uses `postgres.BasicWaitStrategies()` which combines waiting for the Postgres log message and for the port to be ready. This replaces manual wait strategy configuration. * Calls `testcontainers.CleanupContainer(t, ctr)` right after `postgres.Run()`. This registers automatic cleanup with the test framework, replacing the manual `t.Cleanup` and `Terminate` pattern. * Obtains the database `ConnectionString` from the container and initializes a `Repository`. * Creates a customer with the email `henry@gmail.com` and verifies that the customer exists in the database. [Reuse containers with test suites »](https://docs.docker.com/guides/testcontainers-go-getting-started/test-suites/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Java](https://docs.docker.com/guides/testcontainers-java-getting-started/) Learn how to create a Java application and test database interactions using Testcontainers for Java with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * You have the `CustomerService` implementation ready, but for testing you need a PostgreSQL database. You can use Testcontainers to spin up a Postgres database in a Docker container and run your tests against it. [Add Testcontainers dependencies](https://docs.docker.com/guides/testcontainers-java-getting-started/write-tests/#add-testcontainers-dependencies) --------------------------------------------------------------------------------------------------------------------------------------------------- Add the Testcontainers PostgreSQL module as a test dependency in `pom.xml`: org.testcontainers testcontainers-postgresql 2.0.4 test Since the application uses a Postgres database, the Testcontainers Postgres module provides a `PostgreSQLContainer` class for managing the container. [Write the test](https://docs.docker.com/guides/testcontainers-java-getting-started/write-tests/#write-the-test) ----------------------------------------------------------------------------------------------------------------- Create `CustomerServiceTest.java` under `src/test/java`: package com.testcontainers.demo; import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.postgresql.PostgreSQLContainer; class CustomerServiceTest { static PostgreSQLContainer postgres = new PostgreSQLContainer( "postgres:16-alpine" ); CustomerService customerService; @BeforeAll static void beforeAll() { postgres.start(); } @AfterAll static void afterAll() { postgres.stop(); } @BeforeEach void setUp() { DBConnectionProvider connectionProvider = new DBConnectionProvider( postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword() ); customerService = new CustomerService(connectionProvider); } @Test void shouldGetCustomers() { customerService.createCustomer(new Customer(1L, "George")); customerService.createCustomer(new Customer(2L, "John")); List customers = customerService.getAllCustomers(); assertEquals(2, customers.size()); } } Here's what the test does: * Declares a `PostgreSQLContainer` with the `postgres:16-alpine` Docker image. * The `@BeforeAll` callback starts the Postgres container before any test methods run. * The `@BeforeEach` callback creates a `DBConnectionProvider` using the JDBC connection parameters from the container, then creates a `CustomerService`. The `CustomerService` constructor creates the `customers` table if it doesn't exist. * `shouldGetCustomers()` inserts 2 customer records, fetches all customers, and asserts the count. * The `@AfterAll` callback stops the container after all test methods finish. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-getting-started/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing a Spring Boot REST API with Testcontainers](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/) Learn how to create a Spring Boot REST API with Spring Data JPA and PostgreSQL, then test it using Testcontainers and REST Assured. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * To test the REST API, you need a running Postgres database and a started Spring context. Testcontainers spins up Postgres in a Docker container and `@DynamicPropertySource` connects it to Spring. [Write the test](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/#write-the-test) ---------------------------------------------------------------------------------------------------------------------- Create `CustomerControllerTest.java`: package com.testcontainers.demo; import static io.restassured.RestAssured.given; import static org.hamcrest.Matchers.hasSize; import io.restassured.RestAssured; import io.restassured.http.ContentType; import java.util.List; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.postgresql.PostgreSQLContainer; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) class CustomerControllerTest { @LocalServerPort private Integer port; static PostgreSQLContainer postgres = new PostgreSQLContainer( "postgres:16-alpine" ); @BeforeAll static void beforeAll() { postgres.start(); } @AfterAll static void afterAll() { postgres.stop(); } @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add("spring.datasource.url", postgres::getJdbcUrl); registry.add("spring.datasource.username", postgres::getUsername); registry.add("spring.datasource.password", postgres::getPassword); } @Autowired CustomerRepository customerRepository; @BeforeEach void setUp() { RestAssured.baseURI = "http://localhost:" + port; customerRepository.deleteAll(); } @Test void shouldGetAllCustomers() { List customers = List.of( new Customer(null, "John", "john@mail.com"), new Customer(null, "Dennis", "dennis@mail.com") ); customerRepository.saveAll(customers); given() .contentType(ContentType.JSON) .when() .get("/api/customers") .then() .statusCode(200) .body(".", hasSize(2)); } } Here's what the test does: * `@SpringBootTest` starts the full application on a random port. * A `PostgreSQLContainer` starts in `@BeforeAll` and stops in `@AfterAll`. * `@DynamicPropertySource` registers the container's JDBC URL, username, and password with Spring so that the datasource connects to the test container. * `@BeforeEach` deletes all customer rows before each test to prevent test pollution. * `shouldGetAllCustomers()` inserts two customers, calls `GET /api/customers`, and verifies the response contains 2 records. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Node.js](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/) Learn how to create a Node.js application and test database interactions using Testcontainers for Node.js with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * Create `src/customer-repository.test.js` with the test: const { Client } = require("pg"); const { PostgreSqlContainer } = require("@testcontainers/postgresql"); const { createCustomerTable, createCustomer, getCustomers, } = require("./customer-repository"); describe("Customer Repository", () => { jest.setTimeout(60000); let postgresContainer; let postgresClient; beforeAll(async () => { postgresContainer = await new PostgreSqlContainer().start(); postgresClient = new Client({ connectionString: postgresContainer.getConnectionUri(), }); await postgresClient.connect(); await createCustomerTable(postgresClient); }); afterAll(async () => { await postgresClient.end(); await postgresContainer.stop(); }); it("should create and return multiple customers", async () => { const customer1 = { id: 1, name: "John Doe" }; const customer2 = { id: 2, name: "Jane Doe" }; await createCustomer(postgresClient, customer1); await createCustomer(postgresClient, customer2); const customers = await getCustomers(postgresClient); expect(customers).toEqual([customer1, customer2]); }); }); Here's what the test does: * The `beforeAll` block starts a real PostgreSQL container using `PostgreSqlContainer`. It then creates a `pg` client connected to the container and sets up the `customers` table. * The `afterAll` block closes the client connection and stops the container. * The test inserts two customers, fetches all customers, and asserts the results match. The test timeout is set to 60 seconds to allow time for the container to start on the first run (when the Docker image needs to be pulled). [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-nodejs-getting-started/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Getting started with Testcontainers for Python](https://docs.docker.com/guides/testcontainers-python-getting-started/) Learn how to create a Python application and test database interactions using Testcontainers for Python with a real PostgreSQL instance. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg "Python") Python Testing with Docker 15 minutes [1](https://docs.docker.com/guides/testcontainers-python-getting-started/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-python-getting-started/create-project/) [2](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/) [3](https://docs.docker.com/guides/testcontainers-python-getting-started/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-python-getting-started/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * You'll create a PostgreSQL container using Testcontainers and use it for all the tests. Before each test, you'll delete all customer records so that tests run with a clean database. [Set up pytest fixtures](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/#set-up-pytest-fixtures) ----------------------------------------------------------------------------------------------------------------------------------- This guide uses [pytest fixtures](https://pytest.org/en/stable/how-to/fixtures.html) for setup and teardown logic. A recommended approach is to use [finalizers](https://pytest.org/en/stable/how-to/fixtures.html#adding-finalizers-directly) to guarantee cleanup runs even if setup fails: @pytest.fixture def setup(request): # setup code def cleanup(): # teardown code request.addfinalizer(cleanup) return some_value [Create the test file](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/#create-the-test-file) ------------------------------------------------------------------------------------------------------------------------------- Create a `tests/__init__.py` file with empty content to enable pytest [auto-discovery](https://pytest.org/explanation/goodpractices.html#test-discovery) . Then create `tests/test_customers.py` with the fixtures: import os import pytest from testcontainers.postgres import PostgresContainer from customers import customers postgres = PostgresContainer("postgres:16-alpine") @pytest.fixture(scope="module", autouse=True) def setup(request): postgres.start() def remove_container(): postgres.stop() request.addfinalizer(remove_container) os.environ["DB_CONN"] = postgres.get_connection_url() os.environ["DB_HOST"] = postgres.get_container_host_ip() os.environ["DB_PORT"] = str(postgres.get_exposed_port(5432)) os.environ["DB_USERNAME"] = postgres.username os.environ["DB_PASSWORD"] = postgres.password os.environ["DB_NAME"] = postgres.dbname customers.create_table() @pytest.fixture(scope="function", autouse=True) def setup_data(): customers.delete_all_customers() Here's what the fixtures do: * The `setup` fixture has `scope="module"`, so it runs once for all tests in the file. It starts a PostgreSQL container, sets environment variables with the connection details, and creates the `customers` table. A cleanup function removes the container after all tests complete. * The `setup_data` fixture has `scope="function"`, so it runs before every test. It deletes all records to give each test a clean database. [Write the tests](https://docs.docker.com/guides/testcontainers-python-getting-started/write-tests/#write-the-tests) --------------------------------------------------------------------------------------------------------------------- Add the test functions to the same file: def test_get_all_customers(): customers.create_customer("Siva", "siva@gmail.com") customers.create_customer("James", "james@gmail.com") customers_list = customers.get_all_customers() assert len(customers_list) == 2 def test_get_customer_by_email(): customers.create_customer("John", "john@gmail.com") customer = customers.get_customer_by_email("john@gmail.com") assert customer.name == "John" assert customer.email == "john@gmail.com" * `test_get_all_customers()` inserts two customer records, fetches all customers, and asserts the count. * `test_get_customer_by_email()` inserts a customer, fetches it by email, and asserts the details. Because `setup_data` deletes all records before each test, the tests can run in any order. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-python-getting-started/run-tests/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [C++ language-specific guide](https://docs.docker.com/guides/cpp/) This guide explains how to containerize C++ applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/cplusplus/cplusplus-original.svg "C++") C++ 20 minutes [1](https://docs.docker.com/guides/cpp/multistage/) [Containerize your app using a multi-stage build](https://docs.docker.com/guides/cpp/multistage/) [2](https://docs.docker.com/guides/cpp/containerize/) [Build and run a C++ application using Docker Compose](https://docs.docker.com/guides/cpp/containerize/) [3](https://docs.docker.com/guides/cpp/develop/) [Develop your app](https://docs.docker.com/guides/cpp/develop/) [4](https://docs.docker.com/guides/cpp/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/cpp/configure-ci-cd/) [5](https://docs.docker.com/guides/cpp/deploy/) [Test your deployment](https://docs.docker.com/guides/cpp/deploy/) [6](https://docs.docker.com/guides/cpp/security/) [Supply-chain security](https://docs.docker.com/guides/cpp/security/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for C++ development ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/cpp/develop/#prerequisites) --------------------------------------------------------------------------- Complete [Containerize a C++ application](https://docs.docker.com/guides/cpp/containerize/) . [Overview](https://docs.docker.com/guides/cpp/develop/#overview) ----------------------------------------------------------------- In this section, you'll learn how to set up a development environment for your containerized application. This includes: * Configuring Compose to automatically update your running Compose services as you edit and save your code [Get the sample application](https://docs.docker.com/guides/cpp/develop/#get-the-sample-application) ----------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/dockersamples/c-plus-plus-docker.git && cd c-plus-plus-docker [Automatically update services](https://docs.docker.com/guides/cpp/develop/#automatically-update-services) ----------------------------------------------------------------------------------------------------------- Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Open your `compose.yml` file in an IDE or text editor and then add the Compose Watch instructions. The following example shows how to add Compose Watch to your `compose.yml` file. | | | | --- | --- | | 1
2
3
4
5
6
7
8
9
10
11
12 | services:
ok-api:
image: ok-api
build:
context: .
dockerfile: Dockerfile
ports:
- "8080:8080"
develop:
watch:
- action: rebuild
path: . | Run the following command to run your application with Compose Watch. $ docker compose watch Now, if you modify your `ok_api.cpp` you will see the changes in real time without re-building the image. To test it out, open the `ok_api.cpp` file in your favorite text editor and change the message from `{"Status" : "OK"}` to `{"Status" : "Updated"}`. Save the file and refresh your browser at [http://localhost:8080](http://localhost:8080/) . You should see the updated message. Press `ctrl+c` in the terminal to stop your application. [Summary](https://docs.docker.com/guides/cpp/develop/#summary) --------------------------------------------------------------- In this section, you also learned how to use Compose Watch to automatically rebuild and run your container when you update your code. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose file watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) [Next steps](https://docs.docker.com/guides/cpp/develop/#next-steps) --------------------------------------------------------------------- In the next section, you'll take a look at how to set up a CI/CD pipeline using GitHub Actions. [Configure CI/CD for your C++ application »](https://docs.docker.com/guides/cpp/configure-ci-cd/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Bun language-specific guide](https://docs.docker.com/guides/bun/) Learn how to containerize JavaScript applications with the Bun runtime. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg "JavaScript") JavaScript Docker Hardened Images 10 minutes [1](https://docs.docker.com/guides/bun/containerize/) [Containerize your app](https://docs.docker.com/guides/bun/containerize/) [2](https://docs.docker.com/guides/bun/develop/) [Develop your app](https://docs.docker.com/guides/bun/develop/) [3](https://docs.docker.com/guides/bun/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/bun/configure-ci-cd/) [4](https://docs.docker.com/guides/bun/deploy/) [Test your deployment](https://docs.docker.com/guides/bun/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for Bun development ================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/bun/develop/#prerequisites) --------------------------------------------------------------------------- Complete [Containerize a Bun application](https://docs.docker.com/guides/bun/containerize/) . [Overview](https://docs.docker.com/guides/bun/develop/#overview) ----------------------------------------------------------------- In this section, you'll learn how to set up a development environment for your containerized application. This includes: * Configuring Compose to automatically update your running Compose services as you edit and save your code [Get the sample application](https://docs.docker.com/guides/bun/develop/#get-the-sample-application) ----------------------------------------------------------------------------------------------------- Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/dockersamples/bun-docker.git && cd bun-docker [Automatically update services](https://docs.docker.com/guides/bun/develop/#automatically-update-services) ----------------------------------------------------------------------------------------------------------- Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Open your `compose.yml` file in an IDE or text editor and then add the Compose Watch instructions. The following example shows how to add Compose Watch to your `compose.yml` file. | | | | --- | --- | | 1
2
3
4
5
6
7
8
9
10
11
12 | services:
server:
image: bun-server
build:
context: .
dockerfile: Dockerfile
ports:
- "3000:3000"
develop:
watch:
- action: rebuild
path: . | Run the following command to run your application with Compose Watch. $ docker compose watch Now, if you modify your `server.js` you will see the changes in real time without re-building the image. To test it out, open the `server.js` file in your favorite text editor and change the message from `{"Status" : "OK"}` to `{"Status" : "Updated"}`. Save the file and refresh your browser at `http://localhost:3000`. You should see the updated message. Press `ctrl+c` in the terminal to stop your application. [Summary](https://docs.docker.com/guides/bun/develop/#summary) --------------------------------------------------------------- In this section, you also learned how to use Compose Watch to automatically rebuild and run your container when you update your code. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose file watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) [Next steps](https://docs.docker.com/guides/bun/develop/#next-steps) --------------------------------------------------------------------- In the next section, you'll take a look at how to set up a CI/CD pipeline using GitHub Actions. [Configure CI/CD for your Bun application »](https://docs.docker.com/guides/bun/configure-ci-cd/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations using MockServer](https://docs.docker.com/guides/testcontainers-java-mockserver/) Learn how to create a Spring Boot application that integrates with external REST APIs, then test those integrations using Testcontainers and MockServer. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers MockServer ========================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Mocking external API interactions at the HTTP protocol level, rather than mocking Java methods, lets you verify marshalling and unmarshalling behavior and simulate network issues. Testcontainers provides a MockServer module that starts a [MockServer](https://www.mock-server.com/) instance inside a Docker container. You can then use `MockServerClient` to configure mock expectations. [Write the test](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/#write-the-test) ------------------------------------------------------------------------------------------------------------ Create `AlbumControllerTest.java`: package com.testcontainers.demo; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; import static org.mockserver.model.HttpRequest.request; import static org.mockserver.model.HttpResponse.response; import static org.mockserver.model.JsonBody.json; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockserver.client.MockServerClient; import org.mockserver.model.Header; import org.mockserver.verify.VerificationTimes; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.mockserver.MockServerContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @Testcontainers class AlbumControllerTest { @LocalServerPort private Integer port; @Container static MockServerContainer mockServerContainer = new MockServerContainer("mockserver/mockserver:5.15.0"); static MockServerClient mockServerClient; @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { mockServerClient = new MockServerClient( mockServerContainer.getHost(), mockServerContainer.getServerPort() ); registry.add("photos.api.base-url", mockServerContainer::getEndpoint); } @BeforeEach void setUp() { RestAssured.port = port; mockServerClient.reset(); } @Test void shouldGetAlbumById() { Long albumId = 1L; mockServerClient .when( request().withMethod("GET").withPath("/albums/" + albumId + "/photos") ) .respond( response() .withStatusCode(200) .withHeaders( new Header("Content-Type", "application/json; charset=utf-8") ) .withBody( json( """ [\ {\ "id": 1,\ "title": "accusamus beatae ad facilis cum similique qui sunt",\ "url": "https://via.placeholder.com/600/92c952",\ "thumbnailUrl": "https://via.placeholder.com/150/92c952"\ },\ {\ "id": 2,\ "title": "reprehenderit est deserunt velit ipsam",\ "url": "https://via.placeholder.com/600/771796",\ "thumbnailUrl": "https://via.placeholder.com/150/771796"\ }\ ] """ ) ) ); given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(2)); verifyMockServerRequest("GET", "/albums/" + albumId + "/photos", 1); } @Test void shouldReturn404StatusWhenAlbumNotFound() { Long albumId = 1L; mockServerClient .when( request().withMethod("GET").withPath("/albums/" + albumId + "/photos") ) .respond(response().withStatusCode(404)); given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(404); verifyMockServerRequest("GET", "/albums/" + albumId + "/photos", 1); } private void verifyMockServerRequest(String method, String path, int times) { mockServerClient.verify( request().withMethod(method).withPath(path), VerificationTimes.exactly(times) ); } } Here's what the test does: * `@SpringBootTest` starts the full application on a random port. * The `@Testcontainers` and `@Container` annotations start a `MockServerContainer` and create a `MockServerClient` connected to it. * `@DynamicPropertySource` overrides `photos.api.base-url` to point at the MockServer endpoint, so the application talks to MockServer instead of the real photo service. * `@BeforeEach` resets the `MockServerClient` before every test so that expectations from one test don't affect another. * `shouldGetAlbumById()` configures a mock response for `/albums/{albumId}/photos`, sends a request to the application's `/api/albums/{albumId}` endpoint, and verifies the response body. It also uses `mockServerClient.verify()` to confirm that the expected API call reached MockServer. * `shouldReturn404StatusWhenAlbumNotFound()` configures MockServer to return a 404 status and verifies the application propagates that status to the caller. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing an ASP.NET Core web app with Testcontainers](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/) Learn how to test an ASP.NET Core web app using Testcontainers for .NET with a real Microsoft SQL Server instance instead of SQLite. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg "C#") C# Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/) [2](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/) [3](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * The existing tests use an in-memory SQLite database. While convenient, this doesn't match production behavior. You can replace it with a real Microsoft SQL Server instance managed by Testcontainers. [Add dependencies](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/#add-dependencies) ------------------------------------------------------------------------------------------------------------------- Change to the test project directory and add the SQL Server Entity Framework provider and the Testcontainers MSSQL module: $ cd tests/RazorPagesProject.Tests $ dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 7.0.0 $ dotnet add package Testcontainers.MsSql --version 3.0.0 > Note > > Testcontainers for .NET offers a range of [modules](https://www.nuget.org/profiles/Testcontainers) > that follow best practice configurations. [Create the test class](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/#create-the-test-class) ----------------------------------------------------------------------------------------------------------------------------- Create a `MsSqlTests.cs` file in the `IntegrationTests` directory. This class manages the SQL Server container lifecycle and contains a nested test class. using System.Data.Common; using System.Net; using AngleSharp.Html.Dom; using Microsoft.AspNetCore.Mvc.Testing; using Microsoft.EntityFrameworkCore; using RazorPagesProject.Data; using RazorPagesProject.Tests.Helpers; using Testcontainers.MsSql; using Xunit; namespace RazorPagesProject.Tests.IntegrationTests; public sealed class MsSqlTests : IAsyncLifetime { private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build(); public Task InitializeAsync() { return _msSqlContainer.StartAsync(); } public Task DisposeAsync() { return _msSqlContainer.DisposeAsync().AsTask(); } public sealed class IndexPageTests : IClassFixture, IDisposable { private readonly WebApplicationFactory _webApplicationFactory; private readonly HttpClient _httpClient; public IndexPageTests(MsSqlTests fixture) { var clientOptions = new WebApplicationFactoryClientOptions(); clientOptions.AllowAutoRedirect = false; _webApplicationFactory = new CustomWebApplicationFactory(fixture); _httpClient = _webApplicationFactory.CreateClient(clientOptions); } public void Dispose() { _webApplicationFactory.Dispose(); } [Fact] public async Task Post_DeleteAllMessagesHandler_ReturnsRedirectToRoot() { // Arrange var defaultPage = await _httpClient.GetAsync("/") .ConfigureAwait(false); var document = await HtmlHelpers.GetDocumentAsync(defaultPage) .ConfigureAwait(false); // Act var form = (IHtmlFormElement)document.QuerySelector("form[id='messages']"); var submitButton = (IHtmlButtonElement)document.QuerySelector("button[id='deleteAllBtn']"); var response = await _httpClient.SendAsync(form, submitButton) .ConfigureAwait(false); // Assert Assert.Equal(HttpStatusCode.OK, defaultPage.StatusCode); Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); Assert.Equal("/", response.Headers.Location.OriginalString); } private sealed class CustomWebApplicationFactory : WebApplicationFactory { private readonly string _connectionString; public CustomWebApplicationFactory(MsSqlTests fixture) { _connectionString = fixture._msSqlContainer.GetConnectionString(); } protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { services.Remove(services.SingleOrDefault(service => typeof(DbContextOptions) == service.ServiceType)); services.Remove(services.SingleOrDefault(service => typeof(DbConnection) == service.ServiceType)); services.AddDbContext((_, option) => option.UseSqlServer(_connectionString)); }); } } } } [Understand the test structure](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/#understand-the-test-structure) --------------------------------------------------------------------------------------------------------------------------------------------- ### [Container lifecycle with IAsyncLifetime](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/#container-lifecycle-with-iasynclifetime) The outer `MsSqlTests` class implements `IAsyncLifetime`. xUnit calls `InitializeAsync()` right after creating the class instance, which starts the SQL Server container. After all tests complete, `DisposeAsync()` stops and removes the container. private readonly MsSqlContainer _msSqlContainer = new MsSqlBuilder().Build(); `MsSqlBuilder().Build()` creates a pre-configured Microsoft SQL Server container. Testcontainers modules follow best practices, so you don't need to configure ports, passwords, or startup wait strategies yourself. ### [Nested test class with IClassFixture](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/#nested-test-class-with-iclassfixture) The `IndexPageTests` class is nested inside `MsSqlTests` and implements `IClassFixture`. This gives the test class access to the container's private field and creates a clean hierarchy in the test explorer. ### [Custom WebApplicationFactory](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/#custom-webapplicationfactory) Instead of using the SQLite-based factory, the nested `CustomWebApplicationFactory` retrieves the connection string from the running SQL Server container and passes it to `UseSqlServer()`: private sealed class CustomWebApplicationFactory : WebApplicationFactory { private readonly string _connectionString; public CustomWebApplicationFactory(MsSqlTests fixture) { _connectionString = fixture._msSqlContainer.GetConnectionString(); } protected override void ConfigureWebHost(IWebHostBuilder builder) { builder.ConfigureServices(services => { services.Remove(services.SingleOrDefault(service => typeof(DbContextOptions) == service.ServiceType)); services.Remove(services.SingleOrDefault(service => typeof(DbConnection) == service.ServiceType)); services.AddDbContext((_, option) => option.UseSqlServer(_connectionString)); }); } } This factory: 1. Removes the existing `DbContextOptions` registration 2. Removes the existing `DbConnection` registration 3. Adds a new `ApplicationDbContext` configured with the SQL Server connection string from the Testcontainers-managed container > Note > > The Microsoft SQL Server Docker image isn't compatible with ARM devices, such as Macs with Apple Silicon. You can use the [SqlEdge](https://www.nuget.org/packages/Testcontainers.SqlEdge) > module or [Testcontainers Cloud](https://www.testcontainers.cloud/) > as alternatives. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing Spring Boot Kafka Listener using Testcontainers](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/) Learn how to create a Spring Boot application with a Kafka listener that persists data in MySQL, then test it using Testcontainers Kafka and MySQL modules with Awaitility. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * To test the Kafka listener, you need a running Kafka broker and a MySQL database, plus a started Spring context. Testcontainers spins up both services in Docker containers and `@DynamicPropertySource` connects them to Spring. [Write the test](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/#write-the-test) ------------------------------------------------------------------------------------------------------------------- Create `ProductPriceChangedEventHandlerTest.java`: package com.testcontainers.demo; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import java.math.BigDecimal; import java.time.Duration; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.kafka.core.KafkaTemplate; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.springframework.test.context.TestPropertySource; import org.testcontainers.kafka.ConfluentKafkaContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @SpringBootTest @TestPropertySource( properties = { "spring.kafka.consumer.auto-offset-reset=earliest", "spring.datasource.url=jdbc:tc:mysql:8.0.32:///db", } ) @Testcontainers class ProductPriceChangedEventHandlerTest { @Container static final ConfluentKafkaContainer kafka = new ConfluentKafkaContainer("confluentinc/cp-kafka:7.8.0"); @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("spring.kafka.bootstrap-servers", kafka::getBootstrapServers); } @Autowired private KafkaTemplate kafkaTemplate; @Autowired private ProductRepository productRepository; @BeforeEach void setUp() { Product product = new Product(null, "P100", "Product One", BigDecimal.TEN); productRepository.save(product); } @Test void shouldHandleProductPriceChangedEvent() { ProductPriceChangedEvent event = new ProductPriceChangedEvent( "P100", new BigDecimal("14.50") ); kafkaTemplate.send("product-price-changes", event.productCode(), event); await() .pollInterval(Duration.ofSeconds(3)) .atMost(10, SECONDS) .untilAsserted(() -> { Optional optionalProduct = productRepository.findByCode( "P100" ); assertThat(optionalProduct).isPresent(); assertThat(optionalProduct.get().getCode()).isEqualTo("P100"); assertThat(optionalProduct.get().getPrice()) .isEqualTo(new BigDecimal("14.50")); }); } } Here's what the test does: * `@SpringBootTest` starts the full Spring application context. * The Testcontainers special JDBC URL (`jdbc:tc:mysql:8.0.32:///db`) in `@TestPropertySource` spins up a MySQL container and configures it as the datasource automatically. * `@Testcontainers` and `@Container` manage the lifecycle of the Kafka container. `@DynamicPropertySource` registers the Kafka bootstrap servers with Spring so that the producer and consumer connect to the test container. * `@BeforeEach` creates a `Product` record in the database before each test. * The test sends a `ProductPriceChangedEvent` to the `product-price-changes` topic using `KafkaTemplate`. Spring Boot converts the object to JSON using `JsonSerializer`. * Because Kafka message processing is asynchronous, the test uses [Awaitility](http://www.awaitility.org/) to poll every 3 seconds (up to a maximum of 10 seconds) until the product price in the database matches the expected value. * The property `spring.kafka.consumer.auto-offset-reset` is set to `earliest` so that the listener consumes messages even if they're sent to the topic before the listener is ready. This setting is helpful when running tests. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/) --- # Configuring Testcontainers Cloud in the CI Pipeline | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Mastering Testcontainers Cloud by Docker: streamlining integration testing with containers](https://docs.docker.com/guides/testcontainers-cloud/) Automate, scale, and optimize testing workflows with Testcontainers Cloud Product demo 12 minutes [1](https://docs.docker.com/guides/testcontainers-cloud/why/) [Why Testcontainers Cloud?](https://docs.docker.com/guides/testcontainers-cloud/why/) [2](https://docs.docker.com/guides/testcontainers-cloud/demo-local/) [Setting up Testcontainers Cloud by Docker](https://docs.docker.com/guides/testcontainers-cloud/demo-local/) [3](https://docs.docker.com/guides/testcontainers-cloud/demo-ci/) [Configuring Testcontainers Cloud in the CI Pipeline](https://docs.docker.com/guides/testcontainers-cloud/demo-ci/) [4](https://docs.docker.com/guides/testcontainers-cloud/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/testcontainers-cloud/common-questions/) Resources: * [Testcontainers Guides](https://testcontainers.com/guides) * [Testcontainers Best Practices](https://www.docker.com/blog/testcontainers-best-practices/) * [Simple local development with Testcontainers Desktop](https://testcontainers.com/guides/simple-local-development-with-testcontainers-desktop/) * [Streamlining Local Development with Dev Containers and Testcontainers Cloud](https://www.docker.com/blog/streamlining-local-development-with-dev-containers-and-testcontainers-cloud/) * [Running Testcontainers Tests Using GitHub Actions and Testcontainers Cloud](https://www.docker.com/blog/running-testcontainers-tests-using-github-actions/) * [Testcontainers Cloud on the Docker Blog](https://www.docker.com/search/?_sf_s=testcontainers%20cloud) [« Back to all guides](https://docs.docker.com/guides/) Configuring Testcontainers Cloud in the CI Pipeline =================================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * This demo shows how Testcontainers Cloud can be seamlessly integrated into a Continuous Integration (CI) pipeline using GitHub Workflows, providing a powerful solution for running containerized integration tests without overloading local or CI runner resources. By leveraging GitHub Actions, developers can automate the process of spinning up and managing containers for testing in the cloud, ensuring faster and more reliable test execution. With just a few configuration steps, including setting up Testcontainers Cloud authentication and adding it to your workflow, you can offload container orchestration to the cloud. This approach improves the scalability of your pipeline, ensures consistency across tests, and simplifies resource management, making it an ideal solution for modern, containerized development workflows. * Understand how to set up a GitHub Actions workflow to automate the build and testing of a project. * Learn how to configure Testcontainers Cloud within GitHub Actions to offload containerized testing to the cloud, improving efficiency and resource management. * Explore how Testcontainers Cloud integrates with GitHub workflows to run integration tests that require containerized services, such as databases and message brokers. [Common challenges and questions »](https://docs.docker.com/guides/testcontainers-cloud/common-questions/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing Micronaut Kafka Listener using Testcontainers](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/) Learn how to create a Micronaut application with a Kafka listener that persists data in MySQL, then test it using Testcontainers Kafka and MySQL modules with Awaitility. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * To test the Kafka listener, you need a running Kafka broker and a MySQL database, plus a started Micronaut application context. Testcontainers spins up both services in Docker containers and the `TestPropertyProvider` interface connects them to Micronaut. [Create a Kafka client for testing](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/write-tests/#create-a-kafka-client-for-testing) ------------------------------------------------------------------------------------------------------------------------------------------------------- First, create a `@KafkaClient` interface to publish events in the test: package com.testcontainers.demo; import io.micronaut.configuration.kafka.annotation.KafkaClient; import io.micronaut.configuration.kafka.annotation.KafkaKey; import io.micronaut.configuration.kafka.annotation.Topic; @KafkaClient public interface ProductPriceChangesClient { @Topic("product-price-changes") void send(@KafkaKey String productCode, ProductPriceChangedEvent event); } Key details: * The `@KafkaClient` annotation designates this interface as a Kafka producer. * The `@Topic` annotation specifies the target topic. * The `@KafkaKey` annotation marks the parameter used as the Kafka message key. If no such parameter exists, Micronaut sends the record with a null key. [Write the test](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/write-tests/#write-the-test) ----------------------------------------------------------------------------------------------------------------- Create `ProductPriceChangedEventHandlerTest.java`: package com.testcontainers.demo; import static java.util.concurrent.TimeUnit.SECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import io.micronaut.context.annotation.Property; import io.micronaut.core.annotation.NonNull; import io.micronaut.test.extensions.junit5.annotation.MicronautTest; import io.micronaut.test.support.TestPropertyProvider; import java.math.BigDecimal; import java.time.Duration; import java.util.Collections; import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.testcontainers.kafka.ConfluentKafkaContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @MicronautTest(transactional = false) @Property(name = "datasources.default.driver-class-name", value = "org.testcontainers.jdbc.ContainerDatabaseDriver") @Property(name = "datasources.default.url", value = "jdbc:tc:mysql:8.0.32:///db") @Testcontainers(disabledWithoutDocker = true) @TestInstance(TestInstance.Lifecycle.PER_CLASS) class ProductPriceChangedEventHandlerTest implements TestPropertyProvider { @Container static final ConfluentKafkaContainer kafka = new ConfluentKafkaContainer("confluentinc/cp-kafka:7.8.0"); @Override public @NonNull Map getProperties() { if (!kafka.isRunning()) { kafka.start(); } return Collections.singletonMap("kafka.bootstrap.servers", kafka.getBootstrapServers()); } @Test void shouldHandleProductPriceChangedEvent( ProductPriceChangesClient productPriceChangesClient, ProductRepository productRepository) { Product product = new Product(null, "P100", "Product One", BigDecimal.TEN); Long id = productRepository.save(product).getId(); ProductPriceChangedEvent event = new ProductPriceChangedEvent("P100", new BigDecimal("14.50")); productPriceChangesClient.send(event.productCode(), event); await().pollInterval(Duration.ofSeconds(3)).atMost(10, SECONDS).untilAsserted(() -> { Optional optionalProduct = productRepository.findByCode("P100"); assertThat(optionalProduct).isPresent(); assertThat(optionalProduct.get().getCode()).isEqualTo("P100"); assertThat(optionalProduct.get().getPrice()).isEqualTo(new BigDecimal("14.50")); }); productRepository.deleteById(id); } } Here's what the test does: * `@MicronautTest` initializes the Micronaut application context and the embedded server. Setting `transactional` to `false` prevents each test method from running inside a rolled-back transaction, which is necessary because the Kafka listener processes messages in a separate thread. * The `@Property` annotations override the datasource driver and URL to use the Testcontainers special JDBC URL (`jdbc:tc:mysql:8.0.32:///db`). This spins up a MySQL container and configures it as the datasource automatically. * `@Testcontainers` and `@Container` manage the Kafka container lifecycle. The `TestPropertyProvider` interface registers the Kafka bootstrap servers with Micronaut so that the producer and consumer connect to the test container. * `@TestInstance(TestInstance.Lifecycle.PER_CLASS)` creates a single test instance for all test methods, which is required when implementing `TestPropertyProvider`. * The test creates a `Product` record in the database, then sends a `ProductPriceChangedEvent` to the `product-price-changes` topic using the `ProductPriceChangesClient`. * Because Kafka message processing is asynchronous, the test uses [Awaitility](http://www.awaitility.org/) to poll every 3 seconds (up to a maximum of 10 seconds) until the product price in the database matches the expected value. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-micronaut-kafka/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing AWS service integrations using LocalStack](https://docs.docker.com/guides/testcontainers-java-aws-localstack/) Learn how to create a Spring Boot application with Spring Cloud AWS, then test S3 and SQS integrations using Testcontainers and LocalStack. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-aws-localstack/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-aws-localstack/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-aws-localstack/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-aws-localstack/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-aws-localstack/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * To test the application, you need a running LocalStack instance that emulates the AWS S3 and SQS services. Testcontainers spins up LocalStack in a Docker container and `@DynamicPropertySource` connects it to Spring Cloud AWS. [Configure the test container](https://docs.docker.com/guides/testcontainers-java-aws-localstack/write-tests/#configure-the-test-container) -------------------------------------------------------------------------------------------------------------------------------------------- You can start a LocalStack container and configure the Spring Cloud AWS properties to talk to it instead of actual AWS services. The properties you need to set are: spring.cloud.aws.s3.endpoint=http://localhost:4566 spring.cloud.aws.sqs.endpoint=http://localhost:4566 spring.cloud.aws.credentials.access-key=noop spring.cloud.aws.credentials.secret-key=noop spring.cloud.aws.region.static=us-east-1 For testing, use an ephemeral container that starts on a random available port so that you can run multiple builds in CI in parallel without port conflicts. [Write the test](https://docs.docker.com/guides/testcontainers-java-aws-localstack/write-tests/#write-the-test) ---------------------------------------------------------------------------------------------------------------- Create `MessageListenerTest.java`: package com.testcontainers.demo; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.S3; import static org.testcontainers.containers.localstack.LocalStackContainer.Service.SQS; import java.io.IOException; import java.time.Duration; import java.util.UUID; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.containers.localstack.LocalStackContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.testcontainers.utility.DockerImageName; @SpringBootTest @Testcontainers class MessageListenerTest { @Container static LocalStackContainer localStack = new LocalStackContainer( DockerImageName.parse("localstack/localstack:3.0") ); static final String BUCKET_NAME = UUID.randomUUID().toString(); static final String QUEUE_NAME = UUID.randomUUID().toString(); @DynamicPropertySource static void overrideProperties(DynamicPropertyRegistry registry) { registry.add("app.bucket", () -> BUCKET_NAME); registry.add("app.queue", () -> QUEUE_NAME); registry.add( "spring.cloud.aws.region.static", () -> localStack.getRegion() ); registry.add( "spring.cloud.aws.credentials.access-key", () -> localStack.getAccessKey() ); registry.add( "spring.cloud.aws.credentials.secret-key", () -> localStack.getSecretKey() ); registry.add( "spring.cloud.aws.s3.endpoint", () -> localStack.getEndpointOverride(S3).toString() ); registry.add( "spring.cloud.aws.sqs.endpoint", () -> localStack.getEndpointOverride(SQS).toString() ); } @BeforeAll static void beforeAll() throws IOException, InterruptedException { localStack.execInContainer("awslocal", "s3", "mb", "s3://" + BUCKET_NAME); localStack.execInContainer( "awslocal", "sqs", "create-queue", "--queue-name", QUEUE_NAME ); } @Autowired StorageService storageService; @Autowired MessageSender publisher; @Autowired ApplicationProperties properties; @Test void shouldHandleMessageSuccessfully() { Message message = new Message(UUID.randomUUID(), "Hello World"); publisher.publish(properties.queue(), message); await() .pollInterval(Duration.ofSeconds(2)) .atMost(Duration.ofSeconds(10)) .ignoreExceptions() .untilAsserted(() -> { String msg = storageService.downloadAsString( properties.bucket(), message.uuid().toString() ); assertThat(msg).isEqualTo("Hello World"); }); } } Here's what the test does: * `@SpringBootTest` starts the full Spring application context. * The Testcontainers JUnit 5 annotations `@Testcontainers` and `@Container` manage the lifecycle of a `LocalStackContainer` instance. * `@DynamicPropertySource` obtains the dynamic S3 and SQS endpoint URLs, region, access key, and secret key from the container, and registers them as Spring Cloud AWS configuration properties. * `@BeforeAll` creates the required SQS queue and S3 bucket using the `awslocal` CLI tool that comes pre-installed in the LocalStack Docker image. The `localStack.execInContainer()` API runs commands inside the container. * `shouldHandleMessageSuccessfully()` publishes a `Message` to the SQS queue. The listener receives the message and stores its content in the S3 bucket with the UUID as the key. Awaitility waits up to 10 seconds for the expected content to appear in the bucket. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-aws-localstack/run-tests/) --- # Linting and typing | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Python language-specific guide](https://docs.docker.com/guides/python/) This guide explains how to containerize Python applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg "Python") Python Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/python/containerize/) [Containerize your app](https://docs.docker.com/guides/python/containerize/) [2](https://docs.docker.com/guides/python/develop/) [Develop your app](https://docs.docker.com/guides/python/develop/) [3](https://docs.docker.com/guides/python/lint-format-typing/) [Linting and typing](https://docs.docker.com/guides/python/lint-format-typing/) [4](https://docs.docker.com/guides/python/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/python/configure-github-actions/) [5](https://docs.docker.com/guides/python/deploy/) [Test your deployment](https://docs.docker.com/guides/python/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Linting, formatting, and type checking for Python ================================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/python/lint-format-typing/#prerequisites) ----------------------------------------------------------------------------------------- Complete [Develop your app](https://docs.docker.com/guides/python/develop/) . [Overview](https://docs.docker.com/guides/python/lint-format-typing/#overview) ------------------------------------------------------------------------------- In this section, you'll learn how to set up code quality tools for your Python application. This includes: * Linting and formatting with Ruff * Static type checking with Pyright * Automating checks with pre-commit hooks [Linting and formatting with Ruff](https://docs.docker.com/guides/python/lint-format-typing/#linting-and-formatting-with-ruff) ------------------------------------------------------------------------------------------------------------------------------- Ruff is an extremely fast Python linter and formatter written in Rust. It replaces multiple tools like flake8, isort, and black with a single unified tool. Before using Ruff, install it in your Python environment: pip install ruff If you're using a virtual environment, make sure it is activated so the `ruff` command is available when you run the commands below. Create a `pyproject.toml` file: [tool.ruff] target-version = "py312" [tool.ruff.lint] select = [\ "E", # pycodestyle errors\ "W", # pycodestyle warnings\ "F", # pyflakes\ "I", # isort\ "B", # flake8-bugbear\ "C4", # flake8-comprehensions\ "UP", # pyupgrade\ "ARG001", # unused arguments in functions\ ] ignore = [\ "E501", # line too long, handled by black\ "B008", # do not perform function calls in argument defaults\ "W191", # indentation contains tabs\ "B904", # Allow raising exceptions without from e, for HTTPException\ ] ### [Using Ruff](https://docs.docker.com/guides/python/lint-format-typing/#using-ruff) Run these commands to check and format your code: # Check for errors ruff check . # Automatically fix fixable errors ruff check --fix . # Format code ruff format . [Type checking with Pyright](https://docs.docker.com/guides/python/lint-format-typing/#type-checking-with-pyright) ------------------------------------------------------------------------------------------------------------------- Pyright is a fast static type checker for Python that works well with modern Python features. Add `Pyright` configuration in `pyproject.toml`: [tool.pyright] typeCheckingMode = "strict" pythonVersion = "3.12" exclude = [".venv"] ### [Running Pyright](https://docs.docker.com/guides/python/lint-format-typing/#running-pyright) To check your code for type errors: pyright [Setting up pre-commit hooks](https://docs.docker.com/guides/python/lint-format-typing/#setting-up-pre-commit-hooks) --------------------------------------------------------------------------------------------------------------------- Pre-commit hooks automatically run checks before each commit. The following `.pre-commit-config.yaml` snippet sets up Ruff: https: https://github.com/charliermarsh/ruff-pre-commit rev: v0.2.2 hooks: - id: ruff args: [--fix] - id: ruff-format To install and use: pre-commit install git commit -m "Test commit" # Automatically runs checks [Summary](https://docs.docker.com/guides/python/lint-format-typing/#summary) ----------------------------------------------------------------------------- In this section, you learned how to: * Configure and use Ruff for linting and formatting * Set up Pyright for static type checking * Automate checks with pre-commit hooks These tools help maintain code quality and catch errors early in development. [Next steps](https://docs.docker.com/guides/python/lint-format-typing/#next-steps) ----------------------------------------------------------------------------------- * [Configure GitHub Actions](https://docs.docker.com/guides/python/configure-github-actions/) to run these checks automatically * Customize linting rules to match your team's style preferences * Explore advanced type checking features [Automate your builds with GitHub Actions »](https://docs.docker.com/guides/python/configure-github-actions/) --- # Containerize your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Python language-specific guide](https://docs.docker.com/guides/python/) This guide explains how to containerize Python applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg "Python") Python Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/python/containerize/) [Containerize your app](https://docs.docker.com/guides/python/containerize/) [2](https://docs.docker.com/guides/python/develop/) [Develop your app](https://docs.docker.com/guides/python/develop/) [3](https://docs.docker.com/guides/python/lint-format-typing/) [Linting and typing](https://docs.docker.com/guides/python/lint-format-typing/) [4](https://docs.docker.com/guides/python/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/python/configure-github-actions/) [5](https://docs.docker.com/guides/python/deploy/) [Test your deployment](https://docs.docker.com/guides/python/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Containerize a Python application ================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/python/containerize/#prerequisites) ----------------------------------------------------------------------------------- * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have a [Git client](https://git-scm.com/downloads) . The examples in this section use a command-line based Git client, but you can use any client. [Overview](https://docs.docker.com/guides/python/containerize/#overview) ------------------------------------------------------------------------- This section walks you through containerizing and running a Python application. [Get the sample application](https://docs.docker.com/guides/python/containerize/#get-the-sample-application) ------------------------------------------------------------------------------------------------------------- The sample application uses the popular [FastAPI](https://fastapi.tiangolo.com/) framework. Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command to clone the repository: $ git clone https://github.com/estebanx64/python-docker-example && cd python-docker-example [Initialize Docker assets](https://docs.docker.com/guides/python/containerize/#initialize-docker-assets) --------------------------------------------------------------------------------------------------------- Now that you have an application, you can create the necessary Docker assets to containerize your application. You can use Docker Desktop's built-in Docker Init feature to help streamline the process, or you can manually create the assets. Use Docker Init Using the official Docker image Using Docker Hardened Image Inside the `python-docker-example` directory, run the `docker init` command. `docker init` provides some default configuration, but you'll need to answer a few questions about your application. For example, this application uses FastAPI to run. Refer to the following example to answer the prompts from `docker init` and use the same answers for your prompts. Before editing your Dockerfile, you need to choose a base image. You can use the [Python Docker Official Image](https://hub.docker.com/_/python) , or a [Docker Hardened Image (DHI)](https://hub.docker.com/hardened-images/catalog/dhi/python) . Docker Hardened Images (DHIs) are minimal, secure, and production-ready base images maintained by Docker. They help reduce vulnerabilities and simplify compliance. For more details, see [Docker Hardened Images](https://docs.docker.com/dhi/) . $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? Python ? What version of Python do you want to use? 3.12 ? What port do you want your app to listen on? 8000 ? What is the command to run your app? python3 -m uvicorn app:app --host=0.0.0.0 --port=8000 Create a file named `.gitignore` with the following contents. .gitignore Show more # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ Hide If you don't have Docker Desktop installed or prefer creating the assets manually, you can create the following files in your project directory. Create a file named `Dockerfile` with the following contents. Dockerfile Show more # syntax=docker/dockerfile:1 # Comments are provided throughout this file to help you get started. # If you need more help, visit the Dockerfile reference guide at # https://docs.docker.com/go/dockerfile-reference/ # Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 # This Dockerfile uses Python Docker Official Image ARG PYTHON_VERSION=3.12 FROM python:${PYTHON_VERSION}-slim # Prevents Python from writing pyc files. ENV PYTHONDONTWRITEBYTECODE=1 # Keeps Python from buffering stdout and stderr to avoid situations where # the application crashes without emitting any logs due to buffering. ENV PYTHONUNBUFFERED=1 WORKDIR /app # Create a non-privileged user that the app will run under. # See https://docs.docker.com/go/dockerfile-user-best-practices/ ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. # Leverage a bind mount to requirements.txt to avoid having to copy them into # into this layer. RUN --mount=type=cache,target=/root/.cache/pip \ --mount=type=bind,source=requirements.txt,target=requirements.txt \ python -m pip install -r requirements.txt # Switch to the non-privileged user to run the application. USER appuser # Copy the source code into the container. COPY . . # Expose the port that the application listens on. EXPOSE 8000 # Run the application. CMD ["python3", "-m", "uvicorn", "app:app", "--host=0.0.0.0", "--port=8000"] Hide Create a file named `compose.yaml` with the following contents. compose.yaml Show more # Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker Compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "server". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: server: build: context: . ports: - 8000:8000 Hide Create a file named `.dockerignore` with the following contents. .dockerignore Show more # Include any files or directories that you don't want to be copied to your # container here (e.g., local build artifacts, temporary files, etc.). # # For more help, visit the .dockerignore file reference guide at # https://docs.docker.com/go/build-context-dockerignore/ **/.DS_Store **/__pycache__ **/.venv **/.classpath **/.dockerignore **/.env **/.git **/.gitignore **/.project **/.settings **/.toolstarget **/.vs **/.vscode **/*.*proj.user **/*.dbmdl **/*.jfm **/bin **/charts **/docker-compose* **/compose.y*ml **/Dockerfile* **/node_modules **/npm-debug.log **/obj **/secrets.dev.yaml **/values.dev.yaml LICENSE README.md Hide Create a file named `.gitignore` with the following contents. .gitignore Show more # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ Hide Docker Hardened Images (DHIs) are available for Python in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/python) . Docker Hardened Images are freely available to everyone with no subscription required. You can pull and use them like any other Docker image after signing in to the DHI registry. For more information, see the [DHI quickstart](https://docs.docker.com/dhi/get-started/) guide. 1. Sign in to the DHI registry: $ docker login dhi.io 2. Pull the Python DHI (check the catalog for available versions): $ docker pull dhi.io/python:3.12.12-debian13-fips-dev Create a file named `Dockerfile` with the following contents. Dockerfile Show more # syntax=docker/dockerfile:1 # Comments are provided throughout this file to help you get started. # If you need more help, visit the Dockerfile reference guide at # https://docs.docker.com/go/dockerfile-reference/ # Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 # This Dockerfile uses Docker Hardened Images (DHI) for enhanced security. # For more information, see https://docs.docker.com/dhi/ ARG PYTHON_VERSION=3.12.12-debian13-fips-dev FROM dhi.io/python:${PYTHON_VERSION} # Prevents Python from writing pyc files. ENV PYTHONDONTWRITEBYTECODE=1 # Keeps Python from buffering stdout and stderr to avoid situations where # the application crashes without emitting any logs due to buffering. ENV PYTHONUNBUFFERED=1 #Add dependencies for adduser RUN apt update -y && apt install adduser -y WORKDIR /app # Create a non-privileged user that the app will run under. # See https://docs.docker.com/go/dockerfile-user-best-practices/ ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. # Leverage a bind mount to requirements.txt to avoid having to copy them into # into this layer. RUN --mount=type=cache,target=/root/.cache/pip \ --mount=type=bind,source=requirements.txt,target=requirements.txt \ python -m pip install -r requirements.txt # Switch to the non-privileged user to run the application. USER appuser # Copy the source code into the container. COPY . . # Expose the port that the application listens on. EXPOSE 8000 # Run the application. CMD ["python3", "-m", "uvicorn", "app:app", "--host=0.0.0.0", "--port=8000"] Hide Create a file named `compose.yaml` with the following contents. compose.yaml Show more # Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker Compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "server". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: server: build: context: . ports: - 8000:8000 Hide Create a file named `.dockerignore` with the following contents. .dockerignore Show more # Include any files or directories that you don't want to be copied to your # container here (e.g., local build artifacts, temporary files, etc.). # # For more help, visit the .dockerignore file reference guide at # https://docs.docker.com/go/build-context-dockerignore/ **/.DS_Store **/__pycache__ **/.venv **/.classpath **/.dockerignore **/.env **/.git **/.gitignore **/.project **/.settings **/.toolstarget **/.vs **/.vscode **/*.*proj.user **/*.dbmdl **/*.jfm **/bin **/charts **/docker-compose* **/compose.y*ml **/Dockerfile* **/node_modules **/npm-debug.log **/obj **/secrets.dev.yaml **/values.dev.yaml LICENSE README.md Hide Create a file named `.gitignore` with the following contents. .gitignore Show more # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ Hide You should now have the following contents in your `python-docker-example` directory. ├── python-docker-example/ │ ├── app.py │ ├── requirements.txt │ ├── .dockerignore │ ├── .gitignore │ ├── compose.yaml │ ├── Dockerfile │ └── README.md To learn more about the files, see the following: * [Dockerfile](https://docs.docker.com/reference/dockerfile/) * [.dockerignore](https://docs.docker.com/reference/dockerfile/#dockerignore-file) * [.gitignore](https://git-scm.com/docs/gitignore) * [compose.yaml](https://docs.docker.com/reference/compose-file/) [Run the application](https://docs.docker.com/guides/python/containerize/#run-the-application) ----------------------------------------------------------------------------------------------- Inside the `python-docker-example` directory, run the following command in a terminal. $ docker compose up --build Open a browser and view the application at [http://localhost:8000](http://localhost:8000/) . You should see a simple FastAPI application. In the terminal, press `ctrl`+`c` to stop the application. ### [Run the application in the background](https://docs.docker.com/guides/python/containerize/#run-the-application-in-the-background) You can run the application detached from the terminal by adding the `-d` option. Inside the `python-docker-example` directory, run the following command in a terminal. $ docker compose up --build -d Open a browser and view the application at [http://localhost:8000](http://localhost:8000/) . To see the OpenAPI docs you can go to [http://localhost:8000/docs](http://localhost:8000/docs) . You should see a simple FastAPI application. In the terminal, run the following command to stop the application. $ docker compose down For more information about Compose commands, see the [Compose CLI reference](https://docs.docker.com/reference/cli/docker/compose/) . [Summary](https://docs.docker.com/guides/python/containerize/#summary) ----------------------------------------------------------------------- In this section, you learned how you can containerize and run your Python application using Docker. Related information: * [Docker Compose overview](https://docs.docker.com/compose/) [Next steps](https://docs.docker.com/guides/python/containerize/#next-steps) ----------------------------------------------------------------------------- In the next section, you'll take a look at how to set up a local development environment using Docker containers. [Use containers for Python development »](https://docs.docker.com/guides/python/develop/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Securing Spring Boot microservice using Keycloak and Testcontainers](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/) Learn how to create an OAuth 2.0 Resource Server using Spring Boot, secure API endpoints with Keycloak, and test the application using the Testcontainers Keycloak module. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 30 minutes [1](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * To test the secured API endpoints, you need a running Keycloak instance and a PostgreSQL database, plus a started Spring context. Testcontainers spins up both services in Docker containers and connects them to Spring through dynamic property registration. [Configure the test containers](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/#configure-the-test-containers) ---------------------------------------------------------------------------------------------------------------------------------------------------- Spring Boot's Testcontainers support lets you declare containers as beans. For Keycloak, `@ServiceConnection` isn't available, but you can use `DynamicPropertyRegistry` to set the JWT issuer URI dynamically. Create `ContainersConfig.java` under `src/test/java`: package com.testcontainers.products; import dasniko.testcontainers.keycloak.KeycloakContainer; import org.springframework.boot.test.context.TestConfiguration; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.context.annotation.Bean; import org.springframework.test.context.DynamicPropertyRegistry; import org.testcontainers.postgresql.PostgreSQLContainer; @TestConfiguration(proxyBeanMethods = false) public class ContainersConfig { static String POSTGRES_IMAGE = "postgres:16-alpine"; static String KEYCLOAK_IMAGE = "quay.io/keycloak/keycloak:25.0"; static String realmImportFile = "/keycloaktcdemo-realm.json"; static String realmName = "keycloaktcdemo"; @Bean @ServiceConnection PostgreSQLContainer postgres() { return new PostgreSQLContainer(POSTGRES_IMAGE); } @Bean KeycloakContainer keycloak(DynamicPropertyRegistry registry) { var keycloak = new KeycloakContainer(KEYCLOAK_IMAGE) .withRealmImportFile(realmImportFile); registry.add( "spring.security.oauth2.resourceserver.jwt.issuer-uri", () -> keycloak.getAuthServerUrl() + "/realms/" + realmName ); return keycloak; } } This configuration: * Declares a `PostgreSQLContainer` bean with `@ServiceConnection`, which starts a PostgreSQL container and automatically registers the datasource properties. * Declares a `KeycloakContainer` bean using the `quay.io/keycloak/keycloak:25.0` image, imports the realm configuration file, and dynamically registers the JWT issuer URI from the Keycloak container's auth server URL. [Write the test](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/#write-the-test) ---------------------------------------------------------------------------------------------------------------------- Create `ProductControllerTests.java`: package com.testcontainers.products.api; import static io.restassured.RestAssured.given; import static io.restassured.RestAssured.when; import static java.util.Collections.singletonList; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import com.fasterxml.jackson.annotation.JsonProperty; import com.testcontainers.products.ContainersConfig; import io.restassured.RestAssured; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.context.annotation.Import; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; @SpringBootTest(webEnvironment = RANDOM_PORT) @Import(ContainersConfig.class) class ProductControllerTests { static final String GRANT_TYPE_CLIENT_CREDENTIALS = "client_credentials"; static final String CLIENT_ID = "product-service"; static final String CLIENT_SECRET = "jTJJqdzeCSt3DmypfHZa42vX8U9rQKZ9"; @LocalServerPort private int port; @Autowired OAuth2ResourceServerProperties oAuth2ResourceServerProperties; @BeforeEach void setup() { RestAssured.port = port; } @Test void shouldGetProductsWithoutAuthToken() { when().get("/api/products").then().statusCode(200); } @Test void shouldGetUnauthorizedWhenCreateProductWithoutAuthToken() { given() .contentType("application/json") .body( """ { "title": "New Product", "description": "Brand New Product" } """ ) .when() .post("/api/products") .then() .statusCode(401); } @Test void shouldCreateProductWithAuthToken() { String token = getToken(); given() .header("Authorization", "Bearer " + token) .contentType("application/json") .body( """ { "title": "New Product", "description": "Brand New Product" } """ ) .when() .post("/api/products") .then() .statusCode(201); } private String getToken() { RestTemplate restTemplate = new RestTemplate(); HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); MultiValueMap map = new LinkedMultiValueMap<>(); map.put("grant_type", singletonList(GRANT_TYPE_CLIENT_CREDENTIALS)); map.put("client_id", singletonList(CLIENT_ID)); map.put("client_secret", singletonList(CLIENT_SECRET)); String authServerUrl = oAuth2ResourceServerProperties.getJwt().getIssuerUri() + "/protocol/openid-connect/token"; var request = new HttpEntity<>(map, httpHeaders); KeyCloakToken token = restTemplate.postForObject( authServerUrl, request, KeyCloakToken.class ); assert token != null; return token.accessToken(); } record KeyCloakToken(@JsonProperty("access_token") String accessToken) {} } Here's what the tests cover: * `shouldGetProductsWithoutAuthToken()` invokes `GET /api/products` without an `Authorization` header. Because this endpoint is configured to permit unauthenticated access, the response status code is 200. * `shouldGetUnauthorizedWhenCreateProductWithoutAuthToken()` invokes the secured `POST /api/products` endpoint without an `Authorization` header and asserts the response status code is 401 (Unauthorized). * `shouldCreateProductWithAuthToken()` first obtains an `access_token` using the Client Credentials flow. It then includes the token as a Bearer token in the `Authorization` header when invoking `POST /api/products` and asserts the response status code is 201 (Created). The `getToken()` helper method requests an access token from the Keycloak token endpoint using the client ID and client secret that were configured in the exported realm. [Use Testcontainers for local development](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/write-tests/#use-testcontainers-for-local-development) -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Spring Boot's Testcontainers support also works for local development. Create `TestApplication.java` under `src/test/java`: package com.testcontainers.products; import org.springframework.boot.SpringApplication; public class TestApplication { public static void main(String[] args) { SpringApplication .from(Application::main) .with(ContainersConfig.class) .run(args); } } Run `TestApplication.java` from your IDE instead of the main `Application.java`. It starts the containers defined in `ContainersConfig` and configures the application to use the dynamically registered properties, so you don't have to install or configure PostgreSQL and Keycloak manually. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-keycloak-spring-boot/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Working with jOOQ and Flyway using Testcontainers](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/) Generate typesafe jOOQ code from a real PostgreSQL database managed by Flyway migrations, then test repositories using Testcontainers. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with Testcontainers =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Before writing the tests, create an SQL script to seed test data at `src/test/resources/test-data.sql`: DELETE FROM comments; DELETE FROM posts; DELETE FROM users; INSERT INTO users(id, name, email) VALUES (1, 'Siva', 'siva@gmail.com'), (2, 'Oleg', 'oleg@gmail.com'); INSERT INTO posts(id, title, content, created_by, created_at) VALUES (1, 'Post 1 Title', 'Post 1 content', 1, CURRENT_TIMESTAMP), (2, 'Post 2 Title', 'Post 2 content', 2, CURRENT_TIMESTAMP); INSERT INTO comments(id, name, content, post_id, created_at) VALUES (1, 'Ron', 'Comment 1', 1, CURRENT_TIMESTAMP), (2, 'James', 'Comment 2', 1, CURRENT_TIMESTAMP), (3, 'Robert', 'Comment 3', 2, CURRENT_TIMESTAMP); [Test with the @JooqTest slice](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/#test-with-the-jooqtest-slice) ------------------------------------------------------------------------------------------------------------------------------------------ The `@JooqTest` annotation loads only the persistence layer components and auto-configures jOOQ's `DSLContext`. Use the Testcontainers special JDBC URL to start a Postgres container. Create `UserRepositoryJooqTest.java`: package com.testcontainers.demo.domain; import static org.assertj.core.api.Assertions.assertThat; import org.jooq.DSLContext; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.jooq.JooqTest; import org.springframework.test.context.jdbc.Sql; @JooqTest( properties = { "spring.test.database.replace=none", "spring.datasource.url=jdbc:tc:postgresql:16-alpine:///db", } ) @Sql("/test-data.sql") class UserRepositoryJooqTest { @Autowired DSLContext dsl; UserRepository repository; @BeforeEach void setUp() { this.repository = new UserRepository(dsl); } @Test void shouldCreateUserSuccessfully() { User user = new User(null, "John", "john@gmail.com"); User savedUser = repository.createUser(user); assertThat(savedUser.id()).isNotNull(); assertThat(savedUser.name()).isEqualTo("John"); assertThat(savedUser.email()).isEqualTo("john@gmail.com"); } @Test void shouldGetUserByEmail() { User user = repository.getUserByEmail("siva@gmail.com").orElseThrow(); assertThat(user.id()).isEqualTo(1L); assertThat(user.name()).isEqualTo("Siva"); assertThat(user.email()).isEqualTo("siva@gmail.com"); } } Here's what the test does: * `@JooqTest` loads only the persistence layer and auto-configures `DSLContext`. * The Testcontainers special JDBC URL (`jdbc:tc:postgresql:16-alpine:///db`) starts a PostgreSQL container automatically. * Because `flyway-core` is on the classpath, Spring Boot runs the Flyway migrations from `src/main/resources/db/migration` on startup. * `@Sql("/test-data.sql")` loads the test data before each test. * The `UserRepository` is instantiated manually with the injected `DSLContext`. [Integration test with @SpringBootTest](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/#integration-test-with-springboottest) ---------------------------------------------------------------------------------------------------------------------------------------------------------- For a full integration test, use `@SpringBootTest` with the Testcontainers `@ServiceConnection` support introduced in Spring Boot 3.1. Create `UserRepositoryTest.java`: package com.testcontainers.demo.domain; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.testcontainers.service.connection.ServiceConnection; import org.springframework.test.context.jdbc.Sql; import org.testcontainers.postgresql.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @SpringBootTest @Sql("/test-data.sql") @Testcontainers class UserRepositoryTest { @Container @ServiceConnection static PostgreSQLContainer postgres = new PostgreSQLContainer( "postgres:16-alpine" ); @Autowired UserRepository repository; @Test void shouldCreateUserSuccessfully() { User user = new User(null, "John", "john@gmail.com"); User savedUser = repository.createUser(user); assertThat(savedUser.id()).isNotNull(); assertThat(savedUser.name()).isEqualTo("John"); assertThat(savedUser.email()).isEqualTo("john@gmail.com"); } @Test void shouldGetUserByEmail() { User user = repository.getUserByEmail("siva@gmail.com").orElseThrow(); assertThat(user.id()).isEqualTo(1L); assertThat(user.name()).isEqualTo("Siva"); assertThat(user.email()).isEqualTo("siva@gmail.com"); } } Here's what the test does: * `@SpringBootTest` loads the entire application context, so `UserRepository` is injected directly. * `@Testcontainers` and `@Container` manage the PostgreSQL container lifecycle. * `@ServiceConnection` auto-configures the datasource properties from the running container, replacing the need for `@DynamicPropertySource`. * `@Sql("/test-data.sql")` initializes the test data. [Test PostRepository](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/write-tests/#test-postrepository) ----------------------------------------------------------------------------------------------------------------------- Test the `PostRepository` that fetches complex object graphs using the Testcontainers special JDBC URL. Create `PostRepositoryTest.java`: package com.testcontainers.demo.domain; import static org.assertj.core.api.Assertions.assertThat; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; @SpringBootTest( properties = { "spring.test.database.replace=none", "spring.datasource.url=jdbc:tc:postgresql:16-alpine:///db", } ) @Sql("/test-data.sql") class PostRepositoryTest { @Autowired PostRepository repository; @Test void shouldGetPostById() { Post post = repository.getPostById(1L).orElseThrow(); assertThat(post.id()).isEqualTo(1L); assertThat(post.title()).isEqualTo("Post 1 Title"); assertThat(post.content()).isEqualTo("Post 1 content"); assertThat(post.createdBy().id()).isEqualTo(1L); assertThat(post.createdBy().name()).isEqualTo("Siva"); assertThat(post.createdBy().email()).isEqualTo("siva@gmail.com"); assertThat(post.comments()).hasSize(2); } } This test verifies that `getPostById` loads the post along with its creator and comments in a single query using jOOQ's MULTISET feature. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-jooq-flyway/run-tests/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Python language-specific guide](https://docs.docker.com/guides/python/) This guide explains how to containerize Python applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/python/python-original.svg "Python") Python Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/python/containerize/) [Containerize your app](https://docs.docker.com/guides/python/containerize/) [2](https://docs.docker.com/guides/python/develop/) [Develop your app](https://docs.docker.com/guides/python/develop/) [3](https://docs.docker.com/guides/python/lint-format-typing/) [Linting and typing](https://docs.docker.com/guides/python/lint-format-typing/) [4](https://docs.docker.com/guides/python/configure-github-actions/) [Automate your builds with GitHub Actions](https://docs.docker.com/guides/python/configure-github-actions/) [5](https://docs.docker.com/guides/python/deploy/) [Test your deployment](https://docs.docker.com/guides/python/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for Python development ===================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/python/develop/#prerequisites) ------------------------------------------------------------------------------ Complete [Containerize a Python application](https://docs.docker.com/guides/python/containerize/) . [Overview](https://docs.docker.com/guides/python/develop/#overview) -------------------------------------------------------------------- In this section, you'll learn how to set up a development environment for your containerized application. This includes: * Adding a local database and persisting data * Configuring Compose to automatically update your running Compose services as you edit and save your code [Get the sample application](https://docs.docker.com/guides/python/develop/#get-the-sample-application) -------------------------------------------------------------------------------------------------------- You'll need to clone a new repository to get a sample application that includes logic to connect to the database. 1. Change to a directory where you want to clone the repository and run the following command. $ git clone https://github.com/estebanx64/python-docker-dev-example 2. In the cloned repository's directory, manually create the Docker assets or run `docker init` to create the necessary Docker assets. Use Docker Init Manually create assets In the cloned repository's directory, run `docker init`. Refer to the following example to answer the prompts from `docker init`. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? Python ? What version of Python do you want to use? 3.12 ? What port do you want your app to listen on? 8001 ? What is the command to run your app? python3 -m uvicorn app:app --host=0.0.0.0 --port=8001 Create a file named `.gitignore` with the following contents. .gitignore Show more # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ Hide If you don't have Docker Desktop installed or prefer creating the assets manually, you can create the following files in your project directory. Create a file named `Dockerfile` with the following contents. Dockerfile Show more # syntax=docker/dockerfile:1 # Comments are provided throughout this file to help you get started. # If you need more help, visit the Dockerfile reference guide at # https://docs.docker.com/go/dockerfile-reference/ # Want to help us make this template better? Share your feedback here: https:// forms.gle/ybq9Krt8jtBL3iCk7 ARG PYTHON_VERSION=3.12 FROM python:${PYTHON_VERSION}-slim # Prevents Python from writing pyc files. ENV PYTHONDONTWRITEBYTECODE=1 # Keeps Python from buffering stdout and stderr to avoid situations where # the application crashes without emitting any logs due to buffering. ENV PYTHONUNBUFFERED=1 WORKDIR /app # Create a non-privileged user that the app will run under. # See https://docs.docker.com/go/dockerfile-user-best-practices/ ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser # Download dependencies as a separate step to take advantage of Docker's caching. # Leverage a cache mount to /root/.cache/pip to speed up subsequent builds. # Leverage a bind mount to requirements.txt to avoid having to copy them into # into this layer. RUN --mount=type=cache,target=/root/.cache/pip \ --mount=type=bind,source=requirements.txt,target=requirements.txt \ python -m pip install -r requirements.txt # Switch to the non-privileged user to run the application. USER appuser # Copy the source code into the container. COPY . . # Expose the port that the application listens on. EXPOSE 8001 # Run the application. CMD ["python3", "-m", "uvicorn", "app:app", "--host=0.0.0.0", "--port=8001"] Hide Create a file named `compose.yaml` with the following contents. compose.yaml Show more # Comments are provided throughout this file to help you get started. # If you need more help, visit the Docker Compose reference guide at # https://docs.docker.com/go/compose-spec-reference/ # Here the instructions define your application as a service called "server". # This service is built from the Dockerfile in the current directory. # You can add other services your application may depend on here, such as a # database or a cache. For examples, see the Awesome Compose repository: # https://github.com/docker/awesome-compose services: server: build: context: . ports: - 8001:8001 # The commented out section below is an example of how to define a PostgreSQL # database that your application can use. `depends_on` tells Docker Compose to # start the database before your application. The `db-data` volume persists the # database data between container restarts. The `db-password` secret is used # to set the database password. You must create `db/password.txt` and add # a password of your choosing to it before running `docker compose up`. # depends_on: # db: # condition: service_healthy # db: # image: postgres:18 # restart: always # user: postgres # secrets: # - db-password # volumes: # - db-data:/var/lib/postgresql # environment: # - POSTGRES_DB=example # - POSTGRES_PASSWORD_FILE=/run/secrets/db-password # expose: # - 5432 # healthcheck: # test: [ "CMD", "pg_isready" ] # interval: 10s # timeout: 5s # retries: 5 # volumes: # db-data: # secrets: # db-password: # file: db/password.txt Hide Create a file named `.dockerignore` with the following contents. .dockerignore Show more # Include any files or directories that you don't want to be copied to your # container here (e.g., local build artifacts, temporary files, etc.). # # For more help, visit the .dockerignore file reference guide at # https://docs.docker.com/go/build-context-dockerignore/ **/.DS_Store **/__pycache__ **/.venv **/.classpath **/.dockerignore **/.env **/.git **/.gitignore **/.project **/.settings **/.toolstarget **/.vs **/.vscode **/*.*proj.user **/*.dbmdl **/*.jfm **/bin **/charts **/docker-compose* **/compose.y*ml **/Dockerfile* **/node_modules **/npm-debug.log **/obj **/secrets.dev.yaml **/values.dev.yaml LICENSE README.md Hide Create a file named `.gitignore` with the following contents. .gitignore Show more # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] *$py.class # C extensions *.so # Distribution / packaging .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST # Unit test / coverage reports htmlcov/ .tox/ .nox/ .coverage .coverage.* .cache nosetests.xml coverage.xml *.cover *.py,cover .hypothesis/ .pytest_cache/ cover/ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm __pypackages__/ # Environments .env .venv env/ venv/ ENV/ env.bak/ venv.bak/ Hide [Add a local database and persist data](https://docs.docker.com/guides/python/develop/#add-a-local-database-and-persist-data) ------------------------------------------------------------------------------------------------------------------------------ You can use containers to set up local services, like a database. In this section, you'll update the `compose.yaml` file to define a database service and a volume to persist data. In the cloned repository's directory, open the `compose.yaml` file in an IDE or text editor. `docker init` handled creating most of the instructions, but you'll need to update it for your unique application. In the `compose.yaml` file, you need to uncomment all of the database instructions. In addition, you need to add the database password file as an environment variable to the server service and specify the secret file to use . The following is the updated `compose.yaml` file. services: server: build: context: . ports: - 8001:8001 environment: - POSTGRES_SERVER=db - POSTGRES_USER=postgres - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password depends_on: db: condition: service_healthy secrets: - db-password db: image: postgres:18 restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt > Note > > To learn more about the instructions in the Compose file, see [Compose file reference](https://docs.docker.com/reference/compose-file/) > . Before you run the application using Compose, notice that this Compose file specifies a `password.txt` file to hold the database's password. You must create this file as it's not included in the source repository. In the cloned repository's directory, create a new directory named `db` and inside that directory create a file named `password.txt` that contains the password for the database. Using your favorite IDE or text editor, add the following contents to the `password.txt` file. mysecretpassword Save and close the `password.txt` file. You should now have the following contents in your `python-docker-dev-example` directory. ├── python-docker-dev-example/ │ ├── db/ │ │ └── password.txt │ ├── app.py │ ├── config.py │ ├── requirements.txt │ ├── .dockerignore │ ├── .gitignore │ ├── compose.yaml │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md Now, run the following `docker compose up` command to start your application. $ docker compose up --build Now test your API endpoint. Open a new terminal then make a request to the server using the curl commands: Let's create an object with a post method $ curl -X 'POST' \ 'http://localhost:8001/heroes/' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{ "id": 1, "name": "my hero", "secret_name": "austing", "age": 12 }' You should receive the following response: { "age": 12, "id": 1, "name": "my hero", "secret_name": "austing" } Let's make a get request with the next curl command: curl -X 'GET' \ 'http://localhost:8001/heroes/' \ -H 'accept: application/json' You should receive the same response as above because it's the only one object we have in database. { "age": 12, "id": 1, "name": "my hero", "secret_name": "austing" } Press `ctrl+c` in the terminal to stop your application. [Automatically update services](https://docs.docker.com/guides/python/develop/#automatically-update-services) -------------------------------------------------------------------------------------------------------------- Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Open your `compose.yaml` file in an IDE or text editor and then add the Compose Watch instructions. The following is the updated `compose.yaml` file. services: server: build: context: . ports: - 8001:8001 environment: - POSTGRES_SERVER=db - POSTGRES_USER=postgres - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password depends_on: db: condition: service_healthy secrets: - db-password develop: watch: - action: rebuild path: . db: image: postgres:18 restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt Run the following command to run your application with Compose Watch. $ docker compose watch In a terminal, curl the application to get a response. $ curl http://localhost:8001 Hello, Docker! Any changes to the application's source files on your local machine will now be immediately reflected in the running container. Open `python-docker-dev-example/app.py` in an IDE or text editor and update the `Hello, Docker!` string by adding a few more exclamation marks. - return 'Hello, Docker!' + return 'Hello, Docker!!!' Save the changes to `app.py` and then wait a few seconds for the application to rebuild. Curl the application again and verify that the updated text appears. $ curl http://localhost:8001 Hello, Docker!!! Press `ctrl+c` in the terminal to stop your application. [Summary](https://docs.docker.com/guides/python/develop/#summary) ------------------------------------------------------------------ In this section, you took a look at setting up your Compose file to add a local database and persist data. You also learned how to use Compose Watch to automatically rebuild and run your container when you update your code. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose file watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) [Next steps](https://docs.docker.com/guides/python/develop/#next-steps) ------------------------------------------------------------------------ In the next section, you'll learn how you can set up linting, formatting and type checking to follow the best practices in python apps. [Linting, formatting, and type checking for Python »](https://docs.docker.com/guides/python/lint-format-typing/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [.NET language-specific guide](https://docs.docker.com/guides/dotnet/) Learn how to containerize .NET applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg "C#") C# 20 minutes [1](https://docs.docker.com/guides/dotnet/containerize/) [Containerize your app](https://docs.docker.com/guides/dotnet/containerize/) [2](https://docs.docker.com/guides/dotnet/develop/) [Develop your app](https://docs.docker.com/guides/dotnet/develop/) [3](https://docs.docker.com/guides/dotnet/run-tests/) [Run your tests](https://docs.docker.com/guides/dotnet/run-tests/) [4](https://docs.docker.com/guides/dotnet/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/dotnet/configure-ci-cd/) [5](https://docs.docker.com/guides/dotnet/deploy/) [Test your deployment](https://docs.docker.com/guides/dotnet/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for .NET development =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/dotnet/develop/#prerequisites) ------------------------------------------------------------------------------ Complete [Containerize a .NET application](https://docs.docker.com/guides/dotnet/containerize/) . [Overview](https://docs.docker.com/guides/dotnet/develop/#overview) -------------------------------------------------------------------- In this section, you'll learn how to set up a development environment for your containerized application. This includes: * Adding a local database and persisting data * Configuring Compose to automatically update your running Compose services as you edit and save your code * Creating a development container that contains the .NET Core SDK tools and dependencies [Update the application](https://docs.docker.com/guides/dotnet/develop/#update-the-application) ------------------------------------------------------------------------------------------------ This section uses a different branch of the `docker-dotnet-sample` repository that contains an updated .NET application. The updated application is on the `add-db` branch of the repository you cloned in [Containerize a .NET application](https://docs.docker.com/guides/dotnet/containerize/) . To get the updated code, you need to checkout the `add-db` branch. For the changes you made in [Containerize a .NET application](https://docs.docker.com/guides/dotnet/containerize/) , for this section, you can stash them. In a terminal, run the following commands in the `docker-dotnet-sample` directory. 1. Stash any previous changes. $ git stash -u 2. Check out the new branch with the updated application. $ git checkout add-db In the `add-db` branch, only the .NET application has been updated. None of the Docker assets have been updated yet. You should now have the following in your `docker-dotnet-sample` directory. ├── docker-dotnet-sample/ │ ├── .git/ │ ├── src/ │ │ ├── Data/ │ │ ├── Models/ │ │ ├── Pages/ │ │ ├── Properties/ │ │ ├── wwwroot/ │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── myWebApp.csproj │ │ └── Program.cs │ ├── tests/ │ │ ├── tests.csproj │ │ ├── UnitTest1.cs │ │ └── Usings.cs │ ├── .dockerignore │ ├── .gitignore │ ├── compose.yaml │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md [Add a local database and persist data](https://docs.docker.com/guides/dotnet/develop/#add-a-local-database-and-persist-data) ------------------------------------------------------------------------------------------------------------------------------ You can use containers to set up local services, like a database. In this section, you'll update the `compose.yaml` file to define a database service and a volume to persist data. Open the `compose.yaml` file in an IDE or text editor. You'll notice it already contains commented-out instructions for a PostgreSQL database and volume. Open `docker-dotnet-sample/src/appsettings.json` in an IDE or text editor. You'll notice the connection string with all the database information. The `compose.yaml` already contains this information, but it's commented out. Uncomment the database instructions in the `compose.yaml` file. The following is the updated `compose.yaml` file. services: server: build: context: . target: final ports: - 8080:8080 depends_on: db: condition: service_healthy db: image: postgres:18 restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt > Note > > To learn more about the instructions in the Compose file, see [Compose file reference](https://docs.docker.com/reference/compose-file/) > . Before you run the application using Compose, notice that this Compose file uses `secrets` and specifies a `password.txt` file to hold the database's password. You must create this file as it's not included in the source repository. In the `docker-dotnet-sample` directory, create a new directory named `db` and inside that directory create a file named `password.txt`. Open `password.txt` in an IDE or text editor and add the following password. The password must be on a single line, with no additional lines in the file. example Save and close the `password.txt` file. You should now have the following in your `docker-dotnet-sample` directory. ├── docker-dotnet-sample/ │ ├── .git/ │ ├── db/ │ │ └── password.txt │ ├── src/ │ ├── tests/ │ ├── .dockerignore │ ├── .gitignore │ ├── compose.yaml │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md Run the following command to start your application. $ docker compose up --build Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple web application with the text `Student name is`. The application doesn't display a name because the database is empty. For this application, you need to access the database and then add records. [Add records to the database](https://docs.docker.com/guides/dotnet/develop/#add-records-to-the-database) ---------------------------------------------------------------------------------------------------------- For the sample application, you must access the database directly to create sample records. You can run commands inside the database container using the `docker exec` command. Before running that command, you must get the ID of the database container. Open a new terminal window and run the following command to list all your running containers. $ docker container ls You should see output like the following. CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES cb36e310aa7e docker-dotnet-server "dotnet myWebApp.dll" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp docker-dotnet-server-1 39fdcf0aff7b postgres "docker-entrypoint.s…" About a minute ago Up About a minute (healthy) 5432/tcp docker-dotnet-db-1 In the previous example, the container ID is `39fdcf0aff7b`. Run the following command to connect to the postgres database in the container. Replace the container ID with your own container ID. $ docker exec -it 39fdcf0aff7b psql -d example -U postgres And finally, insert a record into the database. example=# INSERT INTO "Students" ("ID", "LastName", "FirstMidName", "EnrollmentDate") VALUES (DEFAULT, 'Whale', 'Moby', '2013-03-20'); You should see output like the following. INSERT 0 1 Close the database connection and exit the container shell by running `exit`. example=# exit [Verify that data persists in the database](https://docs.docker.com/guides/dotnet/develop/#verify-that-data-persists-in-the-database) -------------------------------------------------------------------------------------------------------------------------------------- Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple web application with the text `Student name is Whale Moby`. Press `ctrl+c` in the terminal to stop your application. In the terminal, run `docker compose rm` to remove your containers and then run `docker compose up` to run your application again. $ docker compose rm $ docker compose up --build Refresh [http://localhost:8080](http://localhost:8080/) in your browser and verify that the student name persisted, even after the containers were removed and ran again. Press `ctrl+c` in the terminal to stop your application. [Automatically update services](https://docs.docker.com/guides/dotnet/develop/#automatically-update-services) -------------------------------------------------------------------------------------------------------------- Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Open your `compose.yaml` file in an IDE or text editor and then add the Compose Watch instructions. The following is the updated `compose.yaml` file. services: server: build: context: . target: final ports: - 8080:8080 depends_on: db: condition: service_healthy develop: watch: - action: rebuild path: . db: image: postgres:18 restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt Run the following command to run your application with Compose Watch. $ docker compose watch Open a browser and verify that the application is running at [http://localhost:8080](http://localhost:8080/) . Any changes to the application's source files on your local machine will now be immediately reflected in the running container. Open `docker-dotnet-sample/src/Pages/Index.cshtml` in an IDE or text editor and update the student name text on line 13 from `Student name is` to `Student name:`. -

Student name is @Model.StudentName

+

Student name: @Model.StudentName

Save the changes to `Index.cshtml` and then wait a few seconds for the application to rebuild. Refresh [http://localhost:8080](http://localhost:8080/) in your browser and verify that the updated text appears. Press `ctrl+c` in the terminal to stop your application. [Create a development container](https://docs.docker.com/guides/dotnet/develop/#create-a-development-container) ---------------------------------------------------------------------------------------------------------------- At this point, when you run your containerized application, it's using the .NET runtime image. While this small image is good for production, it lacks the SDK tools and dependencies you may need when developing. Also, during development, you may not need to run `dotnet publish`. You can use multi-stage builds to build stages for both development and production in the same Dockerfile. For more details, see [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) . Add a new development stage to your Dockerfile and update your `compose.yaml` file to use this stage for local development. The following is the updated Dockerfile. Using Docker Hardened Images Using the official .NET 10 image # syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM dhi.io/dotnet:10-sdk AS build ARG TARGETARCH COPY . /source WORKDIR /source/src RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app FROM dhi.io/dotnet:10-sdk AS development COPY . /source WORKDIR /source/src CMD dotnet run --no-launch-profile FROM dhi.io/aspnetcore:10 WORKDIR /app COPY --from=build /app . ENTRYPOINT ["dotnet", "myWebApp.dll"] # syntax=docker/dockerfile:1 FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS build ARG TARGETARCH COPY . /source WORKDIR /source/src RUN --mount=type=cache,id=nuget,target=/root/.nuget/packages \ dotnet publish -a ${TARGETARCH/amd64/x64} --use-current-runtime --self-contained false -o /app FROM mcr.microsoft.com/dotnet/sdk:10.0-alpine AS development COPY . /source WORKDIR /source/src CMD dotnet run --no-launch-profile FROM mcr.microsoft.com/dotnet/aspnet:10.0-alpine AS final WORKDIR /app COPY --from=build /app . ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser USER appuser ENTRYPOINT ["dotnet", "myWebApp.dll"] The following is the updated `compose.yaml` file. services: server: build: context: . target: development ports: - 8080:8080 depends_on: db: condition: service_healthy develop: watch: - action: rebuild path: . environment: - ASPNETCORE_ENVIRONMENT=Development db: image: postgres:18 restart: always user: postgres secrets: - db-password volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=example - POSTGRES_PASSWORD_FILE=/run/secrets/db-password expose: - 5432 healthcheck: test: ["CMD", "pg_isready"] interval: 10s timeout: 5s retries: 5 volumes: db-data: secrets: db-password: file: db/password.txt Your containerized application will now use the SDK image (either `dhi.io/dotnet:10-sdk` for DHI or `mcr.microsoft.com/dotnet/sdk:10.0-alpine` for official images), which includes development tools like `dotnet test`. Continue to the next section to learn how you can run `dotnet test`. [Summary](https://docs.docker.com/guides/dotnet/develop/#summary) ------------------------------------------------------------------ In this section, you took a look at setting up your Compose file to add a local database and persist data. You also learned how to use Compose Watch to automatically rebuild and run your container when you update your code. And finally, you learned how to create a development container that contains the SDK tools and dependencies needed for development. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose file watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) [Next steps](https://docs.docker.com/guides/dotnet/develop/#next-steps) ------------------------------------------------------------------------ In the next section, you'll learn how to run unit tests using Docker. [Run .NET tests in a container »](https://docs.docker.com/guides/dotnet/run-tests/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Java language-specific guide](https://docs.docker.com/guides/java/) This guide demonstrates how to containerize Java applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java 20 minutes [1](https://docs.docker.com/guides/java/containerize/) [Containerize your app](https://docs.docker.com/guides/java/containerize/) [2](https://docs.docker.com/guides/java/develop/) [Develop your app](https://docs.docker.com/guides/java/develop/) [3](https://docs.docker.com/guides/java/run-tests/) [Run your tests](https://docs.docker.com/guides/java/run-tests/) [4](https://docs.docker.com/guides/java/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/java/configure-ci-cd/) [5](https://docs.docker.com/guides/java/deploy/) [Test your deployment](https://docs.docker.com/guides/java/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Use containers for Java development =================================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/java/develop/#prerequisites) ---------------------------------------------------------------------------- Work through the steps to containerize your application in [Containerize your app](https://docs.docker.com/guides/java/containerize/) . [Overview](https://docs.docker.com/guides/java/develop/#overview) ------------------------------------------------------------------ In this section, you’ll walk through setting up a local development environment for the application you containerized in the previous section. This includes: * Adding a local database and persisting data * Creating a development container to connect a debugger * Configuring Compose to automatically update your running Compose services as you edit and save your code [Add a local database and persist data](https://docs.docker.com/guides/java/develop/#add-a-local-database-and-persist-data) ---------------------------------------------------------------------------------------------------------------------------- You can use containers to set up local services, like a database. In this section, you'll update the `docker-compose.yaml` file to define a database service and a volume to persist data. Also, this particular application uses a system property to define the database type, so you'll need to update the `Dockerfile` to pass in the system property when starting the app. In the cloned repository's directory, open the `docker-compose.yaml` file in an IDE or text editor. Your Compose file has an example database service, but it'll require a few changes for your unique app. In the `docker-compose.yaml` file, you need to do the following: * Uncomment all of the database instructions. You'll now use a database service instead of local storage for the data. * Remove the top-level `secrets` element as well as the element inside the `db` service. This example uses the environment variable for the password rather than secrets. * Remove the `user` element from the `db` service. This example specifies the user in the environment variable. * Update the database environment variables. These are defined by the Postgres image. For more details, see the [Postgres Official Docker Image](https://hub.docker.com/_/postgres) . * Update the healthcheck test for the `db` service and specify the user. By default, the healthcheck uses the root user instead of the `petclinic` user you defined. * Add the database URL as an environment variable in the `server` service. This overrides the default value defined in `spring-petclinic/src/main/resources/application-postgres.properties`. The following is the updated `docker-compose.yaml` file. All comments have been removed. services: server: build: context: . ports: - 8080:8080 depends_on: db: condition: service_healthy environment: - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic db: image: postgres:18 restart: always volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=petclinic - POSTGRES_USER=petclinic - POSTGRES_PASSWORD=petclinic ports: - 5432:5432 healthcheck: test: ["CMD", "pg_isready", "-U", "petclinic"] interval: 10s timeout: 5s retries: 5 volumes: db-data: Open the `Dockerfile` in an IDE or text editor. In the `ENTRYPOINT` instruction, update the instruction to pass in the system property as specified in the `spring-petclinic/src/resources/db/postgres/petclinic_db_setup_postgres.txt` file. - ENTRYPOINT [ "java", "org.springframework.boot.loader.launch.JarLauncher" ] + ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ] Save and close all the files. Now, run the following `docker compose up` command to start your application. $ docker compose up --build Open a browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see a simple app for a pet clinic. Browse around the application. Navigate to **Veterinarians** and verify that the application is connected to the database by being able to list veterinarians. In the terminal, press `ctrl`+`c` to stop the application. [Dockerfile for development](https://docs.docker.com/guides/java/develop/#dockerfile-for-development) ------------------------------------------------------------------------------------------------------ The Dockerfile you have now is great for a small, secure production image with only the components necessary to run the application. When developing, you may want a different image that has a different environment. For example, in the development image you may want to set up the image to start the application so that you can connect a debugger to the running Java process. Rather than managing multiple Dockerfiles, you can add a new stage. Your Dockerfile can then produce a final image which is ready for production as well as a development image. Replace the contents of your Dockerfile with the following. # syntax=docker/dockerfile:1 FROM eclipse-temurin:21-jdk-jammy as deps WORKDIR /build COPY --chmod=0755 mvnw mvnw COPY .mvn/ .mvn/ RUN --mount=type=bind,source=pom.xml,target=pom.xml \ --mount=type=cache,target=/root/.m2 ./mvnw dependency:go-offline -DskipTests FROM deps as package WORKDIR /build COPY ./src src/ RUN --mount=type=bind,source=pom.xml,target=pom.xml \ --mount=type=cache,target=/root/.m2 \ ./mvnw package -DskipTests && \ mv target/$(./mvnw help:evaluate -Dexpression=project.artifactId -q -DforceStdout)-$(./mvnw help:evaluate -Dexpression=project.version -q -DforceStdout).jar target/app.jar FROM package as extract WORKDIR /build RUN java -Djarmode=layertools -jar target/app.jar extract --destination target/extracted FROM extract as development WORKDIR /build RUN cp -r /build/target/extracted/dependencies/. ./ RUN cp -r /build/target/extracted/spring-boot-loader/. ./ RUN cp -r /build/target/extracted/snapshot-dependencies/. ./ RUN cp -r /build/target/extracted/application/. ./ ENV JAVA_TOOL_OPTIONS -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:8000 CMD [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ] FROM eclipse-temurin:21-jre-jammy AS final ARG UID=10001 RUN adduser \ --disabled-password \ --gecos "" \ --home "/nonexistent" \ --shell "/sbin/nologin" \ --no-create-home \ --uid "${UID}" \ appuser USER appuser COPY --from=extract build/target/extracted/dependencies/ ./ COPY --from=extract build/target/extracted/spring-boot-loader/ ./ COPY --from=extract build/target/extracted/snapshot-dependencies/ ./ COPY --from=extract build/target/extracted/application/ ./ EXPOSE 8080 ENTRYPOINT [ "java", "-Dspring.profiles.active=postgres", "org.springframework.boot.loader.launch.JarLauncher" ] Save and close the `Dockerfile`. In the `Dockerfile` you added a new stage labeled `development` based on the `extract` stage. In this stage, you copy the extracted files to a common directory, then run a command to start the application. In the command, you expose port 8000 and declare the debug configuration for the JVM so that you can attach a debugger. [Use Compose to develop locally](https://docs.docker.com/guides/java/develop/#use-compose-to-develop-locally) -------------------------------------------------------------------------------------------------------------- The current Compose file doesn't start your development container. To do that, you must update your Compose file to target the development stage. Also, update the port mapping of the server service to provide access for the debugger. Open the `docker-compose.yaml` and add the following instructions into the file. services: server: build: context: . target: development ports: - 8080:8080 - 8000:8000 depends_on: db: condition: service_healthy environment: - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic db: image: postgres:18 restart: always volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=petclinic - POSTGRES_USER=petclinic - POSTGRES_PASSWORD=petclinic ports: - 5432:5432 healthcheck: test: ["CMD", "pg_isready", "-U", "petclinic"] interval: 10s timeout: 5s retries: 5 volumes: db-data: Now, start your application and to confirm that it's running. $ docker compose up --build Finally, test your API endpoint. Run the following curl command: $ curl --request GET \ --url http://localhost:8080/vets \ --header 'content-type: application/json' You should receive the following response: { "vetList": [\ {\ "id": 1,\ "firstName": "James",\ "lastName": "Carter",\ "specialties": [],\ "nrOfSpecialties": 0,\ "new": false\ },\ {\ "id": 2,\ "firstName": "Helen",\ "lastName": "Leary",\ "specialties": [{ "id": 1, "name": "radiology", "new": false }],\ "nrOfSpecialties": 1,\ "new": false\ },\ {\ "id": 3,\ "firstName": "Linda",\ "lastName": "Douglas",\ "specialties": [\ { "id": 3, "name": "dentistry", "new": false },\ { "id": 2, "name": "surgery", "new": false }\ ],\ "nrOfSpecialties": 2,\ "new": false\ },\ {\ "id": 4,\ "firstName": "Rafael",\ "lastName": "Ortega",\ "specialties": [{ "id": 2, "name": "surgery", "new": false }],\ "nrOfSpecialties": 1,\ "new": false\ },\ {\ "id": 5,\ "firstName": "Henry",\ "lastName": "Stevens",\ "specialties": [{ "id": 1, "name": "radiology", "new": false }],\ "nrOfSpecialties": 1,\ "new": false\ },\ {\ "id": 6,\ "firstName": "Sharon",\ "lastName": "Jenkins",\ "specialties": [],\ "nrOfSpecialties": 0,\ "new": false\ }\ ] } [Connect a Debugger](https://docs.docker.com/guides/java/develop/#connect-a-debugger) -------------------------------------------------------------------------------------- You’ll use the debugger that comes with the IntelliJ IDEA. You can use the community version of this IDE. Open your project in IntelliJ IDEA, go to the **Run** menu, and then **Edit Configuration**. Add a new Remote JVM Debug configuration similar to the following: ![Java Connect a Debugger](https://docs.docker.com/guides/java/images/connect-debugger.webp) Set a breakpoint. Open `src/main/java/org/springframework/samples/petclinic/vet/VetController.java` and add a breakpoint inside the `showResourcesVetList` function. To start your debug session, select the **Run** menu and then **Debug _NameOfYourConfiguration_**. ![Debug menu](https://docs.docker.com/guides/java/images/debug-menu.webp) You should now see the connection in the logs of your Compose application. ![Compose log file ](https://docs.docker.com/guides/java/images/compose-logs.webp) You can now call the server endpoint. $ curl --request GET --url http://localhost:8080/vets You should have seen the code break on the marked line and now you are able to use the debugger just like you would normally. You can also inspect and watch variables, set conditional breakpoints, view stack traces and a do bunch of other stuff. ![Debugger code breakpoint](https://docs.docker.com/guides/java/images/debugger-breakpoint.webp) Press `ctrl+c` in the terminal to stop your application. [Automatically update services](https://docs.docker.com/guides/java/develop/#automatically-update-services) ------------------------------------------------------------------------------------------------------------ Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Open your `docker-compose.yaml` file in an IDE or text editor and then add the Compose Watch instructions. The following is the updated `docker-compose.yaml` file. services: server: build: context: . target: development ports: - 8080:8080 - 8000:8000 depends_on: db: condition: service_healthy environment: - POSTGRES_URL=jdbc:postgresql://db:5432/petclinic develop: watch: - action: rebuild path: . db: image: postgres:18 restart: always volumes: - db-data:/var/lib/postgresql environment: - POSTGRES_DB=petclinic - POSTGRES_USER=petclinic - POSTGRES_PASSWORD=petclinic ports: - 5432:5432 healthcheck: test: ["CMD", "pg_isready", "-U", "petclinic"] interval: 10s timeout: 5s retries: 5 volumes: db-data: Run the following command to run your application with Compose Watch. $ docker compose watch Open a web browser and view the application at [http://localhost:8080](http://localhost:8080/) . You should see the Spring Pet Clinic home page. Any changes to the application's source files on your local machine will now be automatically reflected in the running container. Open `spring-petclinic/src/main/resources/templates/fragments/layout.html` in an IDE or text editor and update the `Home` navigation string by adding an exclamation mark. -
  • +
  • Save the changes to `layout.html` and then you can continue developing while the container automatically rebuilds. After the container is rebuilt and running, refresh [http://localhost:8080](http://localhost:8080/) and then verify that **Home!** now appears in the menu. Press `ctrl+c` in the terminal to stop Compose Watch. [Summary](https://docs.docker.com/guides/java/develop/#summary) ---------------------------------------------------------------- In this section, you took a look at running a database locally and persisting the data. You also created a development image that contains the JDK and lets you attach a debugger. Finally, you set up your Compose file to expose the debugging port and configured Compose Watch to live reload your changes. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) [Next steps](https://docs.docker.com/guides/java/develop/#next-steps) ---------------------------------------------------------------------- In the next section, you’ll take a look at how to run unit tests in Docker. [Run your Java tests »](https://docs.docker.com/guides/java/run-tests/) --- # Extension annotations | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testcontainers container lifecycle management using JUnit 5](https://docs.docker.com/guides/testcontainers-java-lifecycle/) Learn different approaches to manage container lifecycle with Testcontainers using JUnit 5 lifecycle callbacks, extension annotations, and the singleton containers pattern. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-lifecycle/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-lifecycle/lifecycle-callbacks/) [Lifecycle callbacks](https://docs.docker.com/guides/testcontainers-java-lifecycle/lifecycle-callbacks/) [3](https://docs.docker.com/guides/testcontainers-java-lifecycle/extension-annotations/) [Extension annotations](https://docs.docker.com/guides/testcontainers-java-lifecycle/extension-annotations/) [4](https://docs.docker.com/guides/testcontainers-java-lifecycle/singleton-containers/) [Singleton containers](https://docs.docker.com/guides/testcontainers-java-lifecycle/singleton-containers/) [« Back to all guides](https://docs.docker.com/guides/) JUnit 5 extension annotations ============================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude * * * The Testcontainers library provides a JUnit 5 extension that simplifies starting and stopping containers using annotations. To use it, add the `org.testcontainers:testcontainers-junit-jupiter` test dependency. package com.testcontainers.demo; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.testcontainers.postgresql.PostgreSQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; @Testcontainers class CustomerServiceWithJUnit5ExtensionTest { @Container static PostgreSQLContainer postgres = new PostgreSQLContainer( "postgres:16-alpine" ); CustomerService customerService; @BeforeEach void setUp() { customerService = new CustomerService( postgres.getJdbcUrl(), postgres.getUsername(), postgres.getPassword() ); customerService.deleteAllCustomers(); } @Test void shouldCreateCustomer() { customerService.createCustomer(new Customer(1L, "George")); Optional customer = customerService.getCustomer(1L); assertTrue(customer.isPresent()); assertEquals(1L, customer.get().id()); assertEquals("George", customer.get().name()); } @Test void shouldGetCustomers() { customerService.createCustomer(new Customer(1L, "George")); customerService.createCustomer(new Customer(2L, "John")); List customers = customerService.getAllCustomers(); assertEquals(2, customers.size()); } } Instead of manually starting and stopping the container in `@BeforeAll` and `@AfterAll`, the `@Testcontainers` annotation on the class and the `@Container` annotation on the field handle it automatically: * The extension finds all `@Container`\-annotated fields. * **Static fields** start once before all tests and stop after all tests. * **Instance fields** start before each test and stop after each test (not recommended — it's resource-intensive). [Singleton containers pattern »](https://docs.docker.com/guides/testcontainers-java-lifecycle/singleton-containers/) --- # Develop your app | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Rust language-specific guide](https://docs.docker.com/guides/rust/) This guide covers how to containerize Rust applications using Docker. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/rust/rust-original.svg "Rust") Rust Docker Hardened Images 20 minutes [1](https://docs.docker.com/guides/rust/build-images/) [Build images](https://docs.docker.com/guides/rust/build-images/) [2](https://docs.docker.com/guides/rust/run-containers/) [Run containers](https://docs.docker.com/guides/rust/run-containers/) [3](https://docs.docker.com/guides/rust/develop/) [Develop your app](https://docs.docker.com/guides/rust/develop/) [4](https://docs.docker.com/guides/rust/configure-ci-cd/) [Configure CI/CD](https://docs.docker.com/guides/rust/configure-ci-cd/) [5](https://docs.docker.com/guides/rust/deploy/) [Test your deployment](https://docs.docker.com/guides/rust/deploy/) [« Back to all guides](https://docs.docker.com/guides/) Develop your Rust application ============================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Prerequisites](https://docs.docker.com/guides/rust/develop/#prerequisites) ---------------------------------------------------------------------------- * You have installed the latest version of [Docker Desktop](https://docs.docker.com/get-started/get-docker/) . * You have completed the walkthroughs in the Docker Desktop [Learning Center](https://docs.docker.com/desktop/use-desktop/) to learn about Docker concepts. * You have a [git client](https://git-scm.com/downloads) . The examples in this section use a command-line based git client, but you can use any client. [Overview](https://docs.docker.com/guides/rust/develop/#overview) ------------------------------------------------------------------ In this section, you’ll learn how to use volumes and networking in Docker. You’ll also use Docker to build your images and Docker Compose to make everything a whole lot easier. First, you’ll take a look at running a database in a container and how you can use volumes and networking to persist your data and let your application talk with the database. Then you’ll pull everything together into a Compose file which lets you set up and run a local development environment with one command. [Run a database in a container](https://docs.docker.com/guides/rust/develop/#run-a-database-in-a-container) ------------------------------------------------------------------------------------------------------------ Instead of downloading PostgreSQL, installing, configuring, and then running the PostgreSQL database as a service, you can use the Docker Official Image for PostgreSQL and run it in a container. Before you run PostgreSQL in a container, create a volume that Docker can manage to store your persistent data and configuration. Use the named volumes feature that Docker provides instead of using bind mounts. Run the following command to create your volume. $ docker volume create db-data Now create a network that your application and database will use to talk to each other. The network is called a user-defined bridge network and gives you a nice DNS lookup service which you can use when creating your connection string. $ docker network create postgresnet Now you can run PostgreSQL in a container and attach to the volume and network that you created previously. Docker pulls the image from Hub and runs it for you locally. In the following command, option `--mount` is for starting the container with a volume. For more information, see [Docker volumes](https://docs.docker.com/engine/storage/volumes/) . $ docker run --rm -d --mount \ "type=volume,src=db-data,target=/var/lib/postgresql" \ -p 5432:5432 \ --network postgresnet \ --name db \ -e POSTGRES_PASSWORD=mysecretpassword \ -e POSTGRES_DB=example \ postgres:18 Now, make sure that your PostgreSQL database is running and that you can connect to it. Connect to the running PostgreSQL database inside the container. $ docker exec -it db psql -U postgres You should see output like the following. psql (15.3 (Debian 15.3-1.pgdg110+1)) Type "help" for help. postgres=# In the previous command, you logged in to the PostgreSQL database by passing the `psql` command to the `db` container. Press ctrl-d to exit the PostgreSQL interactive terminal. [Get and run the sample application](https://docs.docker.com/guides/rust/develop/#get-and-run-the-sample-application) ---------------------------------------------------------------------------------------------------------------------- For the sample application, you'll use a variation of the backend from the react-rust-postgres application from [Awesome Compose](https://github.com/docker/awesome-compose/tree/master/react-rust-postgres) . 1. Clone the sample application repository using the following command. $ git clone https://github.com/docker/docker-rust-postgres 2. In the cloned repository's directory, run `docker init` to create the necessary Docker files. Refer to the following example to answer the prompts from `docker init`. $ docker init Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore - Dockerfile - compose.yaml - README.Docker.md Let's get started! ? What application platform does your project use? Rust ? What version of Rust do you want to use? 1.70.0 ? What port does your server listen on? 8000 3. In the cloned repository's directory, open the `Dockerfile` in an IDE or text editor to update it. `docker init` handled creating most of the instructions in the Dockerfile, but you'll need to update it for your unique application. In addition to a `src` directory, this application includes a `migrations` directory to initialize the database. Add a bind mount for the `migrations` directory to the build stage in the Dockerfile. The following is the updated Dockerfile. # syntax=docker/dockerfile:1 # Comments are provided throughout this file to help you get started. # If you need more help, visit the Dockerfile reference guide at # https://docs.docker.com/reference/dockerfile/ ################################################################################ # Create a stage for building the application. ARG RUST_VERSION=1.70.0 ARG APP_NAME=react-rust-postgres FROM rust:${RUST_VERSION}-slim-bullseye AS build ARG APP_NAME WORKDIR /app # Build the application. # Leverage a cache mount to /usr/local/cargo/registry/ # for downloaded dependencies and a cache mount to /app/target/ for # compiled dependencies which will speed up subsequent builds. # Leverage a bind mount to the src directory to avoid having to copy the # source code into the container. Once built, copy the executable to an # output directory before the cache mounted /app/target is unmounted. RUN --mount=type=bind,source=src,target=src \ --mount=type=bind,source=Cargo.toml,target=Cargo.toml \ --mount=type=bind,source=Cargo.lock,target=Cargo.lock \ --mount=type=cache,target=/app/target/ \ --mount=type=cache,target=/usr/local/cargo/registry/ \ --mount=type=bind,source=migrations,target=migrations \ < Note > > To learn more about the instructions in the Compose file, see [Compose file reference](https://docs.docker.com/reference/compose-file/) > . Before you run the application using Compose, notice that this Compose file uses `secrets` and specifies a `password.txt` file to hold the database's password. You must create this file as it's not included in the source repository. In the `docker-php-sample` directory, create a new directory named `db` and inside that directory create a file named `password.txt`. Open `password.txt` in an IDE or text editor and add the following password. The password must be on a single line, with no additional lines in the file. example Save and close the `password.txt` file. You should now have the following in your `docker-php-sample` directory. ├── docker-php-sample/ │ ├── .git/ │ ├── db/ │ │ └── password.txt │ ├── src/ │ ├── tests/ │ ├── .dockerignore │ ├── .gitignore │ ├── compose.yaml │ ├── composer.json │ ├── composer.lock │ ├── Dockerfile │ ├── README.Docker.md │ └── README.md Run the following command to start your application. $ docker compose up --build Open a browser and view the application at [http://localhost:9000/database.php](http://localhost:9000/database.php) . You should see a simple web application with text and a counter that increments every time you refresh. Press `ctrl+c` in the terminal to stop your application. [Verify that data persists in the database](https://docs.docker.com/guides/php/develop/#verify-that-data-persists-in-the-database) ----------------------------------------------------------------------------------------------------------------------------------- In the terminal, run `docker compose rm` to remove your containers and then run `docker compose up` to run your application again. $ docker compose rm $ docker compose up --build Refresh [http://localhost:9000/database.php](http://localhost:9000/database.php) in your browser and verify that the previous count still exists. Without a volume, the database data wouldn't persist after you remove the container. Press `ctrl+c` in the terminal to stop your application. [Add phpMyAdmin to interact with the database](https://docs.docker.com/guides/php/develop/#add-phpmyadmin-to-interact-with-the-database) ----------------------------------------------------------------------------------------------------------------------------------------- You can easily add services to your application stack by updating the `compose.yaml` file. Update your `compose.yaml` to add a new service for phpMyAdmin. For more details, see the [phpMyAdmin Official Docker Image](https://hub.docker.com/_/phpmyadmin) . The following is the updated `compose.yaml` file. services: server: build: context: . ports: - 9000:80 depends_on: db: condition: service_healthy secrets: - db-password environment: - PASSWORD_FILE_PATH=/run/secrets/db-password - DB_HOST=db - DB_NAME=example - DB_USER=root db: image: mariadb restart: always user: root secrets: - db-password volumes: - db-data:/var/lib/mysql environment: - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password - MARIADB_DATABASE=example expose: - 3306 healthcheck: test: [\ "CMD",\ "/usr/local/bin/healthcheck.sh",\ "--su-mysql",\ "--connect",\ "--innodb_initialized",\ ] interval: 10s timeout: 5s retries: 5 phpmyadmin: image: phpmyadmin ports: - 8080:80 depends_on: - db environment: - PMA_HOST=db volumes: db-data: secrets: db-password: file: db/password.txt In the terminal, run `docker compose up` to run your application again. $ docker compose up --build Open [http://localhost:8080](http://localhost:8080/) in your browser to access phpMyAdmin. Log in using `root` as the username and `example` as the password. You can now interact with the database through phpMyAdmin. Press `ctrl+c` in the terminal to stop your application. [Automatically update services](https://docs.docker.com/guides/php/develop/#automatically-update-services) ----------------------------------------------------------------------------------------------------------- Use Compose Watch to automatically update your running Compose services as you edit and save your code. For more details about Compose Watch, see [Use Compose Watch](https://docs.docker.com/compose/how-tos/file-watch/) . Open your `compose.yaml` file in an IDE or text editor and then add the Compose Watch instructions. The following is the updated `compose.yaml` file. services: server: build: context: . ports: - 9000:80 depends_on: db: condition: service_healthy secrets: - db-password environment: - PASSWORD_FILE_PATH=/run/secrets/db-password - DB_HOST=db - DB_NAME=example - DB_USER=root develop: watch: - action: sync path: ./src target: /var/www/html db: image: mariadb restart: always user: root secrets: - db-password volumes: - db-data:/var/lib/mysql environment: - MARIADB_ROOT_PASSWORD_FILE=/run/secrets/db-password - MARIADB_DATABASE=example expose: - 3306 healthcheck: test: [\ "CMD",\ "/usr/local/bin/healthcheck.sh",\ "--su-mysql",\ "--connect",\ "--innodb_initialized",\ ] interval: 10s timeout: 5s retries: 5 phpmyadmin: image: phpmyadmin ports: - 8080:80 depends_on: - db environment: - PMA_HOST=db volumes: db-data: secrets: db-password: file: db/password.txt Run the following command to run your application with Compose Watch. $ docker compose watch Open a browser and verify that the application is running at [http://localhost:9000/hello.php](http://localhost:9000/hello.php) . Any changes to the application's source files on your local machine will now be immediately reflected in the running container. Open `hello.php` in an IDE or text editor and update the string `Hello, world!` to `Hello, Docker!`. Save the changes to `hello.php` and then wait a few seconds for the application to sync. Refresh [http://localhost:9000/hello.php](http://localhost:9000/hello.php) in your browser and verify that the updated text appears. Press `ctrl+c` in the terminal to stop Compose Watch. Run `docker compose down` in the terminal to stop the application. [Create a development container](https://docs.docker.com/guides/php/develop/#create-a-development-container) ------------------------------------------------------------------------------------------------------------- At this point, when you run your containerized application, Composer isn't installing the dev dependencies. While this small image is good for production, it lacks the tools and dependencies you may need when developing and it doesn't include the `tests` directory. You can use multi-stage builds to build stages for both development and production in the same Dockerfile. For more details, see [Multi-stage builds](https://docs.docker.com/build/building/multi-stage/) . In the `Dockerfile`, you'll need to update the following: 1. Split the `deps` staged into two stages. One stage for production (`prod-deps`) and one stage (`dev-deps`) to install development dependencies. 2. Create a common `base` stage. 3. Create a new `development` stage for development. 4. Update the `final` stage to copy dependencies from the new `prod-deps` stage. The following is the `Dockerfile` before and after the changes. Before After # syntax=docker/dockerfile:1 FROM composer:lts as deps WORKDIR /app RUN --mount=type=bind,source=composer.json,target=composer.json \ --mount=type=bind,source=composer.lock,target=composer.lock \ --mount=type=cache,target=/tmp/cache \ composer install --no-dev --no-interaction FROM php:8.2-apache as final RUN docker-php-ext-install pdo pdo_mysql RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" COPY --from=deps app/vendor/ /var/www/html/vendor COPY ./src /var/www/html USER www-data # syntax=docker/dockerfile:1 FROM composer:lts as prod-deps WORKDIR /app RUN --mount=type=bind,source=./composer.json,target=composer.json \ --mount=type=bind,source=./composer.lock,target=composer.lock \ --mount=type=cache,target=/tmp/cache \ composer install --no-dev --no-interaction FROM composer:lts as dev-deps WORKDIR /app RUN --mount=type=bind,source=./composer.json,target=composer.json \ --mount=type=bind,source=./composer.lock,target=composer.lock \ --mount=type=cache,target=/tmp/cache \ composer install --no-interaction FROM php:8.2-apache as base RUN docker-php-ext-install pdo pdo_mysql COPY ./src /var/www/html FROM base as development COPY ./tests /var/www/html/tests RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini" COPY --from=dev-deps app/vendor/ /var/www/html/vendor FROM base as final RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" COPY --from=prod-deps app/vendor/ /var/www/html/vendor USER www-data Update your `compose.yaml` file by adding an instruction to target the development stage. The following is the updated section of the `compose.yaml` file. services: server: build: context: . target: development # ... Your containerized application will now install the dev dependencies. Run the following command to start your application. $ docker compose up --build Open a browser and view the application at [http://localhost:9000/hello.php](http://localhost:9000/hello.php) . You should still see the simple "Hello, Docker!" application. Press `ctrl+c` in the terminal to stop your application. While the application appears the same, you can now make use of the dev dependencies. Continue to the next section to learn how you can run tests using Docker. [Summary](https://docs.docker.com/guides/php/develop/#summary) --------------------------------------------------------------- In this section, you took a look at setting up your Compose file to add a local database and persist data. You also learned how to use Compose Watch to automatically sync your application when you update your code. And finally, you learned how to create a development container that contains the dependencies needed for development. Related information: * [Compose file reference](https://docs.docker.com/reference/compose-file/) * [Compose file watch](https://docs.docker.com/compose/how-tos/file-watch/) * [Dockerfile reference](https://docs.docker.com/reference/dockerfile/) * [Official Docker Image for PHP](https://hub.docker.com/_/php) [Next steps](https://docs.docker.com/guides/php/develop/#next-steps) --------------------------------------------------------------------- In the next section, you'll learn how to run unit tests using Docker. [Run PHP tests in a container »](https://docs.docker.com/guides/php/run-tests/) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations in Micronaut apps using WireMock](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/) Learn how to create a Micronaut application that integrates with external REST APIs, then test those integrations using WireMock and the Testcontainers WireMock module. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with WireMock and Testcontainers ============================================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Mocking external API interactions at the HTTP protocol level, rather than mocking Java methods, lets you verify marshalling and unmarshalling behavior and simulate network issues. [Test with WireMock's JUnit 5 extension](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/#test-with-wiremocks-junit-5-extension) ------------------------------------------------------------------------------------------------------------------------------------------------------------------- The first approach uses WireMock's `WireMockExtension` to start an in-process WireMock server on a dynamic port. Create `AlbumControllerTest.java`: package com.testcontainers.demo; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import io.micronaut.context.ApplicationContext; import io.micronaut.http.MediaType; import io.micronaut.runtime.server.EmbeddedServer; import io.restassured.RestAssured; import io.restassured.http.ContentType; import java.util.Collections; import java.util.Map; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; class AlbumControllerTest { @RegisterExtension static WireMockExtension wireMock = WireMockExtension.newInstance() .options(wireMockConfig().dynamicPort()) .build(); private Map getProperties() { return Collections.singletonMap("micronaut.http.services.photosapi.url", wireMock.baseUrl()); } @Test void shouldGetAlbumById() { try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class, getProperties())) { RestAssured.port = server.getPort(); Long albumId = 1L; String responseJson = """ [\ {\ "id": 1,\ "title": "accusamus beatae ad facilis cum similique qui sunt",\ "url": "https://via.placeholder.com/600/92c952",\ "thumbnailUrl": "https://via.placeholder.com/150/92c952"\ },\ {\ "id": 2,\ "title": "reprehenderit est deserunt velit ipsam",\ "url": "https://via.placeholder.com/600/771796",\ "thumbnailUrl": "https://via.placeholder.com/150/771796"\ }\ ] """; wireMock.stubFor(WireMock.get(urlMatching("/albums/" + albumId + "/photos")) .willReturn(aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON) .withBody(responseJson))); given().contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(2)); } } @Test void shouldReturnServerErrorWhenPhotoServiceCallFailed() { try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class, getProperties())) { RestAssured.port = server.getPort(); Long albumId = 2L; wireMock.stubFor(WireMock.get(urlMatching("/albums/" + albumId + "/photos")) .willReturn(aResponse().withStatus(500))); given().contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(500); } } } Here's what this test does: * `WireMockExtension` starts a WireMock server on a dynamic port. * The `getProperties()` method overrides `micronaut.http.services.photosapi.url` to point at the WireMock endpoint, so the application talks to WireMock instead of the real photo service. * `shouldGetAlbumById()` configures a mock response for `/albums/{albumId}/photos`, sends a request to the application's `/api/albums/{albumId}` endpoint, and verifies the response body. * `shouldReturnServerErrorWhenPhotoServiceCallFailed()` configures WireMock to return a 500 status and verifies the application propagates that error. [Stub using JSON mapping files](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/#stub-using-json-mapping-files) -------------------------------------------------------------------------------------------------------------------------------------------------- Instead of stubbing with the WireMock Java API, you can use JSON mapping-based configuration. Create `src/test/resources/wiremock/mappings/get-album-photos.json`: { "mappings": [\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/([0-9]+)/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "bodyFileName": "album-photos-resp-200.json"\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/2/photos"\ },\ "response": {\ "status": 500,\ "headers": {\ "Content-Type": "application/json"\ }\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/3/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "jsonBody": []\ }\ }\ ] } Create `src/test/resources/wiremock/__files/album-photos-resp-200.json`: [\ {\ "id": 1,\ "title": "accusamus beatae ad facilis cum similique qui sunt",\ "url": "https://via.placeholder.com/600/92c952",\ "thumbnailUrl": "https://via.placeholder.com/150/92c952"\ },\ {\ "id": 2,\ "title": "reprehenderit est deserunt velit ipsam",\ "url": "https://via.placeholder.com/600/771796",\ "thumbnailUrl": "https://via.placeholder.com/150/771796"\ }\ ] Then initialize WireMock to load stub mappings from these files: @RegisterExtension static WireMockExtension wireMock = WireMockExtension.newInstance() .options( wireMockConfig() .dynamicPort() .usingFilesUnderClasspath("wiremock") ) .build(); With mapping files-based stubbing in place, write tests without needing programmatic stubs: @Test void shouldGetAlbumById() { Long albumId = 1L; try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class, getProperties())) { RestAssured.port = server.getPort(); given().contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(2)); } } [Use the Testcontainers WireMock module](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/write-tests/#use-the-testcontainers-wiremock-module) -------------------------------------------------------------------------------------------------------------------------------------------------------------------- The [Testcontainers WireMock module](https://testcontainers.com/modules/wiremock/) provisions a WireMock server as a standalone container within your tests, based on [WireMock Docker](https://github.com/wiremock/wiremock-docker) . Create `src/test/resources/mocks-config.json` with the stub mappings: { "mappings": [\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/([0-9]+)/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "bodyFileName": "album-photos-response.json"\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/2/photos"\ },\ "response": {\ "status": 500,\ "headers": {\ "Content-Type": "application/json"\ }\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/3/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "jsonBody": []\ }\ }\ ] } Create `src/test/resources/album-photos-response.json`: [\ {\ "id": 1,\ "title": "accusamus beatae ad facilis cum similique qui sunt",\ "url": "https://via.placeholder.com/600/92c952",\ "thumbnailUrl": "https://via.placeholder.com/150/92c952"\ },\ {\ "id": 2,\ "title": "reprehenderit est deserunt velit ipsam",\ "url": "https://via.placeholder.com/600/771796",\ "thumbnailUrl": "https://via.placeholder.com/150/771796"\ }\ ] Create `AlbumControllerTestcontainersTests.java`: package com.testcontainers.demo; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.nullValue; import io.micronaut.context.ApplicationContext; import io.micronaut.core.annotation.NonNull; import io.micronaut.runtime.server.EmbeddedServer; import io.restassured.RestAssured; import io.restassured.http.ContentType; import java.util.Collections; import java.util.Map; import org.junit.jupiter.api.Test; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.wiremock.integrations.testcontainers.WireMockContainer; @Testcontainers(disabledWithoutDocker = true) class AlbumControllerTestcontainersTests { @Container static WireMockContainer wiremockServer = new WireMockContainer("wiremock/wiremock:2.35.0") .withMappingFromResource("mocks-config.json") .withFileFromResource("album-photos-response.json"); @NonNull public Map getProperties() { return Collections.singletonMap("micronaut.http.services.photosapi.url", wiremockServer.getBaseUrl()); } @Test void shouldGetAlbumById() { Long albumId = 1L; try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class, getProperties())) { RestAssured.port = server.getPort(); given().contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(2)); } } @Test void shouldReturnServerErrorWhenPhotoServiceCallFailed() { Long albumId = 2L; try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class, getProperties())) { RestAssured.port = server.getPort(); given().contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(500); } } @Test void shouldReturnEmptyPhotos() { Long albumId = 3L; try (EmbeddedServer server = ApplicationContext.run(EmbeddedServer.class, getProperties())) { RestAssured.port = server.getPort(); given().contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", nullValue()); } } } Here's what this test does: * `@Testcontainers` and `@Container` annotations start a `WireMockContainer` using the `wiremock/wiremock:2.35.0` Docker image. * `withMappingFromResource("mocks-config.json")` loads stub mappings from the classpath resource. * `withFileFromResource("album-photos-response.json")` makes the response body file available to WireMock. * `getProperties()` overrides the photo service URL to point at the WireMock container's base URL. * `shouldGetAlbumById()` verifies that the application returns the expected album with two photos. * `shouldReturnServerErrorWhenPhotoServiceCallFailed()` verifies that a 500 from the photo service propagates to the caller. * `shouldReturnEmptyPhotos()` verifies the application handles an empty photo list. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-micronaut-wiremock/run-tests/) --- # Run tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing an ASP.NET Core web app with Testcontainers](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/) Learn how to test an ASP.NET Core web app using Testcontainers for .NET with a real Microsoft SQL Server instance instead of SQLite. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/csharp/csharp-original.svg "C#") C# Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/create-project/) [2](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/write-tests/) [3](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Run tests and next steps ======================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Run the tests](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/#run-the-tests) ----------------------------------------------------------------------------------------------------------- Run the tests from the solution root: $ dotnet test ./RazorPagesProject.sln The first run may take longer because Docker needs to pull the Microsoft SQL Server image. On subsequent runs, the image is cached locally. You should see xUnit discover and run the tests, including the `MsSqlTests.IndexPageTests` class. Testcontainers starts a SQL Server container, the tests execute against it, and the container is stopped and removed automatically after the tests finish. [Summary](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/#summary) ----------------------------------------------------------------------------------------------- By replacing SQLite with a Testcontainers-managed Microsoft SQL Server instance, the integration tests run against the same type of database used in production. This approach catches database-specific issues early, such as differences in SQL dialect, transaction behavior, or data type handling between SQLite and SQL Server. The `MsSqlTests` class uses `IAsyncLifetime` to manage the container lifecycle, and a nested `CustomWebApplicationFactory` wires the container's connection string into the application's service configuration. You can apply this same pattern to any database or service that Testcontainers supports. To learn more about Testcontainers, visit the [Testcontainers overview](https://testcontainers.com/getting-started/) . [Further reading](https://docs.docker.com/guides/testcontainers-dotnet-aspnet-core/run-tests/#further-reading) --------------------------------------------------------------------------------------------------------------- * [Testcontainers for .NET documentation](https://dotnet.testcontainers.org/) * [Testcontainers for .NET modules](https://dotnet.testcontainers.org/modules/) * [Microsoft SQL Server module](https://www.nuget.org/packages/Testcontainers.MsSql) * [Integration tests in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/test/integration-tests) --- # Write tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations using WireMock](https://docs.docker.com/guides/testcontainers-java-wiremock/) Learn how to create a Spring Boot application that integrates with external REST APIs, then test those integrations using Testcontainers and WireMock. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Write tests with WireMock and Testcontainers ============================================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Mocking external API interactions at the HTTP protocol level, rather than mocking Java methods, lets you verify marshalling and unmarshalling behavior and simulate network issues. [Test using WireMock JUnit 5 extension](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/#test-using-wiremock-junit-5-extension) -------------------------------------------------------------------------------------------------------------------------------------------------------- WireMock provides a JUnit 5 extension that starts an in-process WireMock server. You can configure stub responses using the WireMock Java API. Create `AlbumControllerTest.java`: package com.testcontainers.demo; import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.urlMatching; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import com.github.tomakehurst.wiremock.client.WireMock; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.http.MediaType; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @SpringBootTest(webEnvironment = RANDOM_PORT) class AlbumControllerTest { @LocalServerPort private Integer port; @RegisterExtension static WireMockExtension wireMock = WireMockExtension .newInstance() .options(wireMockConfig().dynamicPort()) .build(); @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add("photos.api.base-url", wireMock::baseUrl); } @BeforeEach void setUp() { RestAssured.port = port; } @Test void shouldGetAlbumById() { Long albumId = 1L; wireMock.stubFor( WireMock .get(urlMatching("/albums/" + albumId + "/photos")) .willReturn( aResponse() .withHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE) .withBody( """ [\ {\ "id": 1,\ "title": "accusamus beatae ad facilis cum similique qui sunt",\ "url": "https://via.placeholder.com/600/92c952",\ "thumbnailUrl": "https://via.placeholder.com/150/92c952"\ },\ {\ "id": 2,\ "title": "reprehenderit est deserunt velit ipsam",\ "url": "https://via.placeholder.com/600/771796",\ "thumbnailUrl": "https://via.placeholder.com/150/771796"\ }\ ] """ ) ) ); given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(2)); } @Test void shouldReturnServerErrorWhenPhotoServiceCallFailed() { Long albumId = 2L; wireMock.stubFor( WireMock .get(urlMatching("/albums/" + albumId + "/photos")) .willReturn(aResponse().withStatus(500)) ); given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(500); } } Here's what the test does: * `@SpringBootTest` starts the full application on a random port. * `@RegisterExtension` creates a `WireMockExtension` that starts WireMock on a dynamic port. * `@DynamicPropertySource` overrides `photos.api.base-url` to point at the WireMock endpoint, so the application talks to WireMock instead of the real photo service. * `shouldGetAlbumById()` configures a stub response for `/albums/{albumId}/photos`, sends a request to the application's `/api/albums/{albumId}` endpoint, and verifies the response body. * `shouldReturnServerErrorWhenPhotoServiceCallFailed()` configures WireMock to return a 500 status and verifies that the application propagates that status to the caller. [Stub using JSON mapping files](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/#stub-using-json-mapping-files) ---------------------------------------------------------------------------------------------------------------------------------------- Instead of using the WireMock Java API, you can configure stubs with JSON mapping files. Create `src/test/resources/wiremock/mappings/get-album-photos.json`: { "mappings": [\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/([0-9]+)/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "bodyFileName": "album-photos-resp-200.json"\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/2/photos"\ },\ "response": {\ "status": 500,\ "headers": {\ "Content-Type": "application/json"\ }\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/3/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "jsonBody": []\ }\ }\ ] } Create the response body file at `src/test/resources/wiremock/__files/album-photos-resp-200.json`: [\ {\ "id": 1,\ "title": "accusamus beatae ad facilis cum similique qui sunt",\ "url": "https://via.placeholder.com/600/92c952",\ "thumbnailUrl": "https://via.placeholder.com/150/92c952"\ },\ {\ "id": 2,\ "title": "reprehenderit est deserunt velit ipsam",\ "url": "https://via.placeholder.com/600/771796",\ "thumbnailUrl": "https://via.placeholder.com/150/771796"\ }\ ] Initialize WireMock to load stubs from the mapping files: @RegisterExtension static WireMockExtension wireMockServer = WireMockExtension .newInstance() .options( wireMockConfig().dynamicPort().usingFilesUnderClasspath("wiremock") ) .build(); With mapping-based stubs in place, create `AlbumControllerWireMockMappingTests.java`: package com.testcontainers.demo; import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import com.github.tomakehurst.wiremock.junit5.WireMockExtension; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; @SpringBootTest(webEnvironment = RANDOM_PORT) class AlbumControllerWireMockMappingTests { @LocalServerPort private Integer port; @RegisterExtension static WireMockExtension wireMockServer = WireMockExtension .newInstance() .options( wireMockConfig().dynamicPort().usingFilesUnderClasspath("wiremock") ) .build(); @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add("photos.api.base-url", wireMockServer::baseUrl); } @BeforeEach void setUp() { RestAssured.port = port; } @Test void shouldGetAlbumById() { Long albumId = 1L; given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(2)); } @Test void shouldReturnServerErrorWhenPhotoServiceCallFailed() { Long albumId = 2L; given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(500); } @Test void shouldReturnEmptyPhotos() { Long albumId = 3L; given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(0)); } } The tests don't need inline stub definitions because WireMock loads the mappings automatically from the classpath. [Test using the Testcontainers WireMock module](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/#test-using-the-testcontainers-wiremock-module) ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ The [Testcontainers WireMock module](https://testcontainers.com/modules/wiremock/) provisions WireMock as a standalone Docker container, based on [WireMock Docker](https://github.com/wiremock/wiremock-docker) . This approach is useful when you want complete isolation between the test JVM and the mock server. Create a mock configuration file at `src/test/resources/com/testcontainers/demo/AlbumControllerTestcontainersTests/mocks-config.json`: { "mappings": [\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/([0-9]+)/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "bodyFileName": "album-photos-response.json"\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/2/photos"\ },\ "response": {\ "status": 500,\ "headers": {\ "Content-Type": "application/json"\ }\ }\ },\ {\ "request": {\ "method": "GET",\ "urlPattern": "/albums/3/photos"\ },\ "response": {\ "status": 200,\ "headers": {\ "Content-Type": "application/json"\ },\ "jsonBody": []\ }\ }\ ] } Create the response body file at `src/test/resources/com/testcontainers/demo/AlbumControllerTestcontainersTests/album-photos-response.json`: [\ {\ "id": 1,\ "title": "accusamus beatae ad facilis cum similique qui sunt",\ "url": "https://via.placeholder.com/600/92c952",\ "thumbnailUrl": "https://via.placeholder.com/150/92c952"\ },\ {\ "id": 2,\ "title": "reprehenderit est deserunt velit ipsam",\ "url": "https://via.placeholder.com/600/771796",\ "thumbnailUrl": "https://via.placeholder.com/150/771796"\ }\ ] Create `AlbumControllerTestcontainersTests.java`: package com.testcontainers.demo; import static io.restassured.RestAssured.given; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.Matchers.hasSize; import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; import io.restassured.RestAssured; import io.restassured.http.ContentType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; import org.springframework.test.context.DynamicPropertyRegistry; import org.springframework.test.context.DynamicPropertySource; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import org.wiremock.integrations.testcontainers.WireMockContainer; @SpringBootTest(webEnvironment = RANDOM_PORT) @Testcontainers class AlbumControllerTestcontainersTests { @LocalServerPort private Integer port; @Container static WireMockContainer wiremockServer = new WireMockContainer( "wiremock/wiremock:3.6.0" ) .withMapping( "photos-by-album", AlbumControllerTestcontainersTests.class, "mocks-config.json" ) .withFileFromResource( "album-photos-response.json", AlbumControllerTestcontainersTests.class, "album-photos-response.json" ); @DynamicPropertySource static void configureProperties(DynamicPropertyRegistry registry) { registry.add("photos.api.base-url", wiremockServer::getBaseUrl); } @BeforeEach void setUp() { RestAssured.port = port; } @Test void shouldGetAlbumById() { Long albumId = 1L; given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(2)); } @Test void shouldReturnServerErrorWhenPhotoServiceCallFailed() { Long albumId = 2L; given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(500); } @Test void shouldReturnEmptyPhotos() { Long albumId = 3L; given() .contentType(ContentType.JSON) .when() .get("/api/albums/{albumId}", albumId) .then() .statusCode(200) .body("albumId", is(albumId.intValue())) .body("photos", hasSize(0)); } } Here's what the test does: * The `@Testcontainers` and `@Container` annotations start a `WireMockContainer` using the `wiremock/wiremock:3.6.0` Docker image. * `withMapping()` loads stub mappings from `mocks-config.json`, and `withFileFromResource()` loads the response body file. * `@DynamicPropertySource` overrides `photos.api.base-url` to point at the WireMock container's base URL. * The tests don't contain inline stub definitions because WireMock loads them from the JSON configuration files. [Run tests and next steps »](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/) --- # Connecting services with Docker Compose | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Monitor a Golang application with Prometheus and Grafana](https://docs.docker.com/guides/go-prometheus-monitoring/) Learn how to containerize a Golang application and monitor it with Prometheus and Grafana. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/go/go-original.svg "Go") Go 45 minutes [1](https://docs.docker.com/guides/go-prometheus-monitoring/application/) [Understand the application](https://docs.docker.com/guides/go-prometheus-monitoring/application/) [2](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/) [Containerize your app](https://docs.docker.com/guides/go-prometheus-monitoring/containerize/) [3](https://docs.docker.com/guides/go-prometheus-monitoring/compose/) [Connecting services with Docker Compose](https://docs.docker.com/guides/go-prometheus-monitoring/compose/) [4](https://docs.docker.com/guides/go-prometheus-monitoring/develop/) [Develop your app](https://docs.docker.com/guides/go-prometheus-monitoring/develop/) [« Back to all guides](https://docs.docker.com/guides/) Connecting services with Docker Compose ======================================= Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * Now that you have containerized the Golang application, you will use Docker Compose to connect your services together. You will connect the Golang application, Prometheus, and Grafana services together to monitor the Golang application with Prometheus and Grafana. [Creating a Docker Compose file](https://docs.docker.com/guides/go-prometheus-monitoring/compose/#creating-a-docker-compose-file) ---------------------------------------------------------------------------------------------------------------------------------- Create a new file named `compose.yml` in the root directory of your Golang application. The Docker Compose file contains instructions to run multiple services and connect them together. Here is a Docker Compose file for a project that uses Golang, Prometheus, and Grafana. You will also find this file in the `go-prometheus-monitoring` directory. services: api: container_name: go-api build: context: . dockerfile: Dockerfile image: go-api:latest ports: - 8000:8000 networks: - go-network healthcheck: test: ["CMD", "curl", "-f", "http://localhost:8000/health"] interval: 30s timeout: 10s retries: 5 develop: watch: - path: . action: rebuild prometheus: container_name: prometheus image: prom/prometheus:v2.55.0 volumes: - ./Docker/prometheus.yml:/etc/prometheus/prometheus.yml ports: - 9090:9090 networks: - go-network grafana: container_name: grafana image: grafana/grafana:11.3.0 volumes: - ./Docker/grafana.yml:/etc/grafana/provisioning/datasources/datasource.yaml - grafana-data:/var/lib/grafana ports: - 3000:3000 networks: - go-network environment: - GF_SECURITY_ADMIN_USER=admin - GF_SECURITY_ADMIN_PASSWORD=password volumes: grafana-data: networks: go-network: driver: bridge [Understanding the Docker Compose file](https://docs.docker.com/guides/go-prometheus-monitoring/compose/#understanding-the-docker-compose-file) ------------------------------------------------------------------------------------------------------------------------------------------------ The Docker Compose file consists of three services: * **Golang application service**: This service builds the Golang application using the Dockerfile and runs it in a container. It exposes the application's port `8000` and connects to the `go-network` network. It also defines a health check to monitor the application's health. You have also used `healthcheck` to monitor the health of the application. The health check runs every 30 seconds and retries 5 times if the health check fails. The health check uses the `curl` command to check the `/health` endpoint of the application. Apart from the health check, you have also added a `develop` section to watch the changes in the application's source code and rebuild the application using the Docker Compose Watch feature. * **Prometheus service**: This service runs the Prometheus server in a container. It uses the official Prometheus image `prom/prometheus:v2.55.0`. It exposes the Prometheus server on port `9090` and connects to the `go-network` network. You have also mounted the `prometheus.yml` file from the `Docker` directory which is present in the root directory of your project. The `prometheus.yml` file contains the Prometheus configuration to scrape the metrics from the Golang application. This is how you connect the Prometheus server to the Golang application. global: scrape_interval: 10s evaluation_interval: 10s scrape_configs: - job_name: myapp static_configs: - targets: ["api:8000"] In the `prometheus.yml` file, you have defined a job named `myapp` to scrape the metrics from the Golang application. The `targets` field specifies the target to scrape the metrics from. In this case, the target is the Golang application running on port `8000`. The `api` is the service name of the Golang application in the Docker Compose file. The Prometheus server will scrape the metrics from the Golang application every 10 seconds. * **Grafana service**: This service runs the Grafana server in a container. It uses the official Grafana image `grafana/grafana:11.3.0`. It exposes the Grafana server on port `3000` and connects to the `go-network` network. You have also mounted the `grafana.yml` file from the `Docker` directory which is present in the root directory of your project. The `grafana.yml` file contains the Grafana configuration to add the Prometheus data source. This is how you connect the Grafana server to the Prometheus server. In the environment variables, you have set the Grafana admin user and password, which will be used to log in to the Grafana dashboard. apiVersion: 1 datasources: - name: Prometheus (Main) type: prometheus url: http://prometheus:9090 isDefault: true In the `grafana.yml` file, you have defined a Prometheus data source named `Prometheus (Main)`. The `type` field specifies the type of the data source, which is `prometheus`. The `url` field specifies the URL of the Prometheus server to fetch the metrics from. In this case, the URL is `http://prometheus:9090`. `prometheus` is the service name of the Prometheus server in the Docker Compose file. The `isDefault` field specifies whether the data source is the default data source in Grafana. Apart from the services, the Docker Compose file also defines a volume named `grafana-data` to persist the Grafana data and a network named `go-network` to connect the services together. You have created a custom network `go-network` to connect the services together. The `driver: bridge` field specifies the network driver to use for the network. [Building and running the services](https://docs.docker.com/guides/go-prometheus-monitoring/compose/#building-and-running-the-services) ---------------------------------------------------------------------------------------------------------------------------------------- Now that you have the Docker Compose file, you can build the services and run them together using Docker Compose. To build and run the services, run the following command in the terminal: $ docker compose up The `docker compose up` command builds the services defined in the Docker Compose file and runs them together. You will see the similar output in the terminal: ✔ Network go-prometheus-monitoring_go-network Created 0.0s ✔ Container grafana Created 0.3s ✔ Container go-api Created 0.2s ✔ Container prometheus Created 0.3s Attaching to go-api, grafana, prometheus go-api | [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. go-api | go-api | [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. go-api | - using env: export GIN_MODE=release go-api | - using code: gin.SetMode(gin.ReleaseMode) go-api | go-api | [GIN-debug] GET /metrics --> main.PrometheusHandler.func1 (3 handlers) go-api | [GIN-debug] GET /health --> main.main.func1 (4 handlers) go-api | [GIN-debug] GET /v1/users --> main.main.func2 (4 handlers) go-api | [GIN-debug] [WARNING] You trusted all proxies, this is NOT safe. We recommend you to set a value. go-api | Please check https://pkg.go.dev/github.com/gin-gonic/gin#readme-don-t-trust-all-proxies for details. go-api | [GIN-debug] Listening and serving HTTP on :8000 prometheus | ts=2025-03-15T05:57:06.676Z caller=main.go:627 level=info msg="No time or size retention was set so using the default time retention" duration=15d prometheus | ts=2025-03-15T05:57:06.678Z caller=main.go:671 level=info msg="Starting Prometheus Server" mode=server version="(version=2.55.0, branch=HEAD, revision=91d80252c3e528728b0f88d254dd720f6be07cb8)" grafana | logger=settings t=2025-03-15T05:57:06.865335506Z level=info msg="Config overridden from command line" arg="default.log.mode=console" grafana | logger=settings t=2025-03-15T05:57:06.865337131Z level=info msg="Config overridden from Environment variable" var="GF_PATHS_DATA=/var/lib/grafana" grafana | logger=ngalert.state.manager t=2025-03-15T05:57:07.088956839Z level=info msg="State . . grafana | logger=plugin.angulardetectorsprovider.dynamic t=2025-03-15T05:57:07.530317298Z level=info msg="Patterns update finished" duration=440.489125ms The services will start running, and you can access the Golang application at `http://localhost:8000`, Prometheus at `http://localhost:9090/health`, and Grafana at `http://localhost:3000`. You can also check the running containers using the `docker ps` command. $ docker ps [Summary](https://docs.docker.com/guides/go-prometheus-monitoring/compose/#summary) ------------------------------------------------------------------------------------ In this section, you learned how to connect services together using Docker Compose. You created a Docker Compose file to run multiple services together and connect them using networks. You also learned how to build and run the services using Docker Compose. Related information: * [Docker Compose overview](https://docs.docker.com/compose/) * [Compose file reference](https://docs.docker.com/reference/compose-file/) Next, you will learn how to develop the Golang application with Docker Compose and monitor it with Prometheus and Grafana. [Next steps](https://docs.docker.com/guides/go-prometheus-monitoring/compose/#next-steps) ------------------------------------------------------------------------------------------ In the next section, you will learn how to develop the Golang application with Docker. You will also learn how to use Docker Compose Watch to rebuild the image whenever you make changes to the code. Lastly, you will test the application and visualize the metrics in Grafana using Prometheus as the data source. [Developing your application »](https://docs.docker.com/guides/go-prometheus-monitoring/develop/) --- # Common challenges and questions | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Defining and running multi-container applications with Docker Compose](https://docs.docker.com/guides/docker-compose/) Simplify the process of defining, configuring, and running multi-container Docker applications. Product demo 10 minutes [1](https://docs.docker.com/guides/docker-compose/why/) [Why Docker Compose?](https://docs.docker.com/guides/docker-compose/why/) [2](https://docs.docker.com/guides/docker-compose/setup/) [Demo: set up and use Docker Compose](https://docs.docker.com/guides/docker-compose/setup/) [3](https://docs.docker.com/guides/docker-compose/common-questions/) [Common challenges and questions](https://docs.docker.com/guides/docker-compose/common-questions/) Resources: * [Overview of Docker Compose CLI](https://docs.docker.com/reference/cli/docker/compose/) * [Overview of Docker Compose](https://docs.docker.com/compose/) * [How Compose works](https://docs.docker.com/compose/intro/compose-application-model/) * [Using profiles with Compose](https://docs.docker.com/compose/how-tos/profiles/) * [Control startup and shutdown order with Compose](https://docs.docker.com/compose/how-tos/startup-order/) * [Compose Build Specification](https://docs.docker.com/compose/compose-file/build/) [« Back to all guides](https://docs.docker.com/guides/) Common challenges and questions =============================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * ### [Do I need to maintain a separate Compose file for my development, testing, and staging environments?](https://docs.docker.com/guides/docker-compose/common-questions/#do-i-need-to-maintain-a-separate-compose-file-for-my-development-testing-and-staging-environments) You don't necessarily need to maintain entirely separate Compose files for your development, testing, and staging environments. You can define all your services in a single Compose file (`compose.yaml`). You can use profiles to group service configurations specific to each environment (`dev`, `test`, `staging`). When you need to spin up an environment, you can activate the corresponding profiles. For example, to set up the development environment: $ docker compose --profile dev up This command starts only the services associated with the `dev` profile, leaving the rest inactive. For more information on using profiles, see [Using profiles with Compose](https://docs.docker.com/compose/how-tos/profiles/) . ### [How can I enforce the database service to start up before the frontend service?](https://docs.docker.com/guides/docker-compose/common-questions/#how-can-i-enforce-the-database-service-to-start-up-before-the-frontend-service) Docker Compose ensures services start in a specific order by using the `depends_on` property. This tells Compose to start the database service before even attempting to launch the frontend service. This is crucial since applications often rely on databases being ready for connections. However, `depends_on` only guarantees the order, not that the database is fully initialized. For a more robust approach, especially if your application relies on a prepared database (e.g., after migrations), consider [health checks](https://docs.docker.com/reference/compose-file/services/#healthcheck) . Here, you can configure the frontend to wait until the database passes its health check before starting. This ensures the database is not only up but also ready to handle requests. For more information on setting the startup order of your services, see [Control startup and shutdown order in Compose](https://docs.docker.com/compose/how-tos/startup-order/) . ### [Can I use Compose to build a Docker image?](https://docs.docker.com/guides/docker-compose/common-questions/#can-i-use-compose-to-build-a-docker-image) Yes, you can use Docker Compose to build Docker images. Docker Compose is a tool for defining and running multi-container applications. Even if your application isn't a multi-container application, Docker Compose can make it easier to run by defining all the `docker run` options in a file. To use Compose, you need a `compose.yaml` file. In this file, you can specify the build context and Dockerfile for each service. When you run the command `docker compose up --build`, Docker Compose will build the images for each service and then start the containers. For more information on building Docker images using Compose, see the [Compose Build Specification](https://docs.docker.com/compose/compose-file/build/) . ### [What is the difference between Docker Compose and Dockerfile?](https://docs.docker.com/guides/docker-compose/common-questions/#what-is-the-difference-between-docker-compose-and-dockerfile) A Dockerfile provides instructions to build a container image while a Compose file defines your running containers. Quite often, a Compose file references a Dockerfile to build an image to use for a particular service. ### [What is the difference between the `docker compose up` and `docker compose run` commands?](https://docs.docker.com/guides/docker-compose/common-questions/#what-is-the-difference-between-the-docker-compose-up-and-docker-compose-run-commands) The `docker compose up` command creates and starts all your services. It's perfect for launching your development environment or running the entire application. The `docker compose run` command focuses on individual services. It starts a specified service along with its dependencies, allowing you to run tests or perform one-off tasks within that container. --- # Run tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations using MockServer](https://docs.docker.com/guides/testcontainers-java-mockserver/) Learn how to create a Spring Boot application that integrates with external REST APIs, then test those integrations using Testcontainers and MockServer. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-mockserver/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-mockserver/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Run tests and next steps ======================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Run the tests](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/#run-the-tests) -------------------------------------------------------------------------------------------------------- $ ./mvnw test Or with Gradle: $ ./gradlew test You should see the MockServer Docker container start in the console output. It acts as the photo service, serving mock responses based on the configured expectations. All tests should pass. [Summary](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/#summary) -------------------------------------------------------------------------------------------- You built a Spring Boot application that integrates with an external REST API using declarative HTTP clients, then tested that integration using the Testcontainers MockServer module. Testing at the HTTP protocol level instead of mocking Java methods lets you catch serialization issues and simulate realistic failure scenarios. To learn more about Testcontainers, visit the [Testcontainers overview](https://testcontainers.com/getting-started/) . [Further reading](https://docs.docker.com/guides/testcontainers-java-mockserver/run-tests/#further-reading) ------------------------------------------------------------------------------------------------------------ * [Testcontainers MockServer module](https://java.testcontainers.org/modules/mockserver/) * [MockServer documentation](https://www.mock-server.com/) * [Testcontainers JUnit 5 quickstart](https://java.testcontainers.org/quickstart/junit_5_quickstart/) --- # Run tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing Spring Boot Kafka Listener using Testcontainers](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/) Learn how to create a Spring Boot application with a Kafka listener that persists data in MySQL, then test it using Testcontainers Kafka and MySQL modules with Awaitility. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Run tests and next steps ======================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Run the tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/#run-the-tests) --------------------------------------------------------------------------------------------------------------- $ ./mvnw test Or with Gradle: $ ./gradlew test You should see the Kafka and MySQL Docker containers start and all tests pass. After the tests finish, the containers stop and are removed automatically. [Summary](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/#summary) --------------------------------------------------------------------------------------------------- Testing with real Kafka and MySQL instances gives you more confidence in the correctness of your code than mocks or embedded alternatives. The Testcontainers library manages the container lifecycle so that your integration tests run against the same services you use in production. To learn more about Testcontainers, visit the [Testcontainers overview](https://testcontainers.com/getting-started/) . [Further reading](https://docs.docker.com/guides/testcontainers-java-spring-boot-kafka/run-tests/#further-reading) ------------------------------------------------------------------------------------------------------------------- * [Getting started with Testcontainers in a Java Spring Boot project](https://testcontainers.com/guides/testing-spring-boot-rest-api-using-testcontainers/) * [The simplest way to replace H2 with a real database for testing](https://testcontainers.com/guides/replace-h2-with-real-database-for-testing/) * [Awaitility](http://www.awaitility.org/) * [Testcontainers Kafka module](https://java.testcontainers.org/modules/kafka/) * [Testcontainers MySQL module](https://java.testcontainers.org/modules/databases/mysql/) --- # Run tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing REST API integrations using WireMock](https://docs.docker.com/guides/testcontainers-java-wiremock/) Learn how to create a Spring Boot application that integrates with external REST APIs, then test those integrations using Testcontainers and WireMock. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 20 minutes [1](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-wiremock/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-wiremock/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Run tests and next steps ======================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Run the tests](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/#run-the-tests) ------------------------------------------------------------------------------------------------------ $ ./mvnw test Or with Gradle: $ ./gradlew test You should see the WireMock Docker container start in the console output. It acts as the photo service, serving mock responses based on the configured expectations. All tests should pass. [Summary](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/#summary) ------------------------------------------------------------------------------------------ You built a Spring Boot application that integrates with an external REST API, then tested that integration using three different approaches: * WireMock JUnit 5 extension with inline stubs * WireMock JUnit 5 extension with JSON mapping files * Testcontainers WireMock module running WireMock in a Docker container Testing at the HTTP protocol level instead of mocking Java methods lets you catch serialization issues and simulate realistic failure scenarios. To learn more about Testcontainers, visit the [Testcontainers overview](https://testcontainers.com/getting-started/) . [Further reading](https://docs.docker.com/guides/testcontainers-java-wiremock/run-tests/#further-reading) ---------------------------------------------------------------------------------------------------------- * [Testcontainers WireMock module](https://testcontainers.com/modules/wiremock/) * [WireMock documentation](https://wiremock.org/docs/) * [Testcontainers JUnit 5 quickstart](https://java.testcontainers.org/quickstart/junit_5_quickstart/) --- # Run tests | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Guides](https://docs.docker.com/guides/) * [Get started](https://docs.docker.com/get-started/) * [Manuals](https://docs.docker.com/manuals/) * [Reference](https://docs.docker.com/reference/) [Testing a Spring Boot REST API with Testcontainers](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/) Learn how to create a Spring Boot REST API with Spring Data JPA and PostgreSQL, then test it using Testcontainers and REST Assured. ![](https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/java/java-original.svg "Java") Java Testing with Docker 25 minutes [1](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/) [Create the project](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/create-project/) [2](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/) [Write tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/write-tests/) [3](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/) [Run tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/) [« Back to all guides](https://docs.docker.com/guides/) Run tests and next steps ======================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude Table of contents * * * [Run the tests](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/#run-the-tests) ------------------------------------------------------------------------------------------------------------------ $ ./mvnw test Or with Gradle: $ ./gradlew test You should see the Postgres Docker container start and all tests pass. After the tests finish, the container stops and is removed automatically. [Summary](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/#summary) ------------------------------------------------------------------------------------------------------ The Testcontainers library helps you write integration tests by using the same type of database (Postgres) that you use in production, instead of mocks or in-memory databases. Because you test against real services, you're free to refactor code and still verify that the application works as expected. To learn more about Testcontainers, visit the [Testcontainers overview](https://testcontainers.com/getting-started/) . [Further reading](https://docs.docker.com/guides/testcontainers-java-spring-boot-rest-api/run-tests/#further-reading) ---------------------------------------------------------------------------------------------------------------------- * [Testcontainers JUnit 5 quickstart](https://java.testcontainers.org/quickstart/junit_5_quickstart/) * [Testcontainers Postgres module](https://java.testcontainers.org/modules/databases/postgres/) * [Testcontainers JDBC support](https://java.testcontainers.org/modules/databases/jdbc/) --- # Unknown CLI Cheat Sheet Build an Image from a Dockerfile Build an Image from a Dockerfile without the cache docker build -t . –no-cache List local images docker images Delete an Image docker rmi Remove all unused images docker image prune Login into Docker docker login -u Publish an image to Docker Hub docker push / Search Hub for an image docker search Pull an image from a Docker Hub docker pull Create and run a container from an image, with a custom name: docker run --name Run a container with and publish a container’s port(s) to the host. docker run -p : Run a container in the background docker run -d Start or stop an existing container: docker start|stop (or ) Remove a stopped container: docker rm Open a shell inside a running container: docker exec -it sh Fetch and follow the logs of a container: docker logs -f To inspect a running container: docker inspect (or ) To list currently running containers: docker ps List all docker containers (running and stopped): docker ps --all View resource usage stats docker container stats GENERAL COMMANDS Docker provides the ability to package and run an application in a loosely isolated environment called a container. The isolation and security allows you to run many containers simultaneously on a given host. Containers are lightweight and contain everything needed to run the application, so you do not need to rely on what is currently installed on the host. You can easily share containers while you work, and be sure that everyone you share with gets the same container that works in the same way. IMAGES Docker images are a lightweight, standalone, executable package of software that includes everything needed to run an application: code, runtime, system tools, system libraries and settings. DOCKER HUB Docker Hub is a service provided by Docker for finding and sharing container images with your team. Learn more and find images at https://hub.docker.com CONTAINERS A container is a runtime instance of a docker image. A container will always run the same, regardless of the infrastructure. Containers isolate software from its environment and ensure that it works uniformly despite differences for instance between development and staging. Start the docker daemon docker -d Get help with Docker. Can also use –help on all subcommands docker --help Display system-wide information docker info INSTALLATION Docker Desktop is available for Mac, Linux and Windows https://docs.docker.com/desktop View example projects that use Docker https://github.com/docker/awesome-compose Check out our docs for information on using Docker https://docs.docker.com docker build -t . --- # docker secret inspect | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Reference](https://docs.docker.com/reference/) * [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) docker secret inspect ===================== Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude | | | | --- | --- | | Description | Display detailed information on one or more secrets | | Usage | `docker secret inspect [OPTIONS] SECRET [SECRET...]` | Swarm This command works with the Swarm orchestrator. [Description](https://docs.docker.com/reference/cli/docker/secret/inspect/#description) ---------------------------------------------------------------------------------------- Inspects the specified secret. By default, this renders all results in a JSON array. If a format is specified, the given template will be executed for each result. Go's [text/template](https://pkg.go.dev/text/template) package describes all the details of the format. For detailed information about using secrets, refer to [manage sensitive data with Docker secrets](https://docs.docker.com/engine/swarm/secrets/) . > Note > > This is a cluster management command, and must be executed on a swarm manager node. To learn about managers and workers, refer to the [Swarm mode section](https://docs.docker.com/engine/swarm/) > in the documentation. [Options](https://docs.docker.com/reference/cli/docker/secret/inspect/#options) -------------------------------------------------------------------------------- | Option | Default | Description | | --- | --- | --- | | [`-f, --format`](https://docs.docker.com/reference/cli/docker/secret/inspect/#format) | | Format output using a custom template:
    'json': Print in JSON format
    'TEMPLATE': Print output using the given Go template.
    Refer to [https://docs.docker.com/go/formatting/](https://docs.docker.com/go/formatting/)
    for more information about formatting output with templates | | `--pretty` | | Print the information in a human friendly format | [Examples](https://docs.docker.com/reference/cli/docker/secret/inspect/#examples) ---------------------------------------------------------------------------------- ### [Inspect a secret by name or ID](https://docs.docker.com/reference/cli/docker/secret/inspect/#inspect-a-secret-by-name-or-id) You can inspect a secret, either by its name or ID. For example, given the following secret: $ docker secret ls ID NAME CREATED UPDATED eo7jnzguqgtpdah3cm5srfb97 my_secret 3 minutes ago 3 minutes ago $ docker secret inspect secret.json The output is in JSON format, for example: [\ {\ "ID": "eo7jnzguqgtpdah3cm5srfb97",\ "Version": {\ "Index": 17\ },\ "CreatedAt": "2017-03-24T08:15:09.735271783Z",\ "UpdatedAt": "2017-03-24T08:15:09.735271783Z",\ "Spec": {\ "Name": "my_secret",\ "Labels": {\ "env": "dev",\ "rev": "20170324"\ }\ }\ }\ ] ### [Format the output (--format)](https://docs.docker.com/reference/cli/docker/secret/inspect/#format) You can use the `--format` option to obtain specific information about a secret. The following example command outputs the creation time of the secret. $ docker secret inspect --format='{{.CreatedAt}}' eo7jnzguqgtpdah3cm5srfb97 2017-03-24 08:15:09.735271783 +0000 UTC --- # docker secret rm | Docker Docs Start a new chat When enabled, Gordon considers the current page you're viewing to provide more relevant answers. [Share feedback](https://github.com/docker/docs/issues/23966) Answers are generated based on the documentation. Back [Reference](https://docs.docker.com/reference/) * [Get started](https://docs.docker.com/get-started/) * [Guides](https://docs.docker.com/guides/) * [Manuals](https://docs.docker.com/manuals/) docker secret rm ================ Copy as Markdown Open Markdown Ask Docs AI Claude Open in Claude | | | | --- | --- | | Description | Remove one or more secrets | | Usage | `docker secret rm SECRET [SECRET...]` | | Aliases

    An alias is a short or memorable alternative for a longer command. | `docker secret remove` | Swarm This command works with the Swarm orchestrator. [Description](https://docs.docker.com/reference/cli/docker/secret/rm/#description) ----------------------------------------------------------------------------------- Removes the specified secrets from the swarm. For detailed information about using secrets, refer to [manage sensitive data with Docker secrets](https://docs.docker.com/engine/swarm/secrets/) . > Note > > This is a cluster management command, and must be executed on a swarm manager node. To learn about managers and workers, refer to the [Swarm mode section](https://docs.docker.com/engine/swarm/) > in the documentation. [Examples](https://docs.docker.com/reference/cli/docker/secret/rm/#examples) ----------------------------------------------------------------------------- This example removes a secret: $ docker secret rm secret.json sapth4csdo5b6wz2p5uimh5xg > Warning > > Unlike `docker rm`, this command does not ask for confirmation before removing a secret. ---