Skip to content

Instantly share code, notes, and snippets.

@nicerobot
Created June 27, 2017 14:30
Show Gist options
  • Star 14 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save nicerobot/1136dcfba6ce3da67ce3ded5101a4078 to your computer and use it in GitHub Desktop.
Save nicerobot/1136dcfba6ce3da67ce3ded5101a4078 to your computer and use it in GitHub Desktop.
A better wait-for-postgres.sh
#!/bin/bash -e
# wait-for-postgres.sh
# Adapted from https://docs.docker.com/compose/startup-order/
# Expects the necessary PG* variables.
until psql -c '\l'; do
echo >&2 "$(date +%Y%m%dt%H%M%S) Postgres is unavailable - sleeping"
sleep 1
done
echo >&2 "$(date +%Y%m%dt%H%M%S) Postgres is up - executing command"
exec ${@}
@srghma
Copy link

srghma commented Nov 14, 2018

here's better variant

#!/bin/sh -e

# MIT
# idea stolen from https://gist.github.com/nicerobot/1136dcfba6ce3da67ce3ded5101a4078

# USAGE
# DBNAME=postgres://... ./wait-for-postgres

# Retries a command on failure (idea stolen from http://fahdshariff.blogspot.com/2014/02/retrying-commands-in-shell-scripts.html).
# $1 - the max number of attempts
# $2 - the seconds to sleep
# $3... - the command to run
retry() {
  max_attempts="$1"; shift
  seconds="$1"; shift
  cmd="$@"
  attempt_num=1

  until $cmd
  do
    if [ $attempt_num -eq $max_attempts ]
    then
      echo "Attempt $attempt_num failed and there are no more attempts left!"
      return 1
    else
      echo "Attempt $attempt_num failed! Trying again in $seconds seconds..."
      attempt_num=`expr "$attempt_num" + 1`
      sleep "$seconds"
    fi
  done
}

retry 5 1 psql --dbname=$DBNAME -c '\l' >/dev/null

echo >&2 "$(date +%Y%m%dt%H%M%S) Postgres is up - executing command"

exec ${@}

@nicerobot
Copy link
Author

nicerobot commented Jan 12, 2019

"Better" is subjective. It's more versatile maybe. That doesn't always mean better ;-) Simplicity also matters and I didn't need a retry limit.

Also, I wouldn't provide the --dbname flag. psql supports PG* environment variables. Let the caller provide them in the way psql already expects.

I would also adhere to a different Bash style.

So if you want retries and want a truly better variant, I would write it like this:

#!/usr/bin/env bash

set -o errexit
set -o nounset
set -o pipefail

retry() {
  local max_attempts="${1}"; shift
  local retry_delay_seconds="${1}"; shift
  local cmd="${@}"
  local attempt_num=1

  until ${cmd}; do
    (( attempt_num >= max_attempts )) && {
      echo "Attempt ${attempt_num} failed and there are no more attempts left!"
      return 1
    }
    echo "Attempt ${attempt_num} failed! Trying again in ${retry_delay_seconds} seconds..."
    attempt_num=$[ attempt_num + 1 ]
    sleep ${retry_delay_seconds}
  done
}

retry 1>&2 ${MAX_ATTEMPTS:-5} ${RETRY_DELAY_SECONDS:-1} psql -c '\l'

psql "${@}"

@amikrop
Copy link

amikrop commented Apr 20, 2021

This example (as the original one in the docker website) is using the psql command. This command is not available in the "webapp" service though (only available in the "db" service). Am I getting something wrong?

@therohk
Copy link

therohk commented Nov 7, 2021

You will need to install the "postgresql-client" package for these commands to be available in the webapp.

The included pg_isready command achieves the same as above without the retry limit.

See the complete config using this technique here.

A simplified Dockerfile is shown below. You will need to include a pgsql link for hostname in docker-compose.yml.

FROM openjdk:11

RUN apt-get update && apt-get install -y procps curl net-tools telnet
RUN apt-get install -y postgresql-client

#other things

COPY build/libs/app-0.0.1.war app.war

EXPOSE 8080

CMD until pg_isready --host=pgsql; do sleep 1; done \
    && java -jar app.war

For the above script replace the CMD with :

CMD wait-for-postgres.sh && java -jar app.war

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment