How to Wait for All the Services in the Container to Be up before Exiting Docker Start?

Using Docker version 20.10.18, build b40c2f6 on Ubuntu 20.04. This is how I am building the image and starting up the container.

#!/usr/bin/env bash
# encoding:utf-8
IMAGE=tensorflow_serve
CONTAINER=topic_classification
docker image build --tag=$IMAGE ./
set -e # From this point onwards, any failure must exit the script
HOST_PORT=80
CONTAINER_PORT=8080
docker container created --hostname $CONTAINER --publish $HOST_PORT:$CONTAINER_PORT --name $CONTAINER $IMAGE
docker container start $CONTAINER # Exits immediately
sleep 40s # Will explain 

Now, starting the container takes some non-negligible time (about 30 seconds, to start up some internal service and mount some directories etc.) But I have noticed the start command exits even before the start up (inside the container) is complete.

Normally, this would not be a problem, but the point is, after starting up, my script moves on to run some integration tests assuming the container services are up (in reality, which are still booting up). I went around the problem by introducing the sleep command, but that does not look nice. It leaves me to estimate the start time, and add a good amount of buffer before I proceed to run the tests.

Is there any option to the start command that will block it until the container has started up all its services inside it? Or, am I approaching the whole problem in the wrong way, and should I use some very different commands instead?

Yes you do. Your “services” should run in the foreground to keep the container alive and you don’t need to use sleep anymore. Even if somehow you manage to wait until the processes start, the container could stop while the processes are still running.

Search for “s6-init” or “Supervisor” if you still want to run multple background services in one container but usually that is not recommended.

PS.: Please, use the “Tips and HowTos” category for shaing your tips and tricks not asking for tips to solve an issue. I moved to the topic to “DockerEngine”.

I may have given the wrong impression.
The script I have shown above, is to build the image and start up the container. That script is not running inside the container.
Inside the container, I already have my service running in the foreground, as expected. So keeping it alive is not an issue. The sleep command is not meant to keep the container the container alive.

But starting up the service, inside the container, is what takes some time, about 30s. That means once this command

docker container start $CONTAINER # Exits immediately
exits, the services are not up yet inside the container, they will be up in about 30s. The sleep that comes after this is meant to wait out that container boot up time.

I’m sorry, I don’t know why I didn’t understand your question last year. I read your original question again and it is pretty clear now that sleep was used outside the container. Thank you for coming back.

So there are two ways to start a container, but none of those would be a solution for you.

docker container run runs the container in the foreground by default. Meaning it “attaches” to the standard output and error stream of the container and you need -d or --detach to run it in detached mode so it can give your host prompt back immediately.

docker container start works the opposite way. I’m not sure why. By default, it runs the container in detached mode and you need to use the -a or --attach flag to run it in the foreground. If you do that, it will not give your host prompt back until the process in the container exits.

If the process in the container is not running indefinitely, you can run scripts after it, but there is no feature in Docker to wait on the host until the processes start in the container. You can do what healthcecks would do when you have multiple containers and one of your containers in docker compose depends on another container’s health. Start the container and check the status in a loop until you get the expected result. That could be checking the logs or sending HTTP, TCP, UDP requests before starting the actual tests.

Your integration testing tool may already have a way to wait for processes. I needed it for unit tests and this is how I did it: