From 8f36aa8a015b598365ae7497f803402d8ef596bf Mon Sep 17 00:00:00 2001 From: Olivier DEBAUCHE Date: Wed, 25 Mar 2026 11:46:43 +0100 Subject: [PATCH 1/2] Replace Gunicorn with Granian in Docker runtime --- Dockerfile | 4 ++-- pkg/docker/entrypoint.sh | 27 +++++++++++---------- pkg/docker/gunicorn_config.py | 45 ----------------------------------- 3 files changed, 17 insertions(+), 59 deletions(-) delete mode 100644 pkg/docker/gunicorn_config.py diff --git a/Dockerfile b/Dockerfile index 8277253b6f3..96658e94ddf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -185,14 +185,14 @@ ENV PYTHONPATH=/pgadmin4 # Copy in the code and docs COPY --from=app-builder /pgadmin4/web /pgadmin4 COPY --from=docs-builder /pgadmin4/docs/en_US/_build/html/ /pgadmin4/docs -COPY pkg/docker/run_pgadmin.py pkg/docker/gunicorn_config.py /pgadmin4/ +COPY pkg/docker/run_pgadmin.py /pgadmin4/ COPY pkg/docker/entrypoint.sh /entrypoint.sh # License files COPY LICENSE /pgadmin4/LICENSE # Configure everything in one RUN step -RUN /venv/bin/python3 -m pip install --no-cache-dir gunicorn==23.0.0 && \ +RUN /venv/bin/python3 -m pip install --no-cache-dir granian==2.2.5 && \ find / -type d -name '__pycache__' -exec rm -rf {} + && \ useradd -r -u 5050 -g root -s /sbin/nologin pgadmin && \ mkdir -p /run/pgadmin /var/lib/pgadmin && \ diff --git a/pkg/docker/entrypoint.sh b/pkg/docker/entrypoint.sh index 914a8883307..2c4b4101149 100755 --- a/pkg/docker/entrypoint.sh +++ b/pkg/docker/entrypoint.sh @@ -178,25 +178,28 @@ if [ -z "${PGADMIN_DISABLE_POSTFIX}" ]; then sudo /usr/sbin/postfix start fi -# Get the session timeout from the pgAdmin config. We'll use this (in seconds) -# to define the Gunicorn worker timeout -TIMEOUT=$(cd /pgadmin4 && /venv/bin/python3 -c 'import config; print(config.SESSION_EXPIRATION_TIME * 60 * 60 * 24)') - # NOTE: currently pgadmin can run only with 1 worker due to sessions implementation -# Using --threads to have multi-threaded single-process worker if [ -n "${PGADMIN_ENABLE_SOCK}" ]; then - BIND_ADDRESS="unix:/run/pgadmin/pgadmin.sock" + BIND_ARGS="--uds /run/pgadmin/pgadmin.sock" else + BIND_ARGS="--host ${PGADMIN_LISTEN_ADDRESS:-[::]} --port ${PGADMIN_LISTEN_PORT:-80}" if [ -n "${PGADMIN_ENABLE_TLS}" ]; then - BIND_ADDRESS="${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-443}" - else - BIND_ADDRESS="${PGADMIN_LISTEN_ADDRESS:-[::]}:${PGADMIN_LISTEN_PORT:-80}" + BIND_ARGS="--host ${PGADMIN_LISTEN_ADDRESS:-[::]} --port ${PGADMIN_LISTEN_PORT:-443}" fi fi -if [ -n "${PGADMIN_ENABLE_TLS}" ]; then - exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" --keyfile /certs/server.key --certfile /certs/server.cert -c gunicorn_config.py run_pgadmin:app +if [ "${GUNICORN_ACCESS_LOGFILE:--}" = "-" ]; then + ACCESS_LOG_ARGS="--access-log" else - exec /venv/bin/gunicorn --limit-request-line "${GUNICORN_LIMIT_REQUEST_LINE:-8190}" --limit-request-fields "${GUNICORN_LIMIT_REQUEST_FIELDS:-100}" --limit-request-field_size "${GUNICORN_LIMIT_REQUEST_FIELD_SIZE:-8190}" --timeout "${TIMEOUT}" --bind "${BIND_ADDRESS}" -w 1 --threads "${GUNICORN_THREADS:-25}" --access-logfile "${GUNICORN_ACCESS_LOGFILE:--}" -c gunicorn_config.py run_pgadmin:app + ACCESS_LOG_ARGS="--no-access-log" fi + +TLS_ARGS="" +if [ -n "${PGADMIN_ENABLE_TLS}" ]; then + TLS_ARGS="--ssl-keyfile /certs/server.key --ssl-certificate /certs/server.cert" +fi + +# Keep the existing environment variables for backward compatibility. +exec /venv/bin/granian --interface wsgi --workers 1 --blocking-threads "${GUNICORN_THREADS:-25}" ${ACCESS_LOG_ARGS} ${TLS_ARGS} ${BIND_ARGS} run_pgadmin:app + diff --git a/pkg/docker/gunicorn_config.py b/pkg/docker/gunicorn_config.py deleted file mode 100644 index ac7afe08175..00000000000 --- a/pkg/docker/gunicorn_config.py +++ /dev/null @@ -1,45 +0,0 @@ -import gunicorn - -# Can be resolved because of how Dockerfile organizes the code during build -from config import JSON_LOGGER, CONSOLE_LOG_LEVEL, CONSOLE_LOG_FORMAT_JSON - -gunicorn.SERVER_SOFTWARE = "Python" - -if JSON_LOGGER: - logconfig_dict = { - "version": 1, - "disable_existing_loggers": False, - "root": {"level": CONSOLE_LOG_LEVEL, "handlers": []}, - "loggers": { - "gunicorn.error": { - "level": CONSOLE_LOG_LEVEL, - "handlers": ["error_console"], - "propagate": True, - "qualname": "gunicorn.error", - }, - "gunicorn.access": { - "level": CONSOLE_LOG_LEVEL, - "handlers": ["console"], - "propagate": True, - "qualname": "gunicorn.access", - }, - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "formatter": "json", - "stream": "ext://sys.stdout", - }, - "error_console": { - "class": "logging.StreamHandler", - "formatter": "json", - "stream": "ext://sys.stderr", - }, - }, - "formatters": { - "json": { - "class": "jsonformatter.JsonFormatter", - "format": CONSOLE_LOG_FORMAT_JSON, - }, - }, - } From dbad9fbb8d9c721c304eba329b96f5523a8fd67d Mon Sep 17 00:00:00 2001 From: Olivier DEBAUCHE Date: Wed, 25 Mar 2026 11:49:47 +0100 Subject: [PATCH 2/2] Upgrade granian package version in Dockerfile Updated granian package version from 2.2.5 to 2.7.2 in Dockerfile. --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 96658e94ddf..0e3e60abe81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -192,7 +192,7 @@ COPY pkg/docker/entrypoint.sh /entrypoint.sh COPY LICENSE /pgadmin4/LICENSE # Configure everything in one RUN step -RUN /venv/bin/python3 -m pip install --no-cache-dir granian==2.2.5 && \ +RUN /venv/bin/python3 -m pip install --no-cache-dir granian==2.7.2 && \ find / -type d -name '__pycache__' -exec rm -rf {} + && \ useradd -r -u 5050 -g root -s /sbin/nologin pgadmin && \ mkdir -p /run/pgadmin /var/lib/pgadmin && \