June 25, 2019

Run in docker-compose, wait for the database

Do you use docker-compose to run your local development environment? Do you write your commands into a Makefile to protect your brain and your fingers from complex startup scripts?

If so, then you know how painful it is to tell your service to wait for the database before starting.

In a sane production environment, a service should always boot and patiently wait for the dependencies to become available, and signal their state through something like a readiness probe.

However, when building a prototype, you don’t necessarily want to build retry logic just yet.

Say you have two services defined in your docker-compose file, and that you want the second to only run when the first is ready to accept connections.

Or you want service to only spin up when database’s port is open.

The depends_on property of a docker-compose service will influence the order in which the containers are run, but will not wait until the service depended on is actually ready.

Here is a Make target that artificially waits for Postgres to come online before booting the service:

# Makefile

.PHONY: dev-up

dev-up: ## Run the service locally
	docker-compose build
	docker-compose up -d database

	# 👋 Here comes the interesting line 🤓
	#              👇 execute a command in the running database container
	docker-compose exec database sh -c 'until $$(nc -z localhost 5432); do { printf '.'; sleep 1; }; done'
	#                                            👆 scan the database port until it's open

	# 👇 Now that `nc` has found the port open, spin up the service
	docker-compose up -d service

The above Makefile target checks the Postgres default port. The same system can be used to check any kind of service that can be considered ready when it opens a port.

Note that the lovely nc tool is included by default in all the alpine-based Docker images; in other Linux distributions, you may need to install it.