eBook – Guide Spring Cloud – NPI EA (cat=Spring Cloud)
announcement - icon

Let's get started with a Microservice Architecture with Spring Cloud:

>> Join Pro and download the eBook

eBook – Mockito – NPI EA (tag = Mockito)
announcement - icon

Mocking is an essential part of unit testing, and the Mockito library makes it easy to write clean and intuitive unit tests for your Java code.

Get started with mocking and improve your application tests using our Mockito guide:

Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Reactive – NPI EA (cat=Reactive)
announcement - icon

Spring 5 added support for reactive programming with the Spring WebFlux module, which has been improved upon ever since. Get started with the Reactor project basics and reactive programming in Spring Boot:

>> Join Pro and download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Jackson – NPI EA (cat=Jackson)
announcement - icon

Do JSON right with Jackson

Download the E-book

eBook – HTTP Client – NPI EA (cat=Http Client-Side)
announcement - icon

Get the most out of the Apache HTTP Client

Download the E-book

eBook – Maven – NPI EA (cat = Maven)
announcement - icon

Get Started with Apache Maven:

Download the E-book

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

eBook – RwS – NPI EA (cat=Spring MVC)
announcement - icon

Building a REST API with Spring?

Download the E-book

Course – LS – NPI EA (cat=Jackson)
announcement - icon

Get started with Spring and Spring Boot, through the Learn Spring course:

>> LEARN SPRING
Course – RWSB – NPI EA (cat=REST)
announcement - icon

Explore Spring Boot 3 and Spring 6 in-depth through building a full REST API with the framework:

>> The New “REST With Spring Boot”

Course – LSS – NPI EA (cat=Spring Security)
announcement - icon

Yes, Spring Security can be complex, from the more advanced functionality within the Core to the deep OAuth support in the framework.

I built the security material as two full courses - Core and OAuth, to get practical with these more complex scenarios. We explore when and how to use each feature and code through it on the backing project.

You can explore the course here:

>> Learn Spring Security

Course – LSD – NPI EA (tag=Spring Data JPA)
announcement - icon

Spring Data JPA is a great way to handle the complexity of JPA with the powerful simplicity of Spring Boot.

Get started with Spring Data JPA through the guided reference course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (cat=Spring Boot)
announcement - icon

Refactor Java code safely — and automatically — with OpenRewrite.

Refactoring big codebases by hand is slow, risky, and easy to put off. That’s where OpenRewrite comes in. The open-source framework for large-scale, automated code transformations helps teams modernize safely and consistently.

Each month, the creators and maintainers of OpenRewrite at Moderne run live, hands-on training sessions — one for newcomers and one for experienced users. You’ll see how recipes work, how to apply them across projects, and how to modernize code with confidence.

Join the next session, bring your questions, and learn how to automate the kind of work that usually eats your sprint time.

Course – LJB – NPI EA (cat = Core Java)
announcement - icon

Code your way through and build up a solid, practical foundation of Java:

>> Learn Java Basics

Partner – LambdaTest – NPI EA (cat= Testing)
announcement - icon

Distributed systems often come with complex challenges such as service-to-service communication, state management, asynchronous messaging, security, and more.

Dapr (Distributed Application Runtime) provides a set of APIs and building blocks to address these challenges, abstracting away infrastructure so we can focus on business logic.

In this tutorial, we'll focus on Dapr's pub/sub API for message brokering. Using its Spring Boot integration, we'll simplify the creation of a loosely coupled, portable, and easily testable pub/sub messaging system:

>> Flexible Pub/Sub Messaging With Spring Boot and Dapr

1. Overview

In this article, we take a look at another well-established platform specific API — Java API Client for Docker.

Throughout the article, we comprehend the way of how to connect with a running Docker daemon and what type of important functionality the API offers to Java developers.

2. Maven Dependency

First, we need to add the main dependency into our pom.xml file:

<dependency>
    <groupId>com.github.docker-java</groupId>
    <artifactId>docker-java</artifactId>
    <version>3.0.14</version>
