From c9207b6f3ea64eb91c080479efb390bb9c807c3a Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Tue, 6 Oct 2026 14:13:34 +0200 Subject: [PATCH] feat(install): install PostgreSQL 18 from PGDG for new local databases The local PostgreSQL setup installed whatever the distro metapackage pointed at, so Ubuntu 22.04 got 14 (EOL Nov 2026), Debian 12 got 15 and EL9 got 13. Servers ended up on different majors, and a pg_dump backup taken on a newer server does not restore onto an older one. New installs now add the official PGDG repository and install PostgreSQL 18 (apt on Debian/Ubuntu, dnf on EL 8+ with the AppStream module disabled, postgresql-18 service and /var/lib/pgsql/18/data). Where PGDG has no build (Ubuntu 20.04, Debian 11, armhf/i386/s390x, EL7, Fedora, Amazon Linux, older EL point releases) the repository change is rolled back and the distro package is installed as before. A host that already has a cluster keeps it, so existing installs are never upgraded. x-ui.sh resolves the postgresql-18 unit on EL for start/stop/log and also removes the PGDG packages on purge. Verified in containers: Ubuntu 22.04/24.04, Debian 12, Rocky 8/9 and AlmaLinux 10 get 18.6; Ubuntu 20.04 falls back to 12 and Debian 12 armv7 to 15 with apt-get update still clean; Ubuntu 22.04 with an existing 14 cluster stays on 14. The CI PostgreSQL test set passes against postgres:18. --- install.sh | 82 +++++++++++++++++++++++++++++++++++++++++------- x-ui.sh | 92 +++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 149 insertions(+), 25 deletions(-) diff --git a/install.sh b/install.sh index aadfa7a17..dd67e6c09 100644 --- a/install.sh +++ b/install.sh @@ -208,6 +208,60 @@ pg_ensure_hba_password_auth() { sudo -u postgres psql -tAc 'SELECT pg_reload_conf()' > /dev/null 2>&1 || true } +# New installs get this PostgreSQL major from the official PGDG repository; distros or +# CPU architectures PGDG does not build for fall back to the distribution's own package. +PG_TARGET_MAJOR=18 + +pg_has_debian_cluster() { + command -v pg_lsclusters > /dev/null 2>&1 && [[ -n "$(pg_lsclusters -h 2> /dev/null)" ]] +} + +# Installs postgresql-${PG_TARGET_MAJOR} from apt.postgresql.org. On failure it removes the +# repository it added, so an unsupported release cannot break later `apt-get update` runs. +pg_apt_install_pgdg_server() { + local list added_lists=() + for list in /etc/apt/sources.list.d/pgdg.list /etc/apt/sources.list.d/pgdg.sources; do + [[ -e "${list}" ]] || added_lists+=("${list}") + done + if apt-get install -y -q postgresql-common ca-certificates gnupg >&2 \ + && /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y >&2 \ + && apt-get install -y -q "postgresql-${PG_TARGET_MAJOR}" >&2; then + return 0 + fi + for list in "${added_lists[@]}"; do + rm -f "${list}" + done + apt-get update >&2 || true + echo -e "${yellow}PostgreSQL ${PG_TARGET_MAJOR} is not available for this system; installing the distribution's PostgreSQL instead.${plain}" >&2 + return 1 +} + +# Installs postgresql${PG_TARGET_MAJOR}-server from the PGDG yum repository (EL 8+ only); on +# failure it undoes the repository and module changes it made. The caller runs initdb. +pg_el_install_pgdg_server() { + local elver + elver=$(rpm -E %rhel 2> /dev/null) + [[ "${elver}" =~ ^[0-9]+$ && "${elver}" -ge 8 ]] && command -v dnf > /dev/null 2>&1 || return 1 + [[ -f /var/lib/pgsql/data/PG_VERSION ]] && return 1 + local added_repo=false + if ! rpm -q pgdg-redhat-repo > /dev/null 2>&1; then + if dnf install -y -q "https://download.postgresql.org/pub/repos/yum/reporpms/EL-${elver}-$(uname -m)/pgdg-redhat-repo-latest.noarch.rpm" >&2; then + added_repo=true + dnf -qy module disable postgresql > /dev/null 2>&1 || true + fi + fi + if rpm -q pgdg-redhat-repo > /dev/null 2>&1 \ + && dnf install -y -q "postgresql${PG_TARGET_MAJOR}-server" "postgresql${PG_TARGET_MAJOR}-contrib" >&2; then + return 0 + fi + if [[ "${added_repo}" == "true" ]]; then + dnf -qy module reset postgresql > /dev/null 2>&1 || true + dnf remove -y -q pgdg-redhat-repo >&2 || true + fi + echo -e "${yellow}PostgreSQL ${PG_TARGET_MAJOR} is not available for this system; installing the distribution's PostgreSQL instead.${plain}" >&2 + return 1 +} + install_postgres_local() { local pg_user pg_pass pg_pass=$(gen_random_string 24) @@ -215,21 +269,27 @@ install_postgres_local() { local pg_host="127.0.0.1" local pg_port="5432" + local pg_service="postgresql" case "${release}" in ubuntu | debian | armbian) - apt-get update >&2 && apt-get install -y -q postgresql >&2 || return 1 + apt-get update >&2 || return 1 + if pg_has_debian_cluster || ! pg_apt_install_pgdg_server; then + apt-get install -y -q postgresql >&2 || return 1 + fi ;; - fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol) - dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1 - [[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1 - ;; - centos) - if [[ "${VERSION_ID}" =~ ^7 ]]; then - yum install -y postgresql-server postgresql-contrib >&2 || return 1 + fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol | centos) + if [[ "${release}" != "fedora" && "${release}" != "amzn" ]] && pg_el_install_pgdg_server; then + pg_service="postgresql-${PG_TARGET_MAJOR}" + [[ -f "/var/lib/pgsql/${PG_TARGET_MAJOR}/data/PG_VERSION" ]] \ + || "/usr/pgsql-${PG_TARGET_MAJOR}/bin/postgresql-${PG_TARGET_MAJOR}-setup" initdb >&2 || return 1 else - dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1 + if [[ "${release}" == "centos" && "${VERSION_ID}" =~ ^7 ]]; then + yum install -y postgresql-server postgresql-contrib >&2 || return 1 + else + dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1 + fi + [[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1 fi - [[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1 ;; arch | manjaro | parch) pacman -Sy --noconfirm postgresql >&2 || return 1 @@ -259,7 +319,7 @@ install_postgres_local() { esac if [[ "${release}" != "alpine" ]]; then - systemctl enable --now postgresql >&2 || return 1 + systemctl enable --now "${pg_service}" >&2 || return 1 fi # Wait briefly for the server to accept connections. diff --git a/x-ui.sh b/x-ui.sh index b552bbfb6..ab41e3824 100644 --- a/x-ui.sh +++ b/x-ui.sh @@ -2738,6 +2738,8 @@ pg_systemd_unit() { ver="${info%% *}" cluster="${info##* }" echo "postgresql@${ver}-${cluster}" + elif [[ -f "/usr/lib/systemd/system/postgresql-${PG_TARGET_MAJOR}.service" ]]; then + echo "postgresql-${PG_TARGET_MAJOR}" else echo "postgresql" fi @@ -2860,19 +2862,21 @@ purge_postgresql() { systemctl disable "$(pg_systemd_unit)" 2> /dev/null fi + local pgdg_pkgs=() + command -v rpm > /dev/null 2>&1 && mapfile -t pgdg_pkgs < <(rpm -qa --qf '%{NAME}\n' "postgresql${PG_TARGET_MAJOR}*") case "${release}" in ubuntu | debian | armbian) apt-get -y --purge remove 'postgresql*' apt-get -y autoremove --purge ;; fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol) - dnf remove -y postgresql postgresql-server postgresql-contrib + dnf remove -y postgresql postgresql-server postgresql-contrib "${pgdg_pkgs[@]}" ;; centos) if [[ "${VERSION_ID}" =~ ^7 ]]; then yum remove -y postgresql postgresql-server postgresql-contrib else - dnf remove -y postgresql postgresql-server postgresql-contrib + dnf remove -y postgresql postgresql-server postgresql-contrib "${pgdg_pkgs[@]}" fi ;; arch | manjaro | parch) @@ -2925,6 +2929,60 @@ pg_ensure_hba_password_auth() { sudo -u postgres psql -tAc 'SELECT pg_reload_conf()' > /dev/null 2>&1 || true } +# New installs get this PostgreSQL major from the official PGDG repository; distros or +# CPU architectures PGDG does not build for fall back to the distribution's own package. +PG_TARGET_MAJOR=18 + +pg_has_debian_cluster() { + command -v pg_lsclusters > /dev/null 2>&1 && [[ -n "$(pg_lsclusters -h 2> /dev/null)" ]] +} + +# Installs postgresql-${PG_TARGET_MAJOR} from apt.postgresql.org. On failure it removes the +# repository it added, so an unsupported release cannot break later `apt-get update` runs. +pg_apt_install_pgdg_server() { + local list added_lists=() + for list in /etc/apt/sources.list.d/pgdg.list /etc/apt/sources.list.d/pgdg.sources; do + [[ -e "${list}" ]] || added_lists+=("${list}") + done + if apt-get install -y -q postgresql-common ca-certificates gnupg >&2 \ + && /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y >&2 \ + && apt-get install -y -q "postgresql-${PG_TARGET_MAJOR}" >&2; then + return 0 + fi + for list in "${added_lists[@]}"; do + rm -f "${list}" + done + apt-get update >&2 || true + echo -e "${yellow}PostgreSQL ${PG_TARGET_MAJOR} is not available for this system; installing the distribution's PostgreSQL instead.${plain}" >&2 + return 1 +} + +# Installs postgresql${PG_TARGET_MAJOR}-server from the PGDG yum repository (EL 8+ only); on +# failure it undoes the repository and module changes it made. The caller runs initdb. +pg_el_install_pgdg_server() { + local elver + elver=$(rpm -E %rhel 2> /dev/null) + [[ "${elver}" =~ ^[0-9]+$ && "${elver}" -ge 8 ]] && command -v dnf > /dev/null 2>&1 || return 1 + [[ -f /var/lib/pgsql/data/PG_VERSION ]] && return 1 + local added_repo=false + if ! rpm -q pgdg-redhat-repo > /dev/null 2>&1; then + if dnf install -y -q "https://download.postgresql.org/pub/repos/yum/reporpms/EL-${elver}-$(uname -m)/pgdg-redhat-repo-latest.noarch.rpm" >&2; then + added_repo=true + dnf -qy module disable postgresql > /dev/null 2>&1 || true + fi + fi + if rpm -q pgdg-redhat-repo > /dev/null 2>&1 \ + && dnf install -y -q "postgresql${PG_TARGET_MAJOR}-server" "postgresql${PG_TARGET_MAJOR}-contrib" >&2; then + return 0 + fi + if [[ "${added_repo}" == "true" ]]; then + dnf -qy module reset postgresql > /dev/null 2>&1 || true + dnf remove -y -q pgdg-redhat-repo >&2 || true + fi + echo -e "${yellow}PostgreSQL ${PG_TARGET_MAJOR} is not available for this system; installing the distribution's PostgreSQL instead.${plain}" >&2 + return 1 +} + # Installs a local PostgreSQL server and creates a dedicated xui user/database. # Progress goes to stderr; on success the connection DSN is printed to stdout so # callers can capture it. Mirrors install_postgres_local() from install.sh, so the @@ -2936,21 +2994,27 @@ pg_install_local() { pg_host="127.0.0.1" pg_port="5432" + local pg_service="postgresql" case "${release}" in ubuntu | debian | armbian) - apt-get update >&2 && apt-get install -y -q postgresql >&2 || return 1 + apt-get update >&2 || return 1 + if pg_has_debian_cluster || ! pg_apt_install_pgdg_server; then + apt-get install -y -q postgresql >&2 || return 1 + fi ;; - fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol) - dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1 - [[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1 - ;; - centos) - if [[ "${VERSION_ID}" =~ ^7 ]]; then - yum install -y postgresql-server postgresql-contrib >&2 || return 1 + fedora | amzn | virtuozzo | rhel | almalinux | rocky | ol | centos) + if [[ "${release}" != "fedora" && "${release}" != "amzn" ]] && pg_el_install_pgdg_server; then + pg_service="postgresql-${PG_TARGET_MAJOR}" + [[ -f "/var/lib/pgsql/${PG_TARGET_MAJOR}/data/PG_VERSION" ]] \ + || "/usr/pgsql-${PG_TARGET_MAJOR}/bin/postgresql-${PG_TARGET_MAJOR}-setup" initdb >&2 || return 1 else - dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1 + if [[ "${release}" == "centos" && "${VERSION_ID}" =~ ^7 ]]; then + yum install -y postgresql-server postgresql-contrib >&2 || return 1 + else + dnf install -y -q postgresql-server postgresql-contrib >&2 || return 1 + fi + [[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1 fi - [[ -d /var/lib/pgsql/data && -f /var/lib/pgsql/data/PG_VERSION ]] || postgresql-setup --initdb >&2 || return 1 ;; arch | manjaro | parch) pacman -Sy --noconfirm postgresql >&2 || return 1 @@ -2980,7 +3044,7 @@ pg_install_local() { esac if [[ "${release}" != "alpine" ]]; then - systemctl enable --now postgresql >&2 || return 1 + systemctl enable --now "${pg_service}" >&2 || return 1 fi local i @@ -3090,7 +3154,7 @@ pg_upgrade_client() { apt-get install -y -q postgresql-client >&2 || return 1 elif ! apt-get install -y -q "postgresql-client-${want}" >&2; then LOGI "postgresql-client-${want} is not in the distribution repositories; adding the official PostgreSQL apt repository..." - apt-get install -y -q postgresql-common ca-certificates >&2 || return 1 + apt-get install -y -q postgresql-common ca-certificates gnupg >&2 || return 1 /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y >&2 || return 1 apt-get install -y -q "postgresql-client-${want}" >&2 || return 1 fi