Waiting on postgres readiness (in 1 line)

I recently launched postgres via docker, and needed a second command to wait for postgres to be ready before trying to connect to it (to apply migrations).

While there's an executable for checking postgres readiness called pg_isready, it's not installed everywhere by default.

Postgres doesn't have any HTTP interface, but it does listen for commands on a TCP port. Let's use netcat to send the following payload to postgres (in my case, postgres was running via docker on localhost (127.0.0.1) and port 5432)

echo -ne "\x00\x00\x00\x17\x00\x03\x00\x00user\x00username\x00\x00" | nc -w 3 127.0.0.1 5432 2>/dev/null | head -c1

If postgres is ready to accept connections, it will respond with R

$ echo -ne "\x00\x00\x00\x17\x00\x03\x00\x00user\x00username\x00\x00" | nc -w 3 127.0.0.1 5432 2>/dev/null | head -c1

R

Knowing that, we can use this request to send the same payload in a loop until postgres responds with R.

Via a makefile, it can be done this way:

@while ! test "`echo -ne "\x00\x00\x00\x17\x00\x03\x00\x00user\x00username\x00\x00" | nc -w 3 127.0.0.1 5432 2>/dev/null | head -c1`" = R; do echo "waiting on postgres..."; sleep 0.3; done;

This uses bash syntax, so we'll have to specify the shell as bash for this specific makefile target:

my-make-target: $(eval SHELL:=/bin/bash)

Putting it all together, this gives us:

my-make-target: $(eval SHELL:=/bin/bash)
    @while ! test "`echo -ne "\x00\x00\x00\x17\x00\x03\x00\x00user\x00username\x00\x00" | nc -w 3 127.0.0.1 5432 2>/dev/null | head -c1`" = R; do echo "waiting on postgres..."; sleep 0.3; done;
    echo "put your commands that depend on postgres being ready here..."

That's it ! We're now able to have any makefile command wait for postgres to be ready before running. And as a bonus, it holds in one line !