</dependency>

At the time of writing the article, the latest version of the API is 3.0.14. Each release can be viewed either from the GitHub release page or from the Maven repository.

3. Using the Docker Client

DockerClient is where we can establish a connection between a Docker engine/daemon and our application.

By default, the Docker daemon can only be accessible at the unix:///var/run/docker.sock file. We can locally communicate with the Docker engine listening on the Unix socket unless otherwise configured.

Here, we apply to the DockerClientBuilder class to create a connection by accepting the default settings:

DockerClient dockerClient = DockerClientBuilder.getInstance().build();

Similarly, we can open a connection in two steps:

DefaultDockerClientConfig.Builder config 
  = DefaultDockerClientConfig.createDefaultConfigBuilder();
DockerClient dockerClient = DockerClientBuilder
  .getInstance(config)
  .build();

Since engines could rely on other characteristics, the client is also configurable with different conditions.

For example, the builder accepts a server URL, that is, we can update the connection value if the engine is available on port 2375:

DockerClient dockerClient
  = DockerClientBuilder.getInstance("tcp://docker.baeldung.com:2375").build();

Note that we need to prepend the connection string with unix:// or tcp:// depending on the connection type.

If we go one step further, we can end up with a more advanced configuration using the DefaultDockerClientConfig class:

DefaultDockerClientConfig config
  = DefaultDockerClientConfig.createDefaultConfigBuilder()
    .withRegistryEmail("[email protected]")
    .withRegistryPassword("baeldung")
    .withRegistryUsername("baeldung")
    .withDockerCertPath("/home/baeldung/.docker/certs")
    .withDockerConfig("/home/baeldung/.docker/")
    .withDockerTlsVerify("1")
    .withDockerHost("tcp://docker.baeldung.com:2376").build();

DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();

Likewise, we can carry out the same approach using Properties:

Properties properties = new Properties();
properties.setProperty("registry.email", "[email protected]");
properties.setProperty("registry.password", "baeldung");
properties.setProperty("registry.username", "baaldung");
properties.setProperty("DOCKER_CERT_PATH", "/home/baeldung/.docker/certs");
properties.setProperty("DOCKER_CONFIG", "/home/baeldung/.docker/");
properties.setProperty("DOCKER_TLS_VERIFY", "1");
properties.setProperty("DOCKER_HOST", "tcp://docker.baeldung.com:2376");

DefaultDockerClientConfig config
  = DefaultDockerClientConfig.createDefaultConfigBuilder()
    .withProperties(properties).build();

DockerClient dockerClient = DockerClientBuilder.getInstance(config).build();

Another choice unless we configure the engine’s settings in the source code is to set corresponding environment variables so that we can only consider the default instantiation of DockerClient in the project:

export DOCKER_CERT_PATH=/home/baeldung/.docker/certs
export DOCKER_CONFIG=/home/baeldung/.docker/
export DOCKER_TLS_VERIFY=1
export DOCKER_HOST=tcp://docker.baeldung.com:2376

4. Container Management

The API allows us a variety of choices about container management. Let’s look at each one of them.

4.1. List Containers

Now that we have an established connection, we can list all the running containers located on the Docker host:

List<Container> containers = dockerClient.listContainersCmd().exec();

Provided that showing the running containers doesn’t appeal the need, we can make use of the offered options to query containers.

In this case, we display containers with the “exited” status:

List<Container> containers = dockerClient.listContainersCmd()
  .withShowSize(true)
  .withShowAll(true)
  .withStatusFilter("exited").exec()

It’s an equivalent of:

$ docker ps -a -s -f status=exited
# or 
$ docker container ls -a -s -f status=exited

4.2. Create a Container

Creating a container is served with the createContainerCmd method. We can declare more complex declaration using the available methods starting with the “with” prefix.

Let’s assume that we have a docker create command defining a host-dependent MongoDB container listening internally on port 27017:

