--- /dev/null
+#!/usr/bin/env bash
+
+###########################
+# REQUIREMENTS
+###########################
+#
+# * Required commands:
+# + pg_dump
+# + du
+# + tee
+# + bzip2 # If bzip2 is not available, change 'CMD_COMPRESS'
+# # to use 'gzip' or whatever compress command you want.
+#
+
+###########################
+# USAGE
+###########################
+#
+# * It stores all backup copies in directory '/var/vmail/backup' by default,
+# You can change it in variable $BACKUP_ROOTDIR below.
+#
+# * Set correct values for below variables:
+#
+# PGSQL_SYS_USER
+# BACKUP_ROOTDIR
+# DATABASES
+#
+# * Add crontab job for root user (or whatever user you want):
+#
+# # crontab -e -u root
+# 1 4 * * * bash /path/to/backup_pgsql.sh
+#
+# * Make sure 'crond' service is running.
+#
+
+set -e
+set -u
+
+export LC_ALL=C
+export LANG=C
+
+#########################################################
+# Modify below variables to fit your need ----
+#########################################################
+# Keep backup for how many days. Default is 90 days.
+KEEP_DAYS='90'
+
+# System user used to run PostgreSQL daemon.
+# - On Linux, it's postgres.
+# - On FreeBSD, it's pgsql.
+# - On OpenBSD, it's _postgresql.
+export PGSQL_SYS_USER="postgres"
+
+# Where to store backup copies.
+export BACKUP_ROOTDIR="/var/backup"
+
+export SUDO=""
+if [[ "$( id -u -n )" != "${PGSQL_SYS_USER}" ]] ; then
+ export SUDO="sudo -i -u ${PGSQL_SYS_USER} "
+fi
+
+# Detect existing databases
+export DATABASES="$( ${SUDO}psql --list --tuples-only --no-align --no-readline --expanded --field-separator=',' | grep -i '^Name' | awk -F ',' '{print $2}' )"
+
+export PATH='/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/sbin'
+
+# Commands.
+export CMD_DATE='/bin/date'
+export CMD_DU='du -sk'
+export CMD_COMPRESS='bzip2 -9'
+export COMPRESS_SUFFIX='bz2'
+export CMD_PG_DUMP='pg_dump'
+
+# Date.
+export YEAR="$(${CMD_DATE} +%Y)"
+export MONTH="$(${CMD_DATE} +%m)"
+export DAY="$(${CMD_DATE} +%d)"
+export TIME="$(${CMD_DATE} +%H:%M:%S)"
+export TIMESTAMP="${YEAR}-${MONTH}-${DAY}-${TIME}"
+
+# Pre-defined backup status
+export BACKUP_SUCCESS='YES'
+
+# Define, check, create directories.
+export BACKUP_DIR="${BACKUP_ROOTDIR}/pgsql/${YEAR}/${MONTH}/${DAY}"
+
+# Find the old backup which should be removed.
+export REMOVE_OLD_BACKUP='NO'
+if which python &>/dev/null; then
+ export REMOVE_OLD_BACKUP='YES'
+ py_cmd="import time; import datetime; t=time.localtime(); print datetime.date(t.tm_year, t.tm_mon, t.tm_mday) - datetime.timedelta(days=${KEEP_DAYS})"
+ shift_date=$(python -c "${py_cmd}")
+ shift_year="$(echo ${shift_date} | awk -F'-' '{print $1}')"
+ shift_month="$(echo ${shift_date} | awk -F'-' '{print $2}')"
+ shift_day="$(echo ${shift_date} | awk -F'-' '{print $3}')"
+ export REMOVED_BACKUP_DIR="${BACKUP_ROOTDIR}/pgsql/${shift_year}/${shift_month}/${shift_day}"
+fi
+
+# Log file
+export LOGFILE="${BACKUP_DIR}/${TIMESTAMP}.log"
+
+# Check and create directories.
+if [[ ! -d "${BACKUP_DIR}" ]] ; then
+ ${SUDO}mkdir -p "${BACKUP_DIR}"
+fi
+
+# Get HOME directory of PGSQL_SYS_USER
+export PGSQL_SYS_USER_HOME=$( getent passwd "${PGSQL_SYS_USER}" | awk -F':' '{print $6}' )
+
+# Initialize log file.
+echo "* Starting at: ${YEAR}-${MONTH}-${DAY}-${TIME}." | ${SUDO}tee ${LOGFILE}
+echo "* Backup directory: ${BACKUP_DIR}." | ${SUDO}tee -a ${LOGFILE}
+
+TMP_DIR=$( ${SUDO}mktemp -d -p "${PGSQL_SYS_USER_HOME}" backup.XXXXXXXX.d )
+
+cleanup_tmp_dir() {
+ ${SUDO}rm -rf "${TMP_DIR}"
+}
+
+trap cleanup_tmp_dir INT TERM EXIT ABRT
+
+# Backup.
+echo "* Backing up databases: ${DATABASES}." | ${SUDO}tee -a ${LOGFILE}
+for db in ${DATABASES}; do
+
+ output_sql="${db}-${TIMESTAMP}.sql"
+ out_sql_tmp="${TMP_DIR}/${output_sql}"
+ out_sql_tgt="${BACKUP_DIR}/${output_sql}"
+
+# # Check database existence
+# if ${SUDO}"psql -d ${db} -c '\q' >/dev/null 2>&1" ; then
+# :
+# else
+# echo "Database '${db}' does not exists." | ${SUDO}tee -a ${LOGFILE}
+# continue
+# fi
+
+ # Dump
+ ${SUDO}"${CMD_PG_DUMP}" ${db} | ${SUDO}tee "${out_sql_tmp}" >/dev/null
+
+ # Move to backup directory.
+ ${SUDO}mv "${out_sql_tmp}" ${BACKUP_DIR}
+
+ cd ${BACKUP_DIR}
+
+ # Get original SQL file size
+ original_size="$(${CMD_DU} ${output_sql} | awk '{print $1}')"
+ echo " Original size ${output_sql}: ${original_size}" | ${SUDO}tee -a ${LOGFILE}
+
+ # Compress
+ ${SUDO}${CMD_COMPRESS} ${out_sql_tgt} 2>&1 | ${SUDO}tee -a ${LOGFILE}
+
+ ${SUDO}rm -f ${out_sql_tgt} | ${SUDO}tee -a ${LOGFILE}
+ echo -e " + ${db} [DONE]" | ${SUDO}tee -a ${LOGFILE}
+
+done
+
+# Append file size of backup files.
+echo -e "* File size:\n----" | ${SUDO}tee -a ${LOGFILE}
+${SUDO}${CMD_DU} ${BACKUP_DIR}/*${TIMESTAMP}*sql* | ${SUDO}tee -a ${LOGFILE}
+echo "----" | ${SUDO}tee -a ${LOGFILE}
+
+echo "* Backup completed (Success? ${BACKUP_SUCCESS})." | ${SUDO}tee -a ${LOGFILE}
+
+if [[ "${BACKUP_SUCCESS}" == 'YES' ]]; then
+ echo -e "\n[OK] Backup successfully completed.\n"
+else
+ echo -e "\n[ERROR] Backup completed with ERRORS.\n" >&2
+fi
+
+if [[ "${REMOVE_OLD_BACKUP}" == 'YES' && -d "${REMOVED_BACKUP_DIR}" ]]; then
+ echo -e "* Delete old backup: ${REMOVED_BACKUP_DIR}." | ${SUDO}tee -a ${LOGFILE}
+ echo -e "* Suppose to delete: ${REMOVED_BACKUP_DIR}" | ${SUDO}tee -a ${LOGFILE}
+ ${SUDO}rm -rf ${REMOVED_BACKUP_DIR} 2>&1 | ${SUDO}tee -a ${LOGFILE}
+fi
+
+echo "* Backup log: ${LOGFILE}:"
+
+
+# vim: ts=4 et list