Skip to content

Update postgres #6989

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 18, 2019
Merged

Conversation

yosifkit
Copy link
Member

Changes:

Changes:

- docker-library/postgres@5beb1d4: Update to 9.4.25-1.pgdg90+1
- docker-library/postgres@a0ec4f5: Update to 9.5.20
- docker-library/postgres@6dfdc0e: Update to 11.6
- docker-library/postgres@2addeda: Update to 11.6-1.pgdg90+1
- docker-library/postgres@f13fbe5: Update to 9.6.16-1.pgdg90+1
- docker-library/postgres@f2596e6: Update to 10.11-1.pgdg90+1
- docker-library/postgres@c1e547b: Update to 10.11
- docker-library/postgres@cac7a60: Update to 9.6.16
- docker-library/postgres@1d43a9d: Update to 12.1
- docker-library/postgres@06a831c: Update to 9.5.20-1.pgdg90+1
- docker-library/postgres@138e959: Update to 9.4.25
- docker-library/postgres@4a82eb9: Update to 12.1-1.pgdg100+1
- docker-library/postgres@0c29c35: Merge pull request docker-library/postgres#496 from infosiftr/functionalization
- docker-library/postgres@8fada98: Fixes from tianon's review
- docker-library/postgres@7c84645: Apply update.sh for new entrypoint
- docker-library/postgres@d1cc089: Improve _is_sourced check
- docker-library/postgres@6e85168: Resync function interfaces with MySQL, improve comments
- docker-library/postgres@2e70e71: Apply function name changes as discussed in docker-library/mysql#471
- docker-library/postgres@49fb876: Namespace functions for less conflict when sourced
- docker-library/postgres@48f2ad1: Functionalize the entrypoint to allow outside sourcing for extreme customizing of startup
@yosifkit
Copy link
Member Author

Diff:
diff --git a/_bashbrew-list b/_bashbrew-list
index e2a30c3..da21e29 100644
--- a/_bashbrew-list
+++ b/_bashbrew-list
@@ -2,27 +2,27 @@ postgres:9
 postgres:9-alpine
 postgres:9.4
 postgres:9.4-alpine
-postgres:9.4.24
-postgres:9.4.24-alpine
+postgres:9.4.25
+postgres:9.4.25-alpine
 postgres:9.5
 postgres:9.5-alpine
-postgres:9.5.19
-postgres:9.5.19-alpine
+postgres:9.5.20
+postgres:9.5.20-alpine
 postgres:9.6
 postgres:9.6-alpine
-postgres:9.6.15
-postgres:9.6.15-alpine
+postgres:9.6.16
+postgres:9.6.16-alpine
 postgres:10
 postgres:10-alpine
-postgres:10.10
-postgres:10.10-alpine
+postgres:10.11
+postgres:10.11-alpine
 postgres:11
 postgres:11-alpine
-postgres:11.5
-postgres:11.5-alpine
+postgres:11.6
+postgres:11.6-alpine
 postgres:12
 postgres:12-alpine
-postgres:12.0
-postgres:12.0-alpine
+postgres:12.1
+postgres:12.1-alpine
 postgres:alpine
 postgres:latest
diff --git a/postgres_10-alpine/Dockerfile b/postgres_10-alpine/Dockerfile
index 659669e..76c5c5f 100644
--- a/postgres_10-alpine/Dockerfile
+++ b/postgres_10-alpine/Dockerfile
@@ -22,8 +22,8 @@ ENV LANG en_US.utf8
 RUN mkdir /docker-entrypoint-initdb.d
 
 ENV PG_MAJOR 10
-ENV PG_VERSION 10.10
-ENV PG_SHA256 ad4f9b8575f98ed6091bf9bb2cb16f0e52795a5f66546c1f499ca5c69b21f253
+ENV PG_VERSION 10.11
+ENV PG_SHA256 0d5d14ff6b075655f4421038fbde3a5d7b418c26a249a187a4175600d7aecc09
 
 RUN set -ex \
 	\
diff --git a/postgres_10-alpine/docker-entrypoint.sh b/postgres_10-alpine/docker-entrypoint.sh
index 6dce8a1..857389d 100755
--- a/postgres_10-alpine/docker-entrypoint.sh
+++ b/postgres_10-alpine/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_WALDIR"
-		chown -R postgres "$POSTGRES_INITDB_WALDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_WALDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_WALDIR"
 	fi
 
-	exec su-exec postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
+		set -- --waldir "$POSTGRES_INITDB_WALDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec su-exec postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_10/Dockerfile b/postgres_10/Dockerfile
index 8dfafd0..0e559f3 100644
--- a/postgres_10/Dockerfile
+++ b/postgres_10/Dockerfile
@@ -71,7 +71,7 @@ RUN set -ex; \
 	apt-key list
 
 ENV PG_MAJOR 10
-ENV PG_VERSION 10.10-1.pgdg90+1
+ENV PG_VERSION 10.11-1.pgdg90+1
 
 RUN set -ex; \
 	\
diff --git a/postgres_10/docker-entrypoint.sh b/postgres_10/docker-entrypoint.sh
index 93ee4fb..02cb8e5 100755
--- a/postgres_10/docker-entrypoint.sh
+++ b/postgres_10/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_WALDIR"
-		chown -R postgres "$POSTGRES_INITDB_WALDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_WALDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_WALDIR"
 	fi
 
-	exec gosu postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
+		set -- --waldir "$POSTGRES_INITDB_WALDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec gosu postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_11-alpine/Dockerfile b/postgres_11-alpine/Dockerfile
index fe9d090..2bf84e9 100644
--- a/postgres_11-alpine/Dockerfile
+++ b/postgres_11-alpine/Dockerfile
@@ -22,8 +22,8 @@ ENV LANG en_US.utf8
 RUN mkdir /docker-entrypoint-initdb.d
 
 ENV PG_MAJOR 11
-ENV PG_VERSION 11.5
-ENV PG_SHA256 7fdf23060bfc715144cbf2696cf05b0fa284ad3eb21f0c378591c6bca99ad180
+ENV PG_VERSION 11.6
+ENV PG_SHA256 49924f7ff92965fdb20c86e0696f2dc9f8553e1563124ead7beedf8910c13170
 
 RUN set -ex \
 	\
diff --git a/postgres_11-alpine/docker-entrypoint.sh b/postgres_11-alpine/docker-entrypoint.sh
index 6dce8a1..857389d 100755
--- a/postgres_11-alpine/docker-entrypoint.sh
+++ b/postgres_11-alpine/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_WALDIR"
-		chown -R postgres "$POSTGRES_INITDB_WALDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_WALDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_WALDIR"
 	fi
 
-	exec su-exec postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
+		set -- --waldir "$POSTGRES_INITDB_WALDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec su-exec postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_11/Dockerfile b/postgres_11/Dockerfile
index 4eebd2c..29af439 100644
--- a/postgres_11/Dockerfile
+++ b/postgres_11/Dockerfile
@@ -71,7 +71,7 @@ RUN set -ex; \
 	apt-key list
 
 ENV PG_MAJOR 11
-ENV PG_VERSION 11.5-3.pgdg90+1
+ENV PG_VERSION 11.6-1.pgdg90+1
 
 RUN set -ex; \
 	\
diff --git a/postgres_11/docker-entrypoint.sh b/postgres_11/docker-entrypoint.sh
index 93ee4fb..02cb8e5 100755
--- a/postgres_11/docker-entrypoint.sh
+++ b/postgres_11/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_WALDIR"
-		chown -R postgres "$POSTGRES_INITDB_WALDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_WALDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_WALDIR"
 	fi
 
-	exec gosu postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
+		set -- --waldir "$POSTGRES_INITDB_WALDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec gosu postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_9-alpine/Dockerfile b/postgres_9-alpine/Dockerfile
index df1dd63..e349927 100644
--- a/postgres_9-alpine/Dockerfile
+++ b/postgres_9-alpine/Dockerfile
@@ -22,8 +22,8 @@ ENV LANG en_US.utf8
 RUN mkdir /docker-entrypoint-initdb.d
 
 ENV PG_MAJOR 9.6
-ENV PG_VERSION 9.6.15
-ENV PG_SHA256 3cd9fe9af247167f863030842c1a57f58bdf3e5d50a94997d34a802b6032170a
+ENV PG_VERSION 9.6.16
+ENV PG_SHA256 5c6cba9cc0df70ba2b128c4a87d0babfce7c0e2b888f70a9c8485745f66b22e7
 
 RUN set -ex \
 	\
diff --git a/postgres_9-alpine/docker-entrypoint.sh b/postgres_9-alpine/docker-entrypoint.sh
index 8f9cfcc..45bb6e1 100755
--- a/postgres_9-alpine/docker-entrypoint.sh
+++ b/postgres_9-alpine/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_XLOGDIR"
-		chown -R postgres "$POSTGRES_INITDB_XLOGDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_XLOGDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_XLOGDIR"
 	fi
 
-	exec su-exec postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --xlogdir $POSTGRES_INITDB_XLOGDIR"
+		set -- --xlogdir "$POSTGRES_INITDB_XLOGDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec su-exec postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_9.4-alpine/Dockerfile b/postgres_9.4-alpine/Dockerfile
index e2d5467..97c7a48 100644
--- a/postgres_9.4-alpine/Dockerfile
+++ b/postgres_9.4-alpine/Dockerfile
@@ -22,8 +22,8 @@ ENV LANG en_US.utf8
 RUN mkdir /docker-entrypoint-initdb.d
 
 ENV PG_MAJOR 9.4
-ENV PG_VERSION 9.4.24
-ENV PG_SHA256 52253d67dd46a7463a9d7c5e82bf959931fa4c11ec56293150210fa82a0f9429
+ENV PG_VERSION 9.4.25
+ENV PG_SHA256 cb98afaef4748de76c13202c14198e3e4717adde49fd9c90fdc81da877520928
 
 RUN set -ex \
 	\
diff --git a/postgres_9.4-alpine/docker-entrypoint.sh b/postgres_9.4-alpine/docker-entrypoint.sh
index 8f9cfcc..45bb6e1 100755
--- a/postgres_9.4-alpine/docker-entrypoint.sh
+++ b/postgres_9.4-alpine/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_XLOGDIR"
-		chown -R postgres "$POSTGRES_INITDB_XLOGDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_XLOGDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_XLOGDIR"
 	fi
 
-	exec su-exec postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --xlogdir $POSTGRES_INITDB_XLOGDIR"
+		set -- --xlogdir "$POSTGRES_INITDB_XLOGDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec su-exec postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_9.4/Dockerfile b/postgres_9.4/Dockerfile
index 82b1f57..f31ff3e 100644
--- a/postgres_9.4/Dockerfile
+++ b/postgres_9.4/Dockerfile
@@ -71,7 +71,7 @@ RUN set -ex; \
 	apt-key list
 
 ENV PG_MAJOR 9.4
-ENV PG_VERSION 9.4.24-1.pgdg90+1
+ENV PG_VERSION 9.4.25-1.pgdg90+1
 
 RUN set -ex; \
 	\
diff --git a/postgres_9.4/docker-entrypoint.sh b/postgres_9.4/docker-entrypoint.sh
index 3f984a1..17b0a68 100755
--- a/postgres_9.4/docker-entrypoint.sh
+++ b/postgres_9.4/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_XLOGDIR"
-		chown -R postgres "$POSTGRES_INITDB_XLOGDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_XLOGDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_XLOGDIR"
 	fi
 
-	exec gosu postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --xlogdir $POSTGRES_INITDB_XLOGDIR"
+		set -- --xlogdir "$POSTGRES_INITDB_XLOGDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec gosu postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_9.5-alpine/Dockerfile b/postgres_9.5-alpine/Dockerfile
index e1577b6..7cfaaad 100644
--- a/postgres_9.5-alpine/Dockerfile
+++ b/postgres_9.5-alpine/Dockerfile
@@ -22,8 +22,8 @@ ENV LANG en_US.utf8
 RUN mkdir /docker-entrypoint-initdb.d
 
 ENV PG_MAJOR 9.5
-ENV PG_VERSION 9.5.19
-ENV PG_SHA256 960caa26612bca8a3791d1c0bdc5c6d24b3d15841becb617470424edbc5e1bb3
+ENV PG_VERSION 9.5.20
+ENV PG_SHA256 925751b375cf975bebbe79753fbcb5fe85d7a62abe516d4c56861a6b877dde0d
 
 RUN set -ex \
 	\
diff --git a/postgres_9.5-alpine/docker-entrypoint.sh b/postgres_9.5-alpine/docker-entrypoint.sh
index 8f9cfcc..45bb6e1 100755
--- a/postgres_9.5-alpine/docker-entrypoint.sh
+++ b/postgres_9.5-alpine/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_XLOGDIR"
-		chown -R postgres "$POSTGRES_INITDB_XLOGDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_XLOGDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_XLOGDIR"
 	fi
 