$ docker create --name mongo \
  --hostname=baeldung \
  -e MONGO_LATEST_VERSION=3.6 \
  -p 9999:27017 \
  -v /Users/baeldung/mongo/data/db:/data/db \
  mongo:3.6 --bind_ip_all

We’re able to bootstrap the same container along with its configurations programmatically:

CreateContainerResponse container
  = dockerClient.createContainerCmd("mongo:3.6")
    .withCmd("--bind_ip_all")
    .withName("mongo")
    .withHostName("baeldung")
    .withEnv("MONGO_LATEST_VERSION=3.6")
    .withPortBindings(PortBinding.parse("9999:27017"))
    .withBinds(Bind.parse("/Users/baeldung/mongo/data/db:/data/db")).exec();

4.3. Start, Stop, and Kill a Container

Once we create the container, we can start, stop and kill it by name or id respectively:

dockerClient.startContainerCmd(container.getId()).exec();

dockerClient.stopContainerCmd(container.getId()).exec();

dockerClient.killContainerCmd(container.getId()).exec();

4.4. Inspect a Container

The inspectContainerCmd method takes a String argument which indicates the name or id of a container. Using this method, we can observe the metadata of a container directly:

InspectContainerResponse container 
  = dockerClient.inspectContainerCmd(container.getId()).exec();

4.5. Snapshot a Container

Similar to the docker commit command, we can create a new image using the commitCmd method.

In our example, the scenario is, we previously run an alpine:3.6 container whose id is “3464bb547f88” and installed git on top of it.

Now, we want to create a new image snapshot from the container:

String snapshotId = dockerClient.commitCmd("3464bb547f88")
  .withAuthor("Baeldung <[email protected]>")
  .withEnv("SNAPSHOT_YEAR=2018")
  .withMessage("add git support")
  .withCmd("git", "version")
  .withRepository("alpine")
  .withTag("3.6.git").exec();

Since our new image bundled with git remains on the host, we can search it on the Docker host:

$ docker image ls alpine --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
alpine     3.6.git

5. Image Management

There are a few applicable commands we are given to manage image operations.

5.1. List Images

To list all the available images including dangling images on the Docker host, we need to apply to the listImagesCmd method:

List<Image> images = dockerClient.listImagesCmd().exec();

If we have two images on our Docker Host, we should obtain the Image objects of them at run-time. The images we look for are:

$ docker image ls --format "table {{.Repository}} {{.Tag}}"
REPOSITORY TAG
alpine     3.6
mongo      3.6

Next to this, to see the intermediate images, we need to request it explicitly:

List<Image> images = dockerClient.listImagesCmd()
  .withShowAll(true).exec();

If only displaying the dangling images is the case, the withDanglingFilter method must be considered:

List<Image> images = dockerClient.listImagesCmd()
  .withDanglingFilter(true).exec();

5.2. Build an Image

Let’s focus on the way of building an image using the API. The buildImageCmd method builds Docker images from a Dockerfile. In our project, we already have one Dockerfile which gives an Alpine image with git installed:

FROM alpine:3.6

RUN apk --update add git openssh && \
  rm -rf /var/lib/apt/lists/* && \
  rm /var/cache/apk/*

ENTRYPOINT ["git"]
CMD ["--help"]

The new image will be built without using cache and before starting the building process, in any case, Docker engine will attempt to pull the newer version of alpine:3.6. If everything goes well, we should eventually see the image with the given name, alpine:git:

String imageId = dockerClient.buildImageCmd()
  .withDockerfile(new File("path/to/Dockerfile"))
  .withPull(true)
  .withNoCache(true)
  .withTag("alpine:git")
  .exec(new BuildImageResultCallback())
  .awaitImageId();

5.3. Inspect an Image

We can inspect the low-level information about an image thanks to the inspectImageCmd method:

InspectImageResponse image 
  = dockerClient.inspectImageCmd("161714540c41").exec();

5.4. Tag an Image

Adding a tag to our image is quite simple using the docker tag command, so the API is no exception. We can carry out the same intention with the tagImageCmd method as well. To tag a Docker image with id 161714540c41 into the baeldung/alpine repository with git:

String imageId = "161714540c41";
String repository = "baeldung/alpine";
String tag = "git";

dockerClient.tagImageCmd(imageId, repository, tag).exec();

We would list the newly created image, and there it is:

$ docker image ls --format "table {{.Repository}} {{.Tag}}"
REPOSITORY      TAG
baeldung/alpine git

5.5. Push an Image

Before sending out an image to a registry service, the docker client must be configured to cooperate with the service because working with registries need to be authenticated in advance.

Since we assume that the client was configured with Docker Hub, we can push the baeldung/alpine image to the baeldung DockerHub account:

dockerClient.pushImageCmd("baeldung/alpine")
  .withTag("git")
  .exec(new PushImageResultCallback())
  .awaitCompletion(90, TimeUnit.SECONDS);

We must abide by the duration of the process. In the example, we are waiting 90 seconds.

5.6. Pull an Image

To download images from registry services, we make use of the pullImageCmd method. In addition, if the image being pulled from a private registry, the client must know our credential otherwise the process ends up with a failure. Same as the pulling an image, we specify a callback along with a fixed period to pull an image:

dockerClient.pullImageCmd("baeldung/alpine")
  .withTag("git")
  .exec(new PullImageResultCallback())
  .awaitCompletion(30, TimeUnit.SECONDS);

To check out whether the mentioned image exists on the Docker host after pulling it:

$ docker images baeldung/alpine --format "table {{.Repository}} {{.Tag}}"
REPOSITORY      TAG
baeldung/alpine git

5.7. Remove an Image

Another simple function among the rest is the removeImageCmd method. We can remove an image with its short or long ID:

dockerClient.removeImageCmd("beaccc8687ae").exec();

5.8. Search in Registry

To search an image from Docker Hub, the client comes with the searchImagesCmd method taking a String value which indicates a term. Here, we explore images related to a name containing ‘Java’ in Docker Hub:

List<SearchItem> items = dockerClient.searchImagesCmd("Java").exec();

The output returns first 25 related images in a list of SearchItem objects.

6. Volume Management

If Java projects need to interact with Docker for volumes, we should also take into account this section. Briefly, we look at the fundamental techniques of volumes provided by the Docker Java API.

6.1. List Volumes

All of the available volumes including named and unnamed are listed with:

ListVolumesResponse volumesResponse = dockerClient.listVolumesCmd().exec();
List<InspectVolumeResponse> volumes = volumesResponse.getVolumes();

6.2. Inspect a Volume

The inspectVolumeCmd method is the form to show the detailed information of a volume. We inspect the volume by specifying its short id:

InspectVolumeResponse volume 
  = dockerClient.inspectVolumeCmd("0220b87330af5").exec();

6.3. Create a Volume

The API serves two different options to create a volume. The non-arg createVolumeCmd method creates a volume where the name is given by Docker:

CreateVolumeResponse unnamedVolume = dockerClient.createVolumeCmd().exec();

Rather than using the default behavior, the helper method called withName lets us set a name to a volume:

CreateVolumeResponse namedVolume 
  = dockerClient.createVolumeCmd().withName("myNamedVolume").exec();

6.4. Remove a Volume

We can intuitively delete a volume from the Docker host using the removeVolumeCmd method. What is important to note that we cannot delete a volume if it is in use from a container. We remove the volume, myNamedVolume, from the volume list:

dockerClient.removeVolumeCmd("myNamedVolume").exec();

7. Network Management

Our last section is about managing network tasks with the API.

7.1. List Networks

We can display the list of network units with one of the conventional API methods starting with list:

List<Network> networks = dockerClient.listNetworksCmd().exec();

7.2. Create a Network

The equivalent of the docker network create command is conducted with the createNetworkCmd method. If we have a thirty party or a custom network driver, the withDriver method can accept them besides the built-in drivers. In our case, let’s create a bridge network whose name is baeldung:

CreateNetworkResponse networkResponse 
  = dockerClient.createNetworkCmd()
    .withName("baeldung")
    .withDriver("bridge").exec();

Furthermore, creating a network unit with the default settings doesn’t solve the problem, we can apply for other helper methods to construct an advanced network. Thus, to override the default subnetwork with a custom value:

CreateNetworkResponse networkResponse = dockerClient.createNetworkCmd()
  .withName("baeldung")
  .withIpam(new Ipam()
    .withConfig(new Config()
    .withSubnet("172.36.0.0/16")
    .withIpRange("172.36.5.0/24")))
  .withDriver("bridge").exec();

The same command we can run with the docker command is:

$ docker network create \
  --subnet=172.36.0.0/16 \
  --ip-range=172.36.5.0/24 \
  baeldung

7.3. Inspect a Network

Displaying the low-level details of a network is also covered in the API:

Network network 
  = dockerClient.inspectNetworkCmd().withNetworkId("baeldung").exec();

7.4. Remove a Network

We can safely remove a network unit with its name or id using the removeNetworkCmd method:

dockerClient.removeNetworkCmd("baeldung").exec();

8. Conclusion

In this extensive tutorial, we explored the various diverse functionality of the Java Docker API Client, along with several implementation approaches for deployment and management scenarios.

The code backing this article is available on GitHub. Once you're logged in as a Baeldung Pro Member, start learning and coding on the project.
Baeldung Pro – NPI EA (cat = Baeldung)
announcement - icon

Baeldung Pro comes with both absolutely No-Ads as well as finally with Dark Mode, for a clean learning experience:

>> Explore a clean Baeldung

Once the early-adopter seats are all used, the price will go up and stay at $33/year.

eBook – HTTP Client – NPI EA (cat=HTTP Client-Side)
announcement - icon

The Apache HTTP Client is a very robust library, suitable for both simple and advanced use cases when testing HTTP endpoints. Check out our guide covering basic request and response handling, as well as security, cookies, timeouts, and more:

>> Download the eBook

eBook – Java Concurrency – NPI EA (cat=Java Concurrency)
announcement - icon

Handling concurrency in an application can be a tricky process with many potential pitfalls. A solid grasp of the fundamentals will go a long way to help minimize these issues.

Get started with understanding multi-threaded applications with our Java Concurrency guide:

>> Download the eBook

eBook – Java Streams – NPI EA (cat=Java Streams)
announcement - icon

Since its introduction in Java 8, the Stream API has become a staple of Java development. The basic operations like iterating, filtering, mapping sequences of elements are deceptively simple to use.

But these can also be overused and fall into some common pitfalls.

To get a better understanding on how Streams work and how to combine them with other language features, check out our guide to Java Streams:

>> Join Pro and download the eBook

eBook – Persistence – NPI EA (cat=Persistence)
announcement - icon

Working on getting your persistence layer right with Spring?

Explore the eBook

Course – LS – NPI EA (cat=REST)

announcement - icon

Get started with Spring Boot and with core Spring, through the Learn Spring course:

>> CHECK OUT THE COURSE

Partner – Moderne – NPI EA (tag=Refactoring)
announcement - icon

Modern Java teams move fast — but codebases don’t always keep up. Frameworks change, dependencies drift, and tech debt builds until it starts to drag on delivery. OpenRewrite was built to fix that: an open-source refactoring engine that automates repetitive code changes while keeping developer intent intact.

The monthly training series, led by the creators and maintainers of OpenRewrite at Moderne, walks through real-world migrations and modernization patterns. Whether you’re new to recipes or ready to write your own, you’ll learn practical ways to refactor safely and at scale.

If you’ve ever wished refactoring felt as natural — and as fast — as writing code, this is a good place to start.

eBook Jackson – NPI EA – 3 (cat = Jackson)