Containerization with Docker
- By Dawid Borycki
- 2/25/2026
- Why use containers?
- What is Docker?
- Running multiple containers
- Summary
Running multiple containers
Consider the processes (instances of applications) in the operating system. Typically, multiple applications run in parallel. The applications can use the system’s storage to persist files or data, interact with the system services, or use the network to communicate with external applications and services. For example, an application can consume a database in the Microsoft SQL Server running as a system service. Also, the same application can save photos to a drive and synchronize its local data with the remote service provisioned in the cloud. Of course, multiple applications can run on the same computer, communicating with the same Microsoft SQL Server but accessing different databases.
To achieve a similar functionality with multiple containerized applications, you simply need to extend what you now know about running and managing an individual container. Docker offers some additional tools to help you, including container networking, volumes, and Docker Compose.
As you remember, a container is created from a container image. The container then acts as a self-contained unit. It can process and store data, but when you close the container, the data stored in it is lost. To preserve this data, you can keep it on the host disk or in a database. You can use volumes to allow the container to persist the data on the Docker host. The database can run inside the container or in another container. For the latter case, you need to network multiple containers. Finally, the Docker Compose tool enables you to run and manage multiple containers, their networks, and volumes using a declarative approach. Take a closer look at each tool in the following sections.
Volumes
Docker provides several mechanisms for sharing data between containers, all of which are detailed in the official Docker documentation. One common feature of these approaches is that the data becomes available inside the container as any other element of the container’s file system. Therefore, the app or service running inside the container does not require any additional changes and can use the same data access methods as it would outside of containerization.
In this section, I will show you how to use volumes, which offer a preferred way to share data between containers. Specifically, you will learn how to create a volume, then spin up two containers that access the same volume.
To start, open Docker Desktop, navigate to the Volumes tab, and click Create (top-right corner). In the New Volume window that appears, name your volume aspnet-volume, and click Create (see Figure 3-12). A new volume will appear on the Volumes list in Docker Desktop.
FIGURE 3-12 Creating a volume using Docker Desktop.
Alternatively, you could create the new volume with a Docker CLI command:
docker volume create --name aspnet-volume
To show the list of volumes using Docker CLI, type:
docker volume ls
With your volume ready, you can spin up the first container. Use the same container image as before and supplement the docker command with two additional parameters: -d to run the container in detached (background) mode and -v to attach aspnet-volume such that it will be accessible inside the container from the /etc/data folder:
docker run -dp 80:8080 --name aspnet-sample-app -v aspnet-volume:/etc/data ↲
mcr.microsoft.com/dotnet/samples:aspnetapp
The command outputs the container identifier, as shown in Figure 3-13.
FIGURE 3-13 Attaching a volume to a container.
You can now go back to Docker Desktop, click the Containers tab, and then open the Terminal of the running container (under Actions select the ellipsis, View Details, then Exec). Then, in the container’s Terminal, type the following commands:
ls -la /etc/data echo "Hello from container 1">> /etc/data/test.file cat /etc/data/test.file
The first command lists the contents of the /etc/data folder to confirm that the volume is initially empty. The second creates a new text file inside that folder. The last command displays the contents of the new file.
To view the items in the volume, you can use Docker Desktop. Click Volumes, select a volume, and then click Data.
One done, one to go. Spin up another container using the same container image as before. This new container will use the same volume (aspnet-volume) and port 81 on the host. To avoid a name conflict, name the second container aspnet-sample-app2:
docker run -dp 81:8080 --name aspnet-sample-app2 -v aspnet-volume:/etc/data ↲
mcr.microsoft.com/dotnet/samples:aspnetapp
Again, the command outputs a container identifier. You now have two containers running, both with the same volume attached. In particular, the second container, aspnet-sample-app2, can access the test.file created from the aspnet-sample-app container. To see this, open the Terminal of the second container (aspnet-sample-app2) and type:
cat /etc/data/test.file
The message “Hello from container 1” displays as shown in Figure 3-14.
FIGURE 3-14 Accessing a file created by another container.
You’ve just learned how to use volumes to persist data. The file you created will be available to other containers until the volume is removed.
Networking
In the previous section, you spun up two containers. Both containers joined the default Docker network (bridge), which Docker creates along with two other default networks. This setup allows both containers to communicate with each other. To confirm this, use the Terminal of the first container (aspnet-sample-app) to type:
hostname -i
The command will output the local IP address of the first container within the Docker network (172.17.0.2, in my case). Open the Terminal of the second container (aspnet-sample-app2), and type the following commands:
hostname -i ping 172.17.0.2
The first command displays the local IP address of the second container (172.17.0.3, in my case). The second command sends a ping to the first container, and you should see a response similar to what is shown in Figure 3-15. This confirms that both containers are inside the same network and can communicate with each other.
FIGURE 3-15 Communication Between Containers.
The practical implication of this communication capability is that you can create multi-container solutions where, for example, one container runs a web application, and another container runs a database server that the application consumes.
Moving forward, you can even create a more advanced network architecture where some containers are isolated within their own networks, so they cannot be reached by containers running in other networks.
To create a new network, use the docker network create command. Once the network is created, you can instruct Docker to have a new container join that network using the --network parameter of the docker run command. Additionally, you can connect a running container to a specified network with the docker network connect command. For specific scenarios, you can refer to various Docker networking tutorials available in the official Docker documentation.
Docker Compose
Although your new containers can share data and communicate with each other, you had to create the volume and spin up the containers manually. Such manual management of Docker components is fine initially, but as your solution grows and becomes more complicated, you might prefer a more automated solution. To address this, Docker provides Compose.
Compose enables you to define, run, and manage Docker applications composed of multiple containers. To define a multi-container Docker application, you use a dedicated YAML file similar to Listing 3-1.
The file defines two application services: people.webapp and sql-server. The first corresponds to a hypothetical frontend, and the second is a database server. Thus, the compose creates two containers. It creates the first, people.webapp, using the container image with the sample ASP.NET Core app. It spins up the second container using an official containerized version of SQL Server. The definition of the first service also includes port mapping (equivalent to the -p parameter of the docker run command) and volume creation and attachment.
LISTING 3-1 A Docker Compose YAML File Defines an App Composed of Two Containers.
If you were to create such a solution manually, you would need to create a volume and then invoke the docker run command twice. Instead, you can use the Compose YAML file to provide a declaration for the manual container application and then provision all Docker components simply by issuing a single command:
docker compose up
To shut down the entire application, you use:
docker compose down
Notably, the containers created with Compose will join the same network and see each other by the hostnames set based on the service name. This means that people.webapp can reach sql-server via the service name on port 1433: sql-server,1433.

NOTE
TIP