-	exec su-exec postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --xlogdir $POSTGRES_INITDB_XLOGDIR"
+		set -- --xlogdir "$POSTGRES_INITDB_XLOGDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec su-exec postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_9.5/Dockerfile b/postgres_9.5/Dockerfile
index 53a2976..8645cb4 100644
--- a/postgres_9.5/Dockerfile
+++ b/postgres_9.5/Dockerfile
@@ -71,7 +71,7 @@ RUN set -ex; \
 	apt-key list
 
 ENV PG_MAJOR 9.5
-ENV PG_VERSION 9.5.19-1.pgdg90+1
+ENV PG_VERSION 9.5.20-1.pgdg90+1
 
 RUN set -ex; \
 	\
diff --git a/postgres_9.5/docker-entrypoint.sh b/postgres_9.5/docker-entrypoint.sh
index 3f984a1..17b0a68 100755
--- a/postgres_9.5/docker-entrypoint.sh
+++ b/postgres_9.5/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_XLOGDIR"
-		chown -R postgres "$POSTGRES_INITDB_XLOGDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_XLOGDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_XLOGDIR"
 	fi
 
-	exec gosu postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --xlogdir $POSTGRES_INITDB_XLOGDIR"
+		set -- --xlogdir "$POSTGRES_INITDB_XLOGDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec gosu postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_9/Dockerfile b/postgres_9/Dockerfile
index 205b0b1..32bcf4e 100644
--- a/postgres_9/Dockerfile
+++ b/postgres_9/Dockerfile
@@ -71,7 +71,7 @@ RUN set -ex; \
 	apt-key list
 
 ENV PG_MAJOR 9.6
-ENV PG_VERSION 9.6.15-1.pgdg90+1
+ENV PG_VERSION 9.6.16-1.pgdg90+1
 
 RUN set -ex; \
 	\
diff --git a/postgres_9/docker-entrypoint.sh b/postgres_9/docker-entrypoint.sh
index 3f984a1..17b0a68 100755
--- a/postgres_9/docker-entrypoint.sh
+++ b/postgres_9/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_XLOGDIR"
-		chown -R postgres "$POSTGRES_INITDB_XLOGDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_XLOGDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_XLOGDIR"
 	fi
 
-	exec gosu postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_XLOGDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --xlogdir $POSTGRES_INITDB_XLOGDIR"
+		set -- --xlogdir "$POSTGRES_INITDB_XLOGDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec gosu postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_alpine/Dockerfile b/postgres_alpine/Dockerfile
index ae7bd94..f611082 100644
--- a/postgres_alpine/Dockerfile
+++ b/postgres_alpine/Dockerfile
@@ -22,8 +22,8 @@ ENV LANG en_US.utf8
 RUN mkdir /docker-entrypoint-initdb.d
 
 ENV PG_MAJOR 12
-ENV PG_VERSION 12.0
-ENV PG_SHA256 cda2397215f758b793f741c86be05468257b0e6bcb1a6113882ab5d0df0855c6
+ENV PG_VERSION 12.1
+ENV PG_SHA256 a09bf3abbaf6763980d0f8acbb943b7629a8b20073de18d867aecdb7988483ed
 
 RUN set -ex \
 	\
diff --git a/postgres_alpine/docker-entrypoint.sh b/postgres_alpine/docker-entrypoint.sh
index 6dce8a1..857389d 100755
--- a/postgres_alpine/docker-entrypoint.sh
+++ b/postgres_alpine/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_WALDIR"
-		chown -R postgres "$POSTGRES_INITDB_WALDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_WALDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_WALDIR"
 	fi
 
-	exec su-exec postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
+		set -- --waldir "$POSTGRES_INITDB_WALDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec su-exec postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi
diff --git a/postgres_latest/Dockerfile b/postgres_latest/Dockerfile
index 274932e..b1da1db 100644
--- a/postgres_latest/Dockerfile
+++ b/postgres_latest/Dockerfile
@@ -71,7 +71,7 @@ RUN set -ex; \
 	apt-key list
 
 ENV PG_MAJOR 12
-ENV PG_VERSION 12.0-2.pgdg100+1
+ENV PG_VERSION 12.1-1.pgdg100+1
 
 RUN set -ex; \
 	\
diff --git a/postgres_latest/docker-entrypoint.sh b/postgres_latest/docker-entrypoint.sh
index 93ee4fb..02cb8e5 100755
--- a/postgres_latest/docker-entrypoint.sh
+++ b/postgres_latest/docker-entrypoint.sh
@@ -24,37 +24,46 @@ file_env() {
 	unset "$fileVar"
 }
 
-if [ "${1:0:1}" = '-' ]; then
-	set -- postgres "$@"
-fi
+# check to see if this file is being run or sourced from another script
+_is_sourced() {
+	# https://unix.stackexchange.com/a/215279
+	[ "${#FUNCNAME[@]}" -ge 2 ] \
+		&& [ "${FUNCNAME[0]}" = '_is_sourced' ] \
+		&& [ "${FUNCNAME[1]}" = 'source' ]
+}
+
+# used to create initial posgres directories and if run as root, ensure ownership to the "postgres" user
+docker_create_db_directories() {
+	local user; user="$(id -u)"
 
-# allow the container to be started with `--user`
-if [ "$1" = 'postgres' ] && [ "$(id -u)" = '0' ]; then
 	mkdir -p "$PGDATA"
-	chown -R postgres "$PGDATA"
 	chmod 700 "$PGDATA"
 
-	mkdir -p /var/run/postgresql
-	chown -R postgres /var/run/postgresql
-	chmod 775 /var/run/postgresql
+	# ignore failure since it will be fine when using the image provided directory; see also https://github.com/docker-library/postgres/pull/289
+	mkdir -p /var/run/postgresql || :
+	chmod 775 /var/run/postgresql || :
 
-	# Create the transaction log directory before initdb is run (below) so the directory is owned by the correct user
+	# Create the transaction log directory before initdb is run so the directory is owned by the correct user
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
 		mkdir -p "$POSTGRES_INITDB_WALDIR"
-		chown -R postgres "$POSTGRES_INITDB_WALDIR"
+		if [ "$user" = '0' ]; then
+			find "$POSTGRES_INITDB_WALDIR" \! -user postgres -exec chown postgres '{}' +
+		fi
 		chmod 700 "$POSTGRES_INITDB_WALDIR"
 	fi
 
-	exec gosu postgres "$BASH_SOURCE" "$@"
-fi
-
-if [ "$1" = 'postgres' ]; then
-	mkdir -p "$PGDATA"
-	chown -R "$(id -u)" "$PGDATA" 2>/dev/null || :
-	chmod 700 "$PGDATA" 2>/dev/null || :
+	# allow the container to be started with `--user`
+	if [ "$user" = '0' ]; then
+		find "$PGDATA" \! -user postgres -exec chown postgres '{}' +
+		find /var/run/postgresql \! -user postgres -exec chown postgres '{}' +
+	fi
+}
 
-	# look specifically for PG_VERSION, as it is expected in the DB dir
-	if [ ! -s "$PGDATA/PG_VERSION" ]; then
+# initialize empty PGDATA directory with new database via 'initdb'
+# arguments to `initdb` can be passed via POSTGRES_INITDB_ARGS or as arguments to this function
+# `initdb` automatically creates the "postgres", "template0", and "template1" dbnames
+# this is also where the database user is created, specified by `POSTGRES_USER` env
+docker_init_database_dir() {
 	# "initdb" is particular about the current user existing in "/etc/passwd", so we use "nss_wrapper" to fake that if necessary
 	# see https://github.com/docker-library/postgres/pull/253, https://github.com/docker-library/postgres/issues/359, https://cwrap.org/nss_wrapper.html
 	if ! getent passwd "$(id -u)" &> /dev/null && [ -e /usr/lib/libnss_wrapper.so ]; then
@@ -65,26 +74,23 @@ if [ "$1" = 'postgres' ]; then
 		echo "postgres:x:$(id -g):" > "$NSS_WRAPPER_GROUP"
 	fi
 
-		file_env 'POSTGRES_USER' 'postgres'
-		file_env 'POSTGRES_PASSWORD'
-
-		file_env 'POSTGRES_INITDB_ARGS'
 	if [ "$POSTGRES_INITDB_WALDIR" ]; then
-			export POSTGRES_INITDB_ARGS="$POSTGRES_INITDB_ARGS --waldir $POSTGRES_INITDB_WALDIR"
+		set -- --waldir "$POSTGRES_INITDB_WALDIR" "$@"
 	fi
-		eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"
+
+	eval 'initdb --username="$POSTGRES_USER" --pwfile=<(echo "$POSTGRES_PASSWORD") '"$POSTGRES_INITDB_ARGS"' "$@"'
 
 	# unset/cleanup "nss_wrapper" bits
 	if [ "${LD_PRELOAD:-}" = '/usr/lib/libnss_wrapper.so' ]; then
 		rm -f "$NSS_WRAPPER_PASSWD" "$NSS_WRAPPER_GROUP"
 		unset LD_PRELOAD NSS_WRAPPER_PASSWD NSS_WRAPPER_GROUP
 	fi
+}
 
+# print large warning if POSTGRES_PASSWORD is empty
+docker_verify_minimum_env() {
 	# check password first so we can output the warning before postgres
 	# messes it up
-		if [ -n "$POSTGRES_PASSWORD" ]; then
-			authMethod=md5
-
 	if [ "${#POSTGRES_PASSWORD}" -ge 100 ]; then
 		cat >&2 <<-'EOWARN'
 
@@ -97,7 +103,7 @@ if [ "$1" = 'postgres' ]; then
 
 		EOWARN
 	fi
-		else
+	if [ -z "$POSTGRES_PASSWORD" ]; then
 		# The - option suppresses leading tabs but *not* spaces. :)
 		cat >&2 <<-'EOWARN'
 			****************************************************
@@ -113,36 +119,19 @@ if [ "$1" = 'postgres' ]; then
 			****************************************************
 		EOWARN
 
-			authMethod=trust
 	fi
+}
 
-		{
-			echo
-			echo "host all all all $authMethod"
-		} >> "$PGDATA/pg_hba.conf"
-
-		# internal start of server in order to allow set-up using psql-client
-		# does not listen on external TCP/IP and waits until start finishes
-		PGUSER="${PGUSER:-$POSTGRES_USER}" \
-		pg_ctl -D "$PGDATA" \
-			-o "-c listen_addresses=''" \
-			-w start
-
-		file_env 'POSTGRES_DB' "$POSTGRES_USER"
-
-		export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
-		psql=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+# usage: docker_process_init_files [file [file [...]]]
+#    ie: docker_process_init_files /always-initdb.d/*
+# process initializer files, based on file extensions and permissions
+docker_process_init_files() {
+	# psql here for backwards compatiblilty "${psql[@]}"
+	psql=( docker_process_sql )
 
-		if [ "$POSTGRES_DB" != 'postgres' ]; then
-			"${psql[@]}" --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
-				CREATE DATABASE :"db" ;
-			EOSQL
 	echo
-		fi
-		psql+=( --dbname "$POSTGRES_DB" )
-
-		echo
-		for f in /docker-entrypoint-initdb.d/*; do
+	local f
+	for f; do
 		case "$f" in
 			*.sh)
 				# https://github.com/docker-library/postgres/issues/450#issuecomment-393167936
@@ -155,22 +144,133 @@ if [ "$1" = 'postgres' ]; then
 					. "$f"
 				fi
 				;;
-				*.sql)    echo "$0: running $f"; "${psql[@]}" -f "$f"; echo ;;
-				*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | "${psql[@]}"; echo ;;
+			*.sql)    echo "$0: running $f"; docker_process_sql -f "$f"; echo ;;
+			*.sql.gz) echo "$0: running $f"; gunzip -c "$f" | docker_process_sql; echo ;;
 			*)        echo "$0: ignoring $f" ;;
 		esac
 		echo
 	done
+}
+
+# Execute sql script, passed via stdin (or -f flag of pqsl)
+# usage: docker_process_sql [psql-cli-args]
+#    ie: docker_process_sql --dbname=mydb <<<'INSERT ...'
+#    ie: docker_process_sql -f my-file.sql
+#    ie: docker_process_sql <my-file.sql
+docker_process_sql() {
+	local query_runner=( psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --no-password )
+	if [ -n "$POSTGRES_DB" ]; then
+		query_runner+=( --dbname "$POSTGRES_DB" )
+	fi
 
+	"${query_runner[@]}" "$@"
+}
+
+# create initial database
+# uses environment variables for input: POSTGRES_DB
+docker_setup_db() {
+	if [ "$POSTGRES_DB" != 'postgres' ]; then
+		POSTGRES_DB= docker_process_sql --dbname postgres --set db="$POSTGRES_DB" <<-'EOSQL'
+			CREATE DATABASE :"db" ;
+		EOSQL
+		echo
+	fi
+}
+
+# Loads various settings that are used elsewhere in the script
+# This should be called before any other functions
+docker_setup_env() {
+	file_env 'POSTGRES_PASSWORD'
+
+	file_env 'POSTGRES_USER' 'postgres'
+	file_env 'POSTGRES_DB' "$POSTGRES_USER"
+	file_env 'POSTGRES_INITDB_ARGS'
+
+	declare -g DATABASE_ALREADY_EXISTS
+	# look specifically for PG_VERSION, as it is expected in the DB dir
+	if [ -s "$PGDATA/PG_VERSION" ]; then
+		DATABASE_ALREADY_EXISTS='true'
+	fi
+}
+
+# append md5 or trust auth to pg_hba.conf based on existence of POSTGRES_PASSWORD
+pg_setup_hba_conf() {
+	local authMethod='md5'
+	if [ -z "$POSTGRES_PASSWORD" ]; then
+		authMethod='trust'
+	fi
+
+	{
+		echo
+		echo "host all all all $authMethod"
+	} >> "$PGDATA/pg_hba.conf"
+}
+
+# start socket-only postgresql server for setting up or running scripts
+# all arguments will be passed along as arguments to `postgres` (via pg_ctl)
+docker_temp_server_start() {
+	if [ "$1" = 'postgres' ]; then
+		shift
+	fi
+	# internal start of server in order to allow setup using psql client
+	# does not listen on external TCP/IP and waits until start finishes (can be overridden via args)
 	PGUSER="${PGUSER:-$POSTGRES_USER}" \
+	pg_ctl -D "$PGDATA" \
+		-o "-c listen_addresses='' $([ "$#" -gt 0 ] && printf '%q ' "$@")" \
+		-w start
+}
+
+# stop postgresql server after done setting up user and running scripts
+docker_temp_server_stop() {
+	PGUSER="${PGUSER:-postgres}" \
 	pg_ctl -D "$PGDATA" -m fast -w stop
+}
+
+_main() {
+	# if first arg looks like a flag, assume we want to run postgres server
+	if [ "${1:0:1}" = '-' ]; then
+		set -- postgres "$@"
+	fi
+
+	if [ "$1" = 'postgres' ]; then
+		docker_setup_env
+		# setup data directories and permissions (when run as root)
+		docker_create_db_directories
+		if [ "$(id -u)" = '0' ]; then
+			# then restart script as postgres user
+			exec gosu postgres "$BASH_SOURCE" "$@"
+		fi
+
+		# only run initialization on an empty data directory
+		if [ -z "$DATABASE_ALREADY_EXISTS" ]; then
+			docker_verify_minimum_env
+			docker_init_database_dir
+			pg_setup_hba_conf
 
+			# PGPASSWORD is required for psql when authentication is required for 'local' connections via pg_hba.conf and is otherwise harmless
+			# e.g. when '--auth=md5' or '--auth-local=md5' is used in POSTGRES_INITDB_ARGS
+			export PGPASSWORD="${PGPASSWORD:-$POSTGRES_PASSWORD}"
+			docker_temp_server_start "$@"
+
+			docker_setup_db
+			docker_process_init_files /docker-entrypoint-initdb.d/*
+
+			docker_temp_server_stop
 			unset PGPASSWORD
 
 			echo
 			echo 'PostgreSQL init process complete; ready for start up.'
 			echo
+		else
+			echo
+			echo 'PostgreSQL Database directory appears to contain a database; Skipping initialization'
+			echo
+		fi
 	fi
-fi
 
-exec "$@"
+	exec "$@"
+}
+
+if ! _is_sourced; then
+	_main "$@"
+fi

@yosifkit
Copy link
Member Author

Build test of #6989; 04d8905; amd64 (postgres):

$ bashbrew build postgres:12.1
Building bashbrew/cache:fe541dfb4b4a47e5175e7121363d721e8113fd99b2e68f79cd3dfd99d70a0522 (postgres:12.1)
Tagging postgres:12.1
Tagging postgres:12
Tagging postgres:latest

$ test/run.sh postgres:12.1
testing postgres:12.1
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:12.1-alpine
Building bashbrew/cache:ba1a23a6465548e4835b56f3ba35413b7866497c3d02e699ae51be058779c619 (postgres:12.1-alpine)
Tagging postgres:12.1-alpine
Tagging postgres:12-alpine
Tagging postgres:alpine

$ test/run.sh postgres:12.1-alpine
testing postgres:12.1-alpine
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:11.6
Building bashbrew/cache:ebbb9e752c62e4a81314881f512a8dc803537a3d2298c0d7e01caa3e660517be (postgres:11.6)
Tagging postgres:11.6
Tagging postgres:11

$ test/run.sh postgres:11.6
testing postgres:11.6
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:11.6-alpine
Building bashbrew/cache:f54950c5682974496fd0fbafe3b769e9016028cd92493594c9b084f04eef6bd5 (postgres:11.6-alpine)
Tagging postgres:11.6-alpine
Tagging postgres:11-alpine

$ test/run.sh postgres:11.6-alpine
testing postgres:11.6-alpine
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:10.11
Building bashbrew/cache:c050e2e2a320142572dafb3e02b7763f04287f497e4ba4e509fa36f654207d3a (postgres:10.11)
Tagging postgres:10.11
Tagging postgres:10

$ test/run.sh postgres:10.11
testing postgres:10.11
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:10.11-alpine
Building bashbrew/cache:be64bbe9d6da28871e1d3a184b35fbb341367009acb990df986d9301eca05401 (postgres:10.11-alpine)
Tagging postgres:10.11-alpine
Tagging postgres:10-alpine

$ test/run.sh postgres:10.11-alpine
testing postgres:10.11-alpine
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:9.6.16
Building bashbrew/cache:3af830af8e30d69fe38bd4abbb6176c11e936c1379390eaa808f763cd9858c09 (postgres:9.6.16)
Tagging postgres:9.6.16
Tagging postgres:9.6
Tagging postgres:9

$ test/run.sh postgres:9.6.16
testing postgres:9.6.16
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6].....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:9.6.16-alpine
Building bashbrew/cache:dc9bbe4b4c34dcd5d6027c54f9c239901a788492d39e8802ebb871702ab49e26 (postgres:9.6.16-alpine)
Tagging postgres:9.6.16-alpine
Tagging postgres:9.6-alpine
Tagging postgres:9-alpine

$ test/run.sh postgres:9.6.16-alpine
testing postgres:9.6.16-alpine
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6]....passed


$ bashbrew build postgres:9.5.20
Building bashbrew/cache:aad8e08dd77cef37b9e859fbfed913a80fe752b680f610a2d5e47d63b12ee1af (postgres:9.5.20)
Tagging postgres:9.5.20
Tagging postgres:9.5

$ test/run.sh postgres:9.5.20
testing postgres:9.5.20
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6].....passed
	'postgres-initdb' [6/6].....passed


$ bashbrew build postgres:9.5.20-alpine
Building bashbrew/cache:a9ccb93fc09b797c7f609248970e48ab67b638146b97f6352645bc20236b230a (postgres:9.5.20-alpine)
Tagging postgres:9.5.20-alpine
Tagging postgres:9.5-alpine

$ test/run.sh postgres:9.5.20-alpine
testing postgres:9.5.20-alpine
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6].....passed
	'postgres-initdb' [6/6].....passed


$ bashbrew build postgres:9.4.25
Building bashbrew/cache:c78e9efdb6cabff7696496142c2983885c6ed363c131bca58f8fd2d1403b0c3a (postgres:9.4.25)
Tagging postgres:9.4.25
Tagging postgres:9.4

$ test/run.sh postgres:9.4.25
testing postgres:9.4.25
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6].....passed
	'postgres-initdb' [6/6].....passed


$ bashbrew build postgres:9.4.25-alpine
Building bashbrew/cache:da941e0a1b2959000beae66464fe0b12d0e4dd2f3e81844f32eb776c4d4bdbec (postgres:9.4.25-alpine)
Tagging postgres:9.4.25-alpine
Tagging postgres:9.4-alpine

$ test/run.sh postgres:9.4.25-alpine
testing postgres:9.4.25-alpine
	'utc' [1/6]...passed
	'cve-2014--shellshock' [2/6]...passed
	'no-hard-coded-passwords' [3/6]...passed
	'override-cmd' [4/6]...passed
	'postgres-basics' [5/6]....passed
	'postgres-initdb' [6/6].....passed

@hyperniklas
Copy link

Is there any reason for the size increase from 12.0-alpine 72.8 MB to 12.1-alpine 146.2 MB?

@tianon
Copy link
Member

tianon commented Dec 5, 2019 via email

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

Successfully merging this pull request may close these issues.

4 participants