Skip to content

refactor(docker): improve Dockerfile security, caching and build stru…#7325

Open
luic1ff wants to merge 1 commit intoopen-condo-software:mainfrom
luic1ff:patch-1
Open

refactor(docker): improve Dockerfile security, caching and build stru…#7325
luic1ff wants to merge 1 commit intoopen-condo-software:mainfrom
luic1ff:patch-1

Conversation

@luic1ff
Copy link
Copy Markdown

@luic1ff luic1ff commented Mar 6, 2026

refactor(docker): improve Dockerfile structure and production readiness

Key improvements:

  • safer multi-stage build
  • better dependency caching (pip/yarn)
  • avoid potential conflicts when copying /usr/local
  • add non-root runtime user
  • reduce image size by separating build and runtime layers
  • improve reproducibility of dependency installation

Summary by CodeRabbit

  • Chores
    • Optimized container build process with improved dependency caching and multi-stage architecture.
    • Enhanced security by implementing non-root user execution.
    • Added health checks and configurable parameters for deployment flexibility.
    • Streamlined runtime layer to reduce image footprint.

…cture

Signed-off-by: luiciff <luiciff.employment@gmail.com>
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 6, 2026

📝 Walkthrough

Walkthrough

The Dockerfile architecture was restructured from a multi-stage design into a consolidated single-base approach with dedicated dependency and builder stages, including new runtime configurations, health checks, and user management improvements.

Changes

Cohort / File(s) Summary
Dockerfile Restructuring
Dockerfile
Refactored multi-stage build pipeline: consolidated base images, introduced isolated python-deps and node-deps stages with explicit caching strategies, reworked builder stage to copy from node-deps and build with Yarn, redesigned runtime stage with non-root user creation and artifact copying, added HEALTHCHECK and EXPOSE directives, introduced ARG-based configuration for REGISTRY, NODE_VERSION, PYTHON_VERSION, APP_USER, and APP_UID, and streamlined file copy semantics and permissions across stages.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A Dockerfile once tangled and wide,
Now builds in stages, with confidence as guide,
Dependencies cached, layers lean and clean,
The swiftest container I've ever seen! ✨
With health checks and users, it's ready to fly,
Our rabbit-fast builds reach the sky! 🚀

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately summarizes the main changes: refactoring the Dockerfile to improve security, caching, and build structure, which aligns with the AI-generated summary showing multi-stage build optimization, dependency caching strategies, security enhancements (non-root user), and streamlined build pipeline.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Copy link
Copy Markdown
Contributor

@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: 4

🧹 Nitpick comments (1)
Dockerfile (1)

86-89: Consider COPY --chown to avoid separate chown -R step.

chown -R on a large /app directory adds build time and an extra layer. Using --chown on COPY is more efficient.

Proposed optimization
-COPY --from=builder /app /app
-COPY --from=python-deps /usr/local/lib/python3* /usr/local/lib/
-
-RUN chown -R ${APP_USER}:${APP_USER} /app
+COPY --from=builder --chown=${APP_USER}:${APP_USER} /app /app
+COPY --from=python-deps /usr/local/lib/python3* /usr/local/lib/
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 86 - 89, The Dockerfile currently does a separate
RUN chown -R ${APP_USER}:${APP_USER} /app which adds time and a layer; instead
modify the COPY instructions (COPY --from=builder /app /app and COPY
--from=python-deps /usr/local/lib/python3* /usr/local/lib/) to use the
--chown=${APP_USER}:${APP_USER} flag so files are owned correctly on copy and
then remove the RUN chown -R ${APP_USER}:${APP_USER} /app step; ensure APP_USER
is defined earlier in the Dockerfile and apply --chown to both COPY lines that
provide files needing that ownership.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Dockerfile`:
- Around line 37-38: The RUN step mounts /root/.cache/pip but also passes pip's
--no-cache-dir, which disables the benefit of the mount; update the Dockerfile
RUN command that invokes pip install (the RUN line installing -r
requirements.txt) to remove the --no-cache-dir flag so pip can use the mounted
cache (keep the --mount=type=cache,target=/root/.cache/pip as-is).
- Around line 3-7: The Dockerfile declares ARG PYTHON_VERSION but never uses it;
either remove the unused ARG or wire it into the image build (for example use
PYTHON_VERSION in the base image or package installation). If you choose to use
it, reference PYTHON_VERSION (the ARG) in the Dockerfile where Python is
installed (e.g., inject it into the FROM line or the apt/yum/pip install
command) so the variable is actually consumed, otherwise delete the ARG line to
avoid confusion with the other ARGs (REGISTRY, NODE_VERSION, APP_USER, APP_UID).
- Around line 96-104: The HEALTHCHECK in the Dockerfile references the
wrong-cased filename "healthcheck.js" which will fail on case-sensitive
filesystems; update the HEALTHCHECK command (the HEALTHCHECK line using CMD node
healthcheck.js || exit 1) to use the correct filename "healthCheck.js" so it
matches the actual module name (healthCheck.js) and prevents runtime failures.
- Around line 70-88: The Dockerfile references a missing requirements.txt and
includes an unused python-deps multi-stage and a COPY from that stage; either
remove the python-deps stage and the related COPY --from=python-deps line
(eliminating Python install lines and any ENV/PACKAGES tied to it) or, if Python
is required, add a valid requirements.txt and ensure the python-deps stage
installs those deps; locate the python-deps stage declaration and the COPY
--from=python-deps /usr/local/lib/python3* /usr/local/lib/ and update
accordingly to delete the dead Python stage and copy, or supply the missing
requirements.txt so the stage can succeed.

---

Nitpick comments:
In `@Dockerfile`:
- Around line 86-89: The Dockerfile currently does a separate RUN chown -R
${APP_USER}:${APP_USER} /app which adds time and a layer; instead modify the
COPY instructions (COPY --from=builder /app /app and COPY --from=python-deps
/usr/local/lib/python3* /usr/local/lib/) to use the
--chown=${APP_USER}:${APP_USER} flag so files are owned correctly on copy and
then remove the RUN chown -R ${APP_USER}:${APP_USER} /app step; ensure APP_USER
is defined earlier in the Dockerfile and apply --chown to both COPY lines that
provide files needing that ownership.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: c46351da-25e2-4e8b-82c8-25cced85c549

📥 Commits

Reviewing files that changed from the base of the PR and between d4500fa and 6030c38.

📒 Files selected for processing (1)
  • Dockerfile

Comment on lines 3 to +7
ARG REGISTRY=docker.io
ARG NODE_VERSION=24
ARG PYTHON_VERSION=3.12
ARG APP_USER=app
ARG APP_UID=10001
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

PYTHON_VERSION ARG is defined but never used.

The PYTHON_VERSION ARG at line 5 is declared but not referenced anywhere in the Dockerfile. Either wire it into the base image or package installation, or remove it to avoid confusion.

Proposed fix: Remove unused ARG or use it

Option 1: Remove unused ARG

 ARG REGISTRY=docker.io
 ARG NODE_VERSION=24
-ARG PYTHON_VERSION=3.12
 ARG APP_USER=app
 ARG APP_UID=10001

Option 2: Use it (e.g., in apt-get install)

-    python3 \
-    python3-pip \
-    python3-venv \
+    python${PYTHON_VERSION} \
+    python${PYTHON_VERSION}-pip \
+    python${PYTHON_VERSION}-venv \
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
ARG REGISTRY=docker.io
ARG NODE_VERSION=24
ARG PYTHON_VERSION=3.12
ARG APP_USER=app
ARG APP_UID=10001
ARG REGISTRY=docker.io
ARG NODE_VERSION=24
ARG APP_USER=app
ARG APP_UID=10001
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 3 - 7, The Dockerfile declares ARG PYTHON_VERSION
but never uses it; either remove the unused ARG or wire it into the image build
(for example use PYTHON_VERSION in the base image or package installation). If
you choose to use it, reference PYTHON_VERSION (the ARG) in the Dockerfile where
Python is installed (e.g., inject it into the FROM line or the apt/yum/pip
install command) so the variable is actually consumed, otherwise delete the ARG
line to avoid confusion with the other ARGs (REGISTRY, NODE_VERSION, APP_USER,
APP_UID).

Comment on lines +37 to +38
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-cache-dir -r requirements.txt
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Contradictory caching flags: --mount=type=cache with --no-cache-dir.

The cache mount at /root/.cache/pip is intended to persist pip's download cache across builds, but --no-cache-dir explicitly tells pip to skip its cache. Remove --no-cache-dir to actually benefit from the cache mount.

Proposed fix
 RUN --mount=type=cache,target=/root/.cache/pip \
-    pip install --no-cache-dir -r requirements.txt
+    pip install -r requirements.txt
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
RUN --mount=type=cache,target=/root/.cache/pip \
pip install --no-cache-dir -r requirements.txt
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 37 - 38, The RUN step mounts /root/.cache/pip but
also passes pip's --no-cache-dir, which disables the benefit of the mount;
update the Dockerfile RUN command that invokes pip install (the RUN line
installing -r requirements.txt) to remove the --no-cache-dir flag so pip can use
the mounted cache (keep the --mount=type=cache,target=/root/.cache/pip as-is).

Comment on lines +70 to +88
FROM ${REGISTRY}/node:${NODE_VERSION}-bookworm-slim AS runtime

ARG APP_USER
ARG APP_UID

ENV NODE_ENV=production \
PYTHONUNBUFFERED=1

RUN useradd --system \
--uid ${APP_UID} \
--create-home \
--home-dir /app \
${APP_USER}

WORKDIR /app
COPY --from=builder --chown=app:app /app /app

COPY --from=builder /app /app
COPY --from=python-deps /usr/local/lib/python3* /usr/local/lib/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, let's see the full Dockerfile structure
cd . && find . -name "Dockerfile" -type f

Repository: open-condo-software/condo

Length of output: 81


🏁 Script executed:

# Look for werf.yaml to understand the application structure
find . -name "werf.yaml" -o -name "werf.yml" | head -5

Repository: open-condo-software/condo

Length of output: 80


🏁 Script executed:

# Check the full Dockerfile context around the runtime stage
cat -n Dockerfile | head -100

Repository: open-condo-software/condo

Length of output: 2733


🏁 Script executed:

# Check werf.yaml to understand what the application does
head -100 werf.yaml

Repository: open-condo-software/condo

Length of output: 2273


🏁 Script executed:

# Check requirements.txt to see what Python packages are installed
cat requirements.txt

Repository: open-condo-software/condo

Length of output: 117


🏁 Script executed:

# Search for any Python process spawning or execution in the application code
rg -i 'spawn|exec|child_process.*py|python' --type=js --type=ts -g '!node_modules' -C2 | head -80

Repository: open-condo-software/condo

Length of output: 6516


🏁 Script executed:

# Search for requirements.txt throughout the entire repository
find . -name "requirements.txt" -type f

Repository: open-condo-software/condo

Length of output: 51


🏁 Script executed:

# Check for any build scripts or CI/CD configuration that references the Dockerfile
find . -name "*.yml" -o -name "*.yaml" -o -name "*.sh" | grep -E '(workflow|action|compose|script)' | head -20

Repository: open-condo-software/condo

Length of output: 904


🏁 Script executed:

# Check if there are any references to the Dockerfile in the repository
rg -l "Dockerfile" --type=yaml --type=json --type=sh -g '!.git'

Repository: open-condo-software/condo

Length of output: 87


🏁 Script executed:

# Check docker-compose.yml to see if it references the Dockerfile
cat docker-compose.yml | head -50

Repository: open-condo-software/condo

Length of output: 1201


🏁 Script executed:

# Check deployment workflows to see which build system is used (werf vs Dockerfile)
rg -l "werf|Dockerfile" .github/workflows/ | head -5

Repository: open-condo-software/condo

Length of output: 254


🏁 Script executed:

# Look at one of the production deployment workflows to understand the build process
head -80 .github/workflows/deploy_production.yaml

Repository: open-condo-software/condo

Length of output: 3986


🏁 Script executed:

# Check git history to see if requirements.txt was recently removed or if it never existed
git log --all --full-history --oneline -- requirements.txt | head -20

Repository: open-condo-software/condo

Length of output: 51


🏁 Script executed:

# Check if requirements.txt might be generated or created during build
rg "requirements\.txt" Dockerfile werf.yaml

Repository: open-condo-software/condo

Length of output: 213


🏁 Script executed:

# Look for any Python dependencies or imports that might suggest the app needs Python
find apps packages -name "*.py" -type f 2>/dev/null | wc -l

Repository: open-condo-software/condo

Length of output: 70


Dockerfile cannot build: missing requirements.txt file

The Dockerfile references requirements.txt on line 35, but this file does not exist in the repository. This will cause the python-deps stage to fail during build, breaking docker-compose.yml local development.

Additionally, the application contains zero Python files and is purely Node.js-based. The Python installation (lines 31-38, 87) is unnecessary dead code.

Either:

  1. Remove the entire python-deps stage (lines 31-38) and the Python copy on line 87 since the application doesn't use Python
  2. Add a requirements.txt file if Python support is actually required
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 70 - 88, The Dockerfile references a missing
requirements.txt and includes an unused python-deps multi-stage and a COPY from
that stage; either remove the python-deps stage and the related COPY
--from=python-deps line (eliminating Python install lines and any ENV/PACKAGES
tied to it) or, if Python is required, add a valid requirements.txt and ensure
the python-deps stage installs those deps; locate the python-deps stage
declaration and the COPY --from=python-deps /usr/local/lib/python3*
/usr/local/lib/ and update accordingly to delete the dead Python stage and copy,
or supply the missing requirements.txt so the stage can succeed.

Comment on lines +96 to +104
HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD node healthcheck.js || exit 1

############################
# Start
############################
EXPOSE 3000

CMD ["node", "server.js"]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if healthcheck.js and server.js exist in the repo
echo "=== Checking for healthcheck.js ==="
fd -t f 'healthcheck.js' | head -5

echo "=== Checking for server.js ==="
fd -t f 'server.js' | head -5

echo "=== Checking package.json for entry points ==="
fd -t f 'package.json' -d 1 -x jq -r '.scripts.start // .main // "not found"' {}

Repository: open-condo-software/condo

Length of output: 502


🏁 Script executed:

#!/bin/bash
# Find the Dockerfile
echo "=== Finding Dockerfile ==="
fd -t f 'Dockerfile' | head -10

echo ""
echo "=== Checking app directory structure for the main app ==="
# Look for typical app directories
ls -la apps/ 2>/dev/null | head -20

echo ""
echo "=== Looking for server.js and healthcheck.js in apps ==="
fd -t f -e js 'server\.js|healthcheck\.js|healthCheck\.js' apps/ | head -20

Repository: open-condo-software/condo

Length of output: 1528


🏁 Script executed:

#!/bin/bash
echo "=== Dockerfile content ==="
cat -n Dockerfile | head -110

echo ""
echo "=== Checking for server.js in root and common locations ==="
find . -maxdepth 3 -name "server.js" -type f 2>/dev/null | grep -v node_modules | head -20

Repository: open-condo-software/condo

Length of output: 2944


🏁 Script executed:

#!/bin/bash
echo "=== Searching for all healthcheck variants ==="
fd -i 'healthcheck' | head -20

echo ""
echo "=== Checking if there's a healthcheck.js at root or in common build output ==="
ls -la healthcheck.js 2>/dev/null || echo "Not found at root"

echo ""
echo "=== Checking packages directory for healthcheck files ==="
find packages -name '*healthcheck*' -o -name '*healthCheck*' 2>/dev/null

echo ""
echo "=== Checking build scripts in package.json ==="
jq '.scripts | keys' package.json 2>/dev/null | head -30

Repository: open-condo-software/condo

Length of output: 709


🏁 Script executed:

#!/bin/bash
echo "=== Contents of packages/keystone ==="
ls -la packages/keystone/ | head -30

echo ""
echo "=== Check if healthcheck.js is referenced anywhere in build/config ==="
rg 'healthcheck\.js' --type js --type json | head -20

echo ""
echo "=== Check app entry point expectations ==="
ls -la apps/condo/ | head -20

Repository: open-condo-software/condo

Length of output: 3280


🏁 Script executed:

#!/bin/bash
echo "=== Contents of apps/condo/bin ==="
ls -la apps/condo/bin/

echo ""
echo "=== Check apps/condo/index.js first 50 lines ==="
head -50 apps/condo/index.js

echo ""
echo "=== Check if server.js or healthcheck.js are generated by looking at gitignore patterns ==="
cat .gitignore 2>/dev/null | grep -E 'server\.js|healthcheck\.js' || echo "No specific patterns found"

Repository: open-condo-software/condo

Length of output: 5114


Fix case sensitivity: use healthCheck.js instead of healthcheck.js.

The Dockerfile references lowercase healthcheck.js, but the actual file is packages/keystone/healthCheck.js (camelCase). This will cause a runtime error on Linux due to case-sensitive filenames. Update line 97 to CMD node healthCheck.js || exit 1.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile` around lines 96 - 104, The HEALTHCHECK in the Dockerfile
references the wrong-cased filename "healthcheck.js" which will fail on
case-sensitive filesystems; update the HEALTHCHECK command (the HEALTHCHECK line
using CMD node healthcheck.js || exit 1) to use the correct filename
"healthCheck.js" so it matches the actual module name (healthCheck.js) and
prevents runtime failures.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Mar 6, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
1 Security Hotspot

See analysis details on SonarQube Cloud

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

Labels

None yet

Development

Successfully merging this pull request may close these issues.

1 participant