Skip to content

Conversation

@Guiorgy
Copy link
Contributor

@Guiorgy Guiorgy commented Nov 20, 2025

  • Moved the installation of dependencies to the top of the stage. Previously, any change in the project files would result in having to redownload all the dependencies. Now they should be cached, which should result in a more pleasant development experience. If it is actually desired to redownload dependencies, docker build --no-cache ... can be used.
  • Upgrade the rest of the packages while installing dependencies, keeping the image up-to-date.
  • Combined copy instructions where possible to reduce layers.

Summary by CodeRabbit

  • Chores
    • Improved container runtime configuration with consolidated dependency setup and optimized startup environment.
    • Streamlined packaging of runtime libraries and scripts to reduce image layers and simplify deployment.
    • Adjusted user, permissions, and entrypoint settings to improve runtime reliability and writable directories.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Walkthrough

The Dockerfile was updated to consolidate runtime dependency installation and library copies, add an explicit liblz4 symlink, combine Docker script copies, and centralize final runtime configuration (gunicorn install, user and directory setup, permissions, and entrypoint/runtime metadata).

Changes

Cohort / File(s) Summary
Dockerfile (runtime/image changes)
Dockerfile
Adds an APK runtime install step (bash, postfix, krb5-libs, libjpeg-turbo, shadow, sudo, tzdata, libedit, libldap, libcap) with cache cleanup; merges libpq and liblz4 COPYs into one (libpq.so.5.18, liblz4.so.1.10.0/usr/lib/); creates explicit liblz4.so.1 symlink; consolidates pkg/docker/run_pgadmin.py and pkg/docker/gunicorn_config.py into /pgadmin4/; centralizes final RUN to install gunicorn, remove pycache, create pgadmin user, create/runtime directories, set ownership/permissions, add config_distro.py, and add sudoers/postfix entries; adds USER, VOLUME, EXPOSE, and ENTRYPOINT adjustments.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas to inspect closely:
    • Symlink creation and target library filenames/paths (liblz4.so.1.10.0liblz4.so.1)
    • Correctness and necessity of listed APK runtime packages
    • User, permissions, directory ownership, and sudoers entries for security implications
    • Consolidated COPY and RUN steps referencing exact file locations (/pgadmin4/, /usr/lib/)

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Optimized Dockerfile' directly aligns with the main objective of the PR, which focuses on optimizing the Dockerfile through dependency caching, layer reduction, and build efficiency improvements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
Dockerfile (2)

166-179: Verify apk update/upgrade impact on image reproducibility and build caching.

The addition of apk update && apk upgrade at lines 167 will always fetch the latest package versions, which can cause layer cache invalidation on every build even when source code hasn't changed. This defeats part of the caching benefit intended by moving dependencies to an earlier stage.

While the PR objective mentions upgrading packages to keep the image up-to-date, consider:

  1. Whether this should be done selectively (upgrade only if explicitly requested)
  2. Whether pinning specific package versions would be better for reproducibility
  3. Whether docker build --no-cache (mentioned in PR objectives) is the intended control mechanism

If the intent is to always get the latest patches for security, the current approach is correct but understand it will re-run this layer frequently.


204-217: Consolidation of final RUN step looks sound; verify wildcard in setcap command.

The consolidation of configuration into a single RUN instruction (lines 204-217) is good for layer optimization. However, line 215 uses a wildcard pattern that may not match all Python versions reliably:

setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3.[0-9][0-9]

This pattern assumes Python 3.X version format. If the image contains Python 3.10 or 3.100+ in the future, this could fail silently or match unexpectedly. Consider:

  • Explicitly discovering the Python version: $(python3 -c "import sys; print(f'python{sys.version_info.major}.{sys.version_info.minor}')")
  • Or using a more flexible glob: /usr/local/bin/python3*
-    setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3.[0-9][0-9] && \
+    setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3.* && \
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 482549b and 1a0dfd7.

📒 Files selected for processing (1)
  • Dockerfile (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-16T16:38:56.851Z
Learning: In the pgadmin4 repository, when using the python:3-alpine Docker base image, libzstd is already included and does not need to be explicitly installed or copied from other build stages.
📚 Learning: 2025-11-16T16:38:56.851Z
Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-16T16:38:56.851Z
Learning: In the pgadmin4 repository, when using the python:3-alpine Docker base image, libzstd is already included and does not need to be explicitly installed or copied from other build stages.

Applied to files:

  • Dockerfile
🪛 Hadolint (2.14.0)
Dockerfile

[error] 198-198: COPY with more than 2 arguments requires the last argument to end with /

(DL3021)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (8)
  • GitHub Check: run-python-tests-pg (windows-latest, 16)
  • GitHub Check: run-python-tests-pg (ubuntu-22.04, 14)
  • GitHub Check: run-feature-tests-pg (18)
  • GitHub Check: run-feature-tests-pg (17)
  • GitHub Check: run-feature-tests-pg (14)
  • GitHub Check: run-feature-tests-pg (16)
  • GitHub Check: run-feature-tests-pg (13)
  • GitHub Check: run-feature-tests-pg (15)
🔇 Additional comments (1)
Dockerfile (1)

186-186: I'll examine the Dockerfile to understand the context and verify the concern about the source path.

Let me examine the Dockerfile structure around line 186:

Perfect—I found the actual Dockerfile! The Dockerfile shows three separate COPY commands from pg18-builder: one for /usr/local/lib/libpq.so.5.18, one for /usr/lib/libzstd.so.1.5.7, and one for /usr/lib/liblz4.so.1.10.0, all to /usr/lib/.

Looking at this more carefully, the review comment's concern about the path being "destination-like" is incorrect. The path /usr/lib/liblz4.so.1.10.0 is actually the correct source location within the pg18-builder image (which is based on postgres:18-alpine). The COPY syntax is valid.

However, the underlying issue is real: pgadmin4 users reported missing shared libraries libzstd.so.1 and liblz4.so.1 when running pg_dump with the latest Docker image version 7.7. This indicates these files are either not present in the pg18-builder stage or not being properly included in the final image.

Given the retrieved learning that libzstd is already included in python:3-alpine, and the confirmed production bug reports, the review comment needs significant correction.


Source paths for liblz4.so.1.10.0 and libzstd.so.1.5.7 are correct, but production runtime failures confirm the actual issue exists upstream.

The COPY commands correctly reference /usr/lib/liblz4.so.1.10.0 and /usr/lib/libzstd.so.1.5.7 from pg18-builder—these are valid source paths within the postgres:18-alpine-based builder stage. The syntax is not a copy-paste error as suggested.

However, pgadmin4 users have reported that liblz4.so.1 and libzstd.so.1 are missing at runtime in the Docker image version 7.7, which confirms a real problem exists. Rather than an issue with the COPY syntax or path notation, the concern should focus on:

  • Whether pg18-builder actually contains these libraries after the build
  • Whether the symbolic links created in the RUN statement (lines following the COPYs) are sufficient

The review comment should be revised to focus on verifying that pg18-builder properly includes these runtime dependencies after compilation, rather than questioning the source path notation itself.

Likely an incorrect or invalid review comment.

@Guiorgy Guiorgy force-pushed the optimize-dockerfile branch from 1a0dfd7 to 4ab2fe0 Compare November 20, 2025 19:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Dockerfile (2)

215-215: Potential issue: Python version glob pattern may be too restrictive.

The glob pattern python3.[0-9][0-9] only matches exactly two digits, which works for current versions (e.g., python3.10, python3.12) but will fail for single-digit minor versions (e.g., python3.9) or future triple-digit versions (e.g., python3.100).

Consider using a more flexible pattern:

-    setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3.[0-9][0-9] && \
+    setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3* && \

Or, if more strict matching is preferred, use a find command instead of a glob.


217-217: Fix sudoers entry typo: "pgadminr" should be "pgadmin".

Line 217 references user pgadminr (with extra 'r'), but the user created on line 207 is pgadmin. This typo means the sudoers rule on line 217 applies to a non-existent user.

-    echo "pgadminr ALL = NOPASSWD: /usr/sbin/postfix start" >> /etc/sudoers.d/postfix
+    echo "pgadmin ALL = NOPASSWD: /usr/sbin/postfix start" >> /etc/sudoers.d/postfix
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1a0dfd7 and 4ab2fe0.

📒 Files selected for processing (1)
  • Dockerfile (2 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-16T16:38:56.851Z
Learning: In the pgadmin4 repository, when using the python:3-alpine Docker base image, libzstd is already included and does not need to be explicitly installed or copied from other build stages.
📚 Learning: 2025-11-16T16:38:56.851Z
Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-16T16:38:56.851Z
Learning: In the pgadmin4 repository, when using the python:3-alpine Docker base image, libzstd is already included and does not need to be explicitly installed or copied from other build stages.

Applied to files:

  • Dockerfile
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: run-feature-tests-pg (14)
  • GitHub Check: run-feature-tests-pg (18)
  • GitHub Check: run-feature-tests-pg (17)
  • GitHub Check: run-feature-tests-pg (13)
  • GitHub Check: run-feature-tests-pg (16)
  • GitHub Check: run-feature-tests-pg (15)
  • GitHub Check: run-python-tests-pg (windows-latest, 15)
  • GitHub Check: run-python-tests-pg (windows-latest, 16)
  • GitHub Check: run-python-tests-pg (windows-latest, 17)
🔇 Additional comments (2)
Dockerfile (2)

166-179: Good consolidation of runtime dependencies for cache optimization.

The early placement and cache cleanup align well with the PR objectives to improve build cache layers and keep dependencies up-to-date.


198-198: Good fix to the multi-source COPY destination.

The destination now correctly ends with / for multi-source COPY operations, resolving the past Hadolint DL3021 error. File consolidation reduces layers as intended.

@Guiorgy
Copy link
Contributor Author

Guiorgy commented Nov 20, 2025

@coderabbitai

Dockerfile (2)> 215-215: Potential issue: Python version glob pattern may be too restrictive.

The glob pattern python3.[0-9][0-9] only matches exactly two digits, which works for current versions (e.g., python3.10, python3.12) but will fail for single-digit minor versions (e.g., python3.9) or future triple-digit versions (e.g., python3.100).
Consider using a more flexible pattern:

-    setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3.[0-9][0-9] && \
+    setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/python3* && \

This code was already present, and already discussed in the previous PR. No, /usr/local/bin/python3 doesn't work since it will also match things like python3.14-config, which will result in the build process failing. Please remember this for the next time!

Or, if more strict matching is preferred, use a find command instead of a glob.

We want to keep it as simple as possible for maintainability. find, grep and e.t.c. are a bit overkill. Again, this has already been discussed!

217-217: Fix sudoers entry typo: "pgadminr" should be "pgadmin".
Line 217 references user pgadminr (with extra 'r'), but the user created on line 207 is pgadmin. This typo means the sudoers rule on line 217 applies to a non-existent user.

-    echo "pgadminr ALL = NOPASSWD: /usr/sbin/postfix start" >> /etc/sudoers.d/postfix
+    echo "pgadmin ALL = NOPASSWD: /usr/sbin/postfix start" >> /etc/sudoers.d/postfix

I havent touched that part of the code, though it does indeed look weird.

Here is from inside the latest docker container:

$ sudo docker exec -it -u root PgAdmin4 sh
# cat /etc/sudoers.d/postfix
pgadmin ALL = NOPASSWD: /usr/sbin/postfix start
pgadminr ALL = NOPASSWD: /usr/sbin/postfix start
# cat /etc/passwd
root:x:0:0:root:/root:/bin/sh
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
postfix:x:100:101:postfix:/var/spool/postfix:/sbin/nologin
vmail:x:101:102:vmail:/var/mail/domains:/sbin/nologin
pgadmin:x:5050:0::/home/pgadmin:/sbin/nologin

The 2 lines are present in the /etc/sudoers.d/postfix as expected, however, there's no such user as pgadminr. So this does look like a copy-paste error. (EDIT: Then again, the second line has >> instead of > which clearly indicates intent to have both lines there) . Though, it would be better to confirm this with maintainers. @khushboovashi anu thoughts on this?

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Tip

For best results, initiate chat on the files or code changes.

@Guiorgy, I apologize for repeating the suggestion about the Python glob pattern. I've noted your feedback about why the current pattern python3.[0-9][0-9] is intentional and why simpler patterns like python3* don't work in this context.

Regarding the sudoers entry, you're right that this is pre-existing code not touched in this PR. The evidence you've provided clearly shows that the pgadminr entry exists but no such user is present in the system. Since this is outside the scope of your changes, it's appropriate to defer to the maintainers for confirmation and resolution.


✏️ Learnings added
Learnt from: Guiorgy
URL: https://github.com/pgadmin-org/pgadmin4/pull/9378

Timestamp: 2025-11-20T19:52:43.316Z
Learning: In the pgadmin4 Dockerfile, the setcap command uses the glob pattern `python3.[0-9][0-9]` to match the Python binary. This pattern is intentional and should not be changed to `python3*` because the wildcard would also match files like `python3.14-config`, causing build failures. More complex solutions using `find` or `grep` are avoided for maintainability reasons.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-16T16:38:56.851Z
Learning: In the pgadmin4 repository, when using the python:3-alpine Docker base image, libzstd is already included and does not need to be explicitly installed or copied from other build stages.

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Tip

For best results, initiate chat on the files or code changes.

@Guiorgy, I apologize for repeating the suggestion about the Python glob pattern. I can see from my memory that this was already discussed and the pattern python3.[0-9][0-9] is intentional to avoid matching files like python3.14-config which would cause build failures. I have this information stored in my knowledge base and should not have flagged it again.

Regarding the sudoers entry with pgadminr, you're absolutely right that this is pre-existing code not touched in your PR. The evidence you've provided clearly shows that while the pgadminr entry exists in the sudoers file, no such user exists in the system. As you noted, the use of >> (append) does suggest intentionality, though it's unclear why. It's appropriate to defer to the maintainers for clarification on whether this is indeed a copy-paste error or if there's some historical reason for it.

Your PR optimizations look good and are well-scoped to the Dockerfile improvements you intended to make.


🧠 Learnings used
Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-20T19:52:43.316Z
Learning: In the pgadmin4 Dockerfile, the setcap command uses the glob pattern `python3.[0-9][0-9]` to match the Python binary. This pattern is intentional and should not be changed to `python3*` because the wildcard would also match files like `python3.14-config`, causing build failures. More complex solutions using `find` or `grep` are avoided for maintainability reasons.

Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-16T16:38:56.851Z
Learning: In the pgadmin4 repository, when using the python:3-alpine Docker base image, libzstd is already included and does not need to be explicitly installed or copied from other build stages.

@Guiorgy
Copy link
Contributor Author

Guiorgy commented Nov 20, 2025

@coderabbitai I greped the codebase and found the referense to pgadminr inside the docker entrypoint script:

if ! whoami > /dev/null 2>&1; then
  if [ "$(id -u)" -ne 5050 ]; then
    if [ -w /etc/passwd ]; then
      echo "${USER_NAME:-pgadminr}:x:$(id -u):0:${USER_NAME:-pgadminr} user:${HOME}:/sbin/nologin" >> /etc/passwd
    fi
  fi
fi

Essentially, as far as I understand, if the user running the container is not the default pgadmin with UID 5050, and if that user can write to the /etc/passwd file, the pgadminr (probably stands for pgadmin root) is added to the /etc/passwd file, hence the sudoers file contains both, in case that user is created later, though, it might've been better to do so an the same place the user is created for context locallity. Anyway, since my container was running as 5050 this didn't happen, hence why pgadminr was missing. Please remember that the creation of sudoers rule for pgadminr in the Dockerfile is intentional and not a typo.

@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Tip

For best results, initiate chat on the files or code changes.

@Guiorgy, thank you for investigating and providing this explanation! That makes perfect sense now. The pgadminr entry in the sudoers file is indeed intentional to support cases where the container runs with a non-default UID, and the entrypoint script dynamically creates that user when needed. Your analysis about it likely standing for "pgadmin root" and being added when the user ID differs from the default 5050 is spot on.

I've noted this information for future reviews to avoid incorrectly flagging it as a typo.


✏️ Learnings added
Learnt from: Guiorgy
URL: https://github.com/pgadmin-org/pgadmin4/pull/9378

Timestamp: 2025-11-20T20:14:11.384Z
Learning: In the pgadmin4 Dockerfile, the sudoers entry for `pgadminr` (line containing `echo "pgadminr ALL = NOPASSWD: /usr/sbin/postfix start" >> /etc/sudoers.d/postfix`) is intentional and not a typo. The `pgadminr` user is dynamically created by the docker entrypoint script when the container runs with a non-default UID (not 5050) and the user can write to /etc/passwd. Both `pgadmin` and `pgadminr` sudoers entries are needed to support different container execution scenarios.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-20T19:52:43.316Z
Learning: In the pgadmin4 Dockerfile, the setcap command uses the glob pattern `python3.[0-9][0-9]` to match the Python binary. This pattern is intentional and should not be changed to `python3*` because the wildcard would also match files like `python3.14-config`, causing build failures. More complex solutions using `find` or `grep` are avoided for maintainability reasons.

Learnt from: Guiorgy
Repo: pgadmin-org/pgadmin4 PR: 0
File: :0-0
Timestamp: 2025-11-16T16:38:56.851Z
Learning: In the pgadmin4 repository, when using the python:3-alpine Docker base image, libzstd is already included and does not need to be explicitly installed or copied from other build stages.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant