How to make one Docker container wait for another

feature image

We can set up the order of service startup and shutdown using the depends_on option. However, it won’t make a Docker container wait for another one to be ready. There are many situations when we need to be sure that the container on which our service depends is actually running.

What we’re going to build

For this example, we’re going to configure the metricbeat serivce to start only after the kibana container is ready. The example used in this post is available in the spring-boot-log4j-2-scaffolding repository.

Docker Compose config

Below you’ll find the docker-compose.yml file responsible for setting up the containers:

As you can see, we’re using a custom image configuration for the metricbeat service. You can read from the snippet below, that we’re using the original image with our own configuration file – metricbeat.yml:

The default Elastic Stack version is specified in the .env file:

Chaos resulting from unmet dependencies

Using solely the depends_on option in the docker-compose.yml file ensures that kibana is run first. However, launching a Kibana instance takes some time. Therefore, metricbeat will try to connect to the service that is not yet ready. As a consequence, the monitoring service will exit with the following error:

In this case, we need to force metricbeat to attempt a connection only when kibana is accessible. In other words, we have to make one Docker container wait for another.

Override the default entrypoint to make Docker wait for another container

We’re going to create a bash script delaying metricbeat connection to kibana. Furthermore, the script will be used as a new entrypoint when starting the metricbeat container. We will execute the default entrypoint only when the kibana dependency is met.

How to write a bash script that will wait until a container is ready

Put the following file alongside the Dockerfile for metricbeat:

You can learn more about scripts like this one in the Control startup and shutdown order in Compose documentation. The most important part is using the curl command to test whether the service on the given url is responding (line 4). We use the following options:

  • output <file> – Write to a file instead of stdout, in our case we write to the Unix null device as we don’t need any output.
  • silent – Silent mode, doesn’t show progress meter or error messages.
  • fail – Fail silently (no output at all) on HTTP errors, since we expect many errors.
  • head – Show document info only, often used for:

(…) testing hypertext links for validity, accessibility, and recent modification.

Return control to the original entrypoint

The last line of our script executes the original entrypoint, the /usr/local/bin/docker-entrypoint file (line 9). Therefore, the original script will be executed only after the Kibana instance is ready. You need to explore the image documentation to find the actual path to the default entrypoint.

Pass the kibana url to the metricbeat container

Our script requires the KIBANA_URL environment variable. Remember to add it to the docker-compose.yml:

Execute a custom script before the original entrypoint

To actually benefit from our script we have to execute the following actions while building the image:

  • copy our script to the container;
  • set it as the new entrypoint;
  • reconstruct settings/commands that the custom entrypoint may have removed.

Below you’ll find the adjusted Dockerfile (lines 5, 10, 11):

Verify that your custom entrypoint was executed

Start the services defined in your docker-compose.yml file with the following command:

If you changed the metricbeat settings, remember to rebuild the image with:

Examine logs printed in the metricbeat container:

make Docker wait for kibana container

As we can see, the metricbeat service doesn’t try connecting to the kibana service until the latter one is up.

Learn more on how to make one container wait for another

Photo by Vinicius Altava from Pexels

Leave a Reply

Your email address will not be published. Required fields are marked *