diff --git a/cdist/conf/type/__postgres_conf/explorer/postgres_user b/cdist/conf/type/__postgres_conf/explorer/postgres_user
new file mode 100644
index 00000000..c6582dc4
--- /dev/null
+++ b/cdist/conf/type/__postgres_conf/explorer/postgres_user
@@ -0,0 +1,64 @@
+#!/bin/sh -e
+# -*- mode: sh; indent-tabs-mode: t -*-
+#
+# 2021 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+os=$("${__explorer:?}/os")
+
+case ${os}
+in
+ (alpine)
+ echo 'postgres'
+ ;;
+ (centos|rhel|scientific)
+ echo 'postgres'
+ ;;
+ (debian|devuan|ubuntu)
+ echo 'postgres'
+ ;;
+ (freebsd)
+ test -x /usr/local/etc/rc.d/postgresql || {
+ printf 'could not find postgresql rc script./n' >&2
+ exit 1
+ }
+ pg_status=$(/usr/local/etc/rc.d/postgresql onestatus) || {
+ printf 'postgresql daemon is not running.\n' >&2
+ exit 1
+ }
+ pg_pid=$(printf '%s\n' "${pg_status}" \
+ | sed -n 's/^pg_ctl:.*(PID: *\([0-9]*\))$/\1/p')
+
+ # PostgreSQL < 9.6: pgsql
+ # PostgreSQL >= 9.6: postgres
+ ps -o user -p "${pg_pid}" | sed -n '2p'
+ ;;
+ (netbsd)
+ echo 'pgsql'
+ ;;
+ (openbsd)
+ echo '_postgresql'
+ ;;
+ (suse)
+ echo 'postgres'
+ ;;
+ (*)
+ echo "Unsupported OS: ${os}" >&2
+ exit 1
+ ;;
+esac
diff --git a/cdist/conf/type/__postgres_conf/explorer/state b/cdist/conf/type/__postgres_conf/explorer/state
new file mode 100644
index 00000000..4b7b0a43
--- /dev/null
+++ b/cdist/conf/type/__postgres_conf/explorer/state
@@ -0,0 +1,223 @@
+#!/bin/sh -e
+# -*- mode: sh; indent-tabs-mode: t -*-
+#
+# 2021 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+postgres_user=$("${__type_explorer:?}/postgres_user")
+conf_name=${__object_id:?}
+
+tolower() { printf '%s' "$*" | tr '[:upper:]' '[:lower:]'; }
+
+tobytes() {
+ # NOTE: This function treats everything as base 2.
+ # It is not compatible with SI units.
+ awk 'BEGIN { FS = "\n" }
+ /TB$/ { $0 = ($0 * 1024) "GB" }
+ /GB$/ { $0 = ($0 * 1024) "MB" }
+ /MB$/ { $0 = ($0 * 1024) "kB" }
+ /kB$/ { $0 = ($0 * 1024) "B" }
+ /B?$/ { sub(/ *B?$/, "") }
+ ($0*1) == $0 # is number
+ ' <<-EOF
+ $1
+ EOF
+}
+
+tomillisecs() {
+ awk 'BEGIN { FS = "\n" }
+ /d$/ { $0 = ($0 * 24) "h" }
+ /h$/ { $0 = ($0 * 60) "min" }
+ /min$/ { $0 = ($0 * 60) "s" }
+ /[^m]s$/ { $0 = ($0 * 1000) "ms" }
+ /ms$/ { $0 *= 1 }
+ ($0*1) == $0 # is number
+ ' <<-EOF
+ $1
+ EOF
+}
+
+tobool() {
+ # prints either 'on' or 'off'
+ case $(tolower "$1")
+ in
+ (t|true|y|yes|on|1)
+ echo 'on' ;;
+ (f|false|n|no|off|0)
+ echo 'off' ;;
+ (*)
+ printf 'Inavlid bool value: %s\n' "$2" >&2
+ return 1
+ ;;
+ esac
+ return 0
+}
+
+quote() { printf '%s\n' "$*" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/"; }
+psql_exec() {
+ su - "${postgres_user}" -c "psql postgres -twAc $(quote "$*")"
+}
+
+psql_conf_source() {
+ # NOTE: SHOW/SET are case-insentitive, so this command should also be.
+ psql_exec "SELECT CASE WHEN source = 'default' OR setting = boot_val THEN 'default' ELSE source END FROM pg_settings WHERE lower(name) = lower('$1')"
+}
+psql_conf_cmp() (
+ IFS='|' read -r lower_name vartype setting unit <<-EOF
+ $(psql_exec "SELECT lower(name), vartype, setting, unit FROM pg_settings WHERE lower(name) = lower('$1')")
+ EOF
+
+ should_value=$2
+ is_value=${setting}
+
+ # The following case contains special cases for special settings.
+ case ${lower_name}
+ in
+ (archive_command)
+ if test "${setting}" = '(disabled)'
+ then
+ # DAFUQ PostgreSQL?!
+ # PostgreSQL returns (disabled) if the feature is inactive.
+ # We cannot compare the values unless it is enabled, first.
+ return 0
+ fi
+ ;;
+ (archive_mode|backslash_quote|constraint_exclusion|force_parallel_mode|huge_pages|synchronous_commit)
+ # Although only 'on', 'off' are documented, PostgreSQL accepts all
+ # the "likely" variants of "on" and "off".
+ case $(tolower "${should_value}")
+ in
+ (on|off|true|false|yes|no|1|0)
+ should_value=$(tobool "${should_value}")
+ ;;
+ esac
+ ;;
+ esac
+
+ case ${vartype}
+ in
+ (bool)
+ test -z "${unit}" || {
+ # please fix the explorer if this error occurs.
+ printf 'units are not supported for vartype: %s\n' "${vartype}" >&2
+ exit 1
+ }
+
+ should_value=$(tobool "${should_value}")
+
+ test "${is_value}" = "${should_value}"
+ ;;
+ (enum)
+ test -z "${unit}" || {
+ # please fix the explorer if this error occurs.
+ printf 'units are not supported with vartype: %s\n' "${vartype}" >&2
+ exit 1
+ }
+
+ # NOTE: All enums that are currently defined are lower case, but
+ # PostgreSQL also accepts upper case spelling.
+ should_value=$(tolower "$2")
+
+ test "${is_value}" = "${should_value}"
+ ;;
+ (integer)
+ # split multiples from unit, first (e.g. 8kB -> 8, kB)
+ case ${unit}
+ in
+ ([0-9]*)
+ multiple=${unit%%[!0-9]*}
+ unit=${unit##*[0-9 ]}
+ ;;
+ (*) multiple=1 ;;
+ esac
+
+ is_value=$((setting * multiple))${unit}
+
+ if expr "${should_value}" : '-\{0,1\}[0-9]*$' >/dev/null
+ then
+ # default unit
+ should_value=$((should_value * multiple))${unit}
+ fi
+
+ # then, do conversion
+ # NOTE: these conversions work for integers only!
+ case ${unit}
+ in
+ (B|[kMGT]B)
+ # bytes
+ is_bytes=$(tobytes "${is_value}")
+ should_bytes=$(tobytes "${should_value}")
+
+ test $((is_bytes)) -eq $((should_bytes))
+ ;;
+ (ms|s|min|h|d)
+ # seconds
+ is_ms=$(tomillisecs "${is_value}")
+ should_ms=$(tomillisecs "${should_value}")
+
+ test $((is_ms)) -eq $((should_ms))
+ ;;
+ ('')
+ # no unit
+ is_int=${is_value}
+ should_int=${should_value}
+
+ test $((is_int)) -eq $((should_int))
+ ;;
+ esac
+ ;;
+ (real|string)
+ # NOTE: reals could possibly have units, but currently there none.
+
+ test -z "${unit}" || {
+ # please fix the explorer if this error occurs.
+ printf 'units are not supported with vartype: %s\n' "${vartype}" >&2
+ exit 1
+ }
+
+ test "${is_value}" = "${should_value}"
+ ;;
+ esac
+)
+
+psql_exec 'SELECT 1' >/dev/null || {
+ echo 'Connection to PostgreSQL server failed' >&2
+ exit 1
+}
+
+case $(psql_conf_source "${conf_name}")
+in
+ ('')
+ printf 'Invalid configuration parameter: %s\n' "${conf_name}" >&2
+ exit 1
+ ;;
+ (default)
+ echo absent
+ ;;
+ (*)
+ if ! test -f "${__object:?}/parameter/value"
+ then
+ echo present
+ elif psql_conf_cmp "${conf_name}" "$(cat "${__object:?}/parameter/value")"
+ then
+ echo present
+ else
+ echo different
+ fi
+ ;;
+esac
diff --git a/cdist/conf/type/__postgres_conf/gencode-remote b/cdist/conf/type/__postgres_conf/gencode-remote
new file mode 100755
index 00000000..27651600
--- /dev/null
+++ b/cdist/conf/type/__postgres_conf/gencode-remote
@@ -0,0 +1,123 @@
+#!/bin/sh -e
+# -*- mode: sh; indent-tabs-mode: t -*-
+#
+# 2019-2021 Dennis Camera (dennis.camera at ssrq-sds-fds.ch)
+# 2020 Beni Ruef (bernhard.ruef at ssrq-sds-fds.ch)
+#
+# This file is part of cdist.
+#
+# cdist is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# cdist is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with cdist. If not, see .
+#
+
+state_is=$(cat "${__object:?}/explorer/state")
+state_should=$(cat "${__object:?}/parameter/state")
+postgres_user=$(cat "${__object:?}/explorer/postgres_user")
+
+conf_name=${__object_id:?}
+
+if test "${state_is}" = "${state_should}"
+then
+ exit 0
+fi
+
+quote() {
+ for _arg
+ do
+ shift
+ if test -n "$(printf '%s' "${_arg}" | tr -d -c '\t\n \042-\047\050-\052\073-\077\133\\`|~' | tr -c '' '.')"
+ then
+ # needs quoting
+ set -- "$@" "'$(printf '%s' "${_arg}" | sed -e "s/'/'\\\\''/g")'"
+ else
+ set -- "$@" "${_arg}"
+ fi
+ done
+ unset _arg
+
+ # NOTE: Use printf because POSIX echo interprets escape sequences
+ printf '%s' "$*"
+}
+
+
+psql_cmd() {
+ printf 'su - %s -c %s\n' "$(quote "${postgres_user}")" "$(quote "$(quote psql "$@")")"
+}
+
+case ${state_should}
+in
+ (present)
+ test -n "${__object:?}/parameter/value" || {
+ echo 'Missing required parameter --value' >&2
+ exit 1
+ }
+
+ cat <<-EOF
+ exec 3< "\${__object:?}/parameter/value"
+ $(psql_cmd postgres -tAwq -o /dev/null -v ON_ERROR_STOP=on) <<'SQL'
+ \\set conf_value \`cat <&3\`
+ ALTER SYSTEM SET ${conf_name} = :'conf_value';
+ SELECT pg_reload_conf();
+ SQL
+ exec 3<&-
+ EOF
+ ;;
+ (absent)
+ psql_cmd postgres -qwc "ALTER SYSTEM SET ${conf_name} TO DEFAULT"
+ ;;
+ (*)
+ printf 'Invalid --state: %s\n' "${state_should}" >&2
+ printf 'Only "present" and "absent" are acceptable.\n' >&2
+ exit 1
+ ;;
+esac
+
+# Restart PostgreSQL server if required to apply new configuration value
+cat <&2
+ exit 1
+ esac
+ ;;
+ (*)
+ printf "Don't know how to restart services with your init (%s)\n" "${init}" >&2
+ exit 1
+ esac
+ )
+fi
+EOF
diff --git a/cdist/conf/type/__postgres_conf/man.rst b/cdist/conf/type/__postgres_conf/man.rst
new file mode 100644
index 00000000..e035f080
--- /dev/null
+++ b/cdist/conf/type/__postgres_conf/man.rst
@@ -0,0 +1,60 @@
+cdist-type__postgres_conf(7)
+============================
+
+NAME
+----
+cdist-type__postgres_conf - Alter PostgreSQL configuration
+
+
+DESCRIPTION
+-----------
+Configure a running PostgreSQL server using ``ALTER SYSTEM``.
+
+
+REQUIRED PARAMETERS
+-------------------
+value
+ The value to set (can be omitted if ``--state`` is set to ``absent``).
+
+
+OPTIONAL PARAMETERS
+-------------------
+state
+ ``present`` or ``absent``.
+ Defaults to ``present``.
+
+
+BOOLEAN PARAMETERS
+------------------
+None.
+
+
+EXAMPLES
+--------
+
+.. code-block:: sh
+
+ # set timezone
+ __postgres_conf timezone --value Europe/Zurich
+
+ # reset maximum number of concurrent connections to default (normally 100)
+ __postgres_conf max_connections --state absent
+
+
+SEE ALSO
+--------
+None.
+
+
+AUTHORS
+-------
+Beni Ruef (bernhard.ruef--@--ssrq-sds-fds.ch)
+Dennis Camera (dennis.camera--@--ssrq-sds-fds.ch)
+
+
+COPYING
+-------
+Copyright \(C) 2019-2021 SSRQ (www.ssrq-sds-fds.ch).
+You can redistribute it and/or modify it under the terms of the GNU General
+Public License as published by the Free Software Foundation, either version 3 of
+the License, or (at your option) any later version.
diff --git a/cdist/conf/type/__postgres_conf/parameter/default/state b/cdist/conf/type/__postgres_conf/parameter/default/state
new file mode 100644
index 00000000..e7f6134f
--- /dev/null
+++ b/cdist/conf/type/__postgres_conf/parameter/default/state
@@ -0,0 +1 @@
+present
diff --git a/cdist/conf/type/__postgres_conf/parameter/optional b/cdist/conf/type/__postgres_conf/parameter/optional
new file mode 100644
index 00000000..d0460d86
--- /dev/null
+++ b/cdist/conf/type/__postgres_conf/parameter/optional
@@ -0,0 +1,2 @@
+state
+value