TL;DR Add dokku/wait as a service, then run
wait -c service:port
If you use Docker Compose, you probably have already tried to request a service that isn't ready, despite the container is running.
Actually "running" and "ready" are two different concepts from Docker's point of view. "Running" refers to the container state. "Ready" refers to the service running inside it.
You can control the order of service startup and shutdown with the depends_on option. (...) However, for startup Compose does not wait until a container is "ready" only until it’s running.
# docker-compose.yaml version: '3' services: postgres: image: postgres:12 environment: POSTGRES_PASSWORD: s3cr3t
Using the docker-compose.yaml file above, if we run these commands sequentially:
docker-compose up -d docker-compose exec postgres psql -U postgres -c "SELECT NOW()"
We'll probably get this output error:
psql: error: could not connect to server: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?
This error is due to the fact we try to run a SQL before the PostgreSQL is ready.
We therefore need to find a way to be sure the service is ready before making requests to it. That's make sense, but how to fix it?
Note that this problem may also happen to other services, because they follow the same pattern: container is running but the service isn't ready. Here a list of outputs for some popular services:
Failed connect to elasticsearch:9200; Connection refused
ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
Error: unable to perform an operation on node 'rabbit@a1b2c3d4e5f6'
Could not connect to Redis at redis:6379: Connection refused
Step 1: Add dokku/wait as a service
Let's change the docker-compose.yaml file by adding a new
# docker-compose.yaml version: '3' services: postgres: image: postgres:12 environment: POSTGRES_PASSWORD: s3cr3t wait: image: dokku/wait
Step 2: Ask dokku/wait to watch the target service
wait service we added to the docker-compose.yaml file is able to reach any service in the stack by giving its name and its exposed port (e.g.
postgres:5432) thanks to the network set up by Docker Composer.
We can then ask it to block until the database get ready.
docker-compose up -d docker-compose run wait -c postgres:5432 docker-compose exec postgres psql -U postgres -c "SELECT NOW()"
wait -c postgres:5432 command will block for some seconds the
psql -U postgres -c "SELECT NOW()" execution. That's why the SQL will work as expected.
Furthermore, we can wait for multiples services at once:
docker-compose run wait -c mysql:3306,redis:6379,rabbitmq:5672,elasticsearch:9200
There are other approaches1 to solve this problem, but they assume we have to install some script into the host machine or into each dependent service.
Adopting dokku/wait as suggested here is less intrusive because it's not needed to install a script on our machine neither on docker services stack.
Tip: We can even apply this to an existing project via docker-compose.override.yaml without changing anything in the project itself.