Skip to content

Conversation

@abhi4578
Copy link
Contributor

@abhi4578 abhi4578 commented Apr 9, 2025

Resolves large size of the docker image for Nextjs based
frontend.

  1. Automatic copying of traced files to standalone folder by enabling it in next.config.ts
    Ref: https://nextjs.org/docs/pages/api-reference/config/next-config-js/output#automatically-copying-traced-files
  • Next.js automatically creates a standalone folder that copies only the necessary files for a production deployment including select files in node_modules.

  • Reduces the size by 10x
    Screenshot from 2025-04-09 23-18-20

  • Reduces build time by 5x
    Screenshot from 2025-04-09 23-17-51
    Screenshot from 2025-04-09 23-19-13

  1. Added non-root users
  2. Added multi-stage docker image with build and run stage as final image

Referenced the repo for constructing optimized nextjs app docker image :

1. Automatic copying of traced files to standalone
folder by enabling it in next.config.ts
Ref: https://nextjs.org/docs/pages/api-reference/config/next-config-js/output#automatically-copying-traced-files
  - Next.js  automatically creates a standalone folder
    that copies only the necessary files for a
    production deployment including select files in
    node_modules.
  - Reduces the size by 10x for this frontend
  - Reduces build time by 5x for this frontend
2. Added non-root users
3. Added multi-stage docker image with build and
   run stage as final image
@abhi4578 abhi4578 requested review from arkid15r and kasya as code owners April 9, 2025 18:08
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Apr 9, 2025

Summary by CodeRabbit

  • New Features

    • Introduced a production deployment configuration mode that delivers a standalone application build for improved performance.
  • Refactor

    • Optimized the build process to streamline dependency installation and security, including enhanced handling of file access permissions and running under a non-administrative user.

Walkthrough

This pull request restructures the Docker build process and updates the Next.js configuration. The Dockerfile now defines separate build stages—renaming the initial stage from "builder" to "base", introducing a new "builder" stage, and adding a "runner" stage that runs the application under a non-root user with proper ownership and permission settings. It simplifies dependency installation and uses explicit file permission options. Additionally, the Next.js configuration is enhanced by adding an output option.

Changes

File Change Summary
frontend/docker/Dockerfile Renamed initial stage from builder to base; added a new builder stage to install libc6-compat and pnpm; simplified dependency installation using pnpm install; updated file copying with --chmod=444; set NEXT_TELEMETRY_DISABLED in multiple stages; reworked final runner stage for security and ownership adjustments; changed launch command to node server.js.
frontend/next.config.ts Added output: "standalone" to the Next.js configuration object.

Possibly related PRs

  • updated dockerfile #1298: Modifies the Dockerfile to enhance file copying specificity, intersecting with this PR's Dockerfile restructuring work.

Suggested labels

docker

Suggested reviewers

  • kasya

📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f8981a9 and 73e9cd2.

📒 Files selected for processing (1)
  • frontend/docker/Dockerfile (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Check code quality
  • GitHub Check: CodeQL (python)
  • GitHub Check: CodeQL (javascript-typescript)
🔇 Additional comments (23)
frontend/docker/Dockerfile (23)

1-1: Efficient Base Image Selection.
Using node:22-alpine as the base image establishes a lightweight and secure foundation for the build process. This is a well-chosen starting point given the objective of reducing image size.


3-3: Informative Build Comment.
The comment clarifies that subsequent steps will install dependencies and build the project, which aids in future maintainability and understanding of the Dockerfile structure.


4-4: Clear Stage Segregation.
Renaming the stage to builder from the base image reinforces the multi-stage strategy, ensuring a clear separation between build and runtime environments.


5-7: Dependency Compatibility Inclusion.
The inclusion of libc6-compat is clearly documented with a reference link, which is useful for understanding why this package is needed on Alpine. This aligns with best practices when handling native dependencies.


10-10: Global Installation of pnpm.
Installing pnpm globally using npm provides a fast and reliable way to manage dependencies in subsequent stages. This step is correctly placed in the builder stage.


12-12: Efficient Dependency Installation.
Using pnpm install --ignore-scripts helps ensure a clean and reproducible installation of dependencies while avoiding unnecessary script executions during the container build.


14-14: Securing Configuration Files.
Copying configuration files with --chmod=444 is an excellent practice for ensuring they remain immutable during the build process, enhancing security throughout the image lifecycle.


15-15: Appropriate Public Assets Permissions.
The public directory is copied with executable permissions (--chmod=555), ensuring files are accessible while remaining non-writable. This is suitable for serving static content.


16-16: Controlled Source Files Copy.
Copying the src directory with restrictive permissions (--chmod=555) helps prevent unintended modifications during runtime. This is consistent with a best-practice approach in containerized builds.


18-20: Consistent Telemetry Disabling.
Disabling Next.js telemetry by setting ENV NEXT_TELEMETRY_DISABLED=1 is carried out with supporting comments. This configuration is beneficial for privacy and reducing unnecessary output during production builds.


23-23: Production Stage Declaration.
The comment for the production image stage clearly explains the intent behind the following steps, affirming that only essential files will be carried over to the runtime image.


24-24: Lean Production Image Creation.
Starting the production stage with FROM base AS runner effectively minimizes the image size by reusing only the necessary base components, which is in line with the PR’s optimization objectives.


25-25: Setting the Work Directory.
Establishing /app as the working directory in the production stage ensures that all subsequent commands are executed in the correct context.


27-27: Redundant Telemetry Configuration.
Reapplying ENV NEXT_TELEMETRY_DISABLED=1 in the runner stage is necessary since environment variables do not persist across stages. This maintains the intended telemetry settings in production.


28-28: Production Environment Setup.
Setting ENV NODE_ENV=production ensures that the Node.js application runs with production optimizations, which is essential for performance and security in a live environment.


32-32: Clear Ownership Strategy Comment.
The explanatory comment about copying files with root ownership preempts potential concerns regarding file mutability, thereby clarifying the intention behind the chosen permissions strategy.


33-33: Static Assets Copied with Caution.
By copying static files from /app/public with --chown=root:root and --chmod=555, the configuration intentionally prevents runtime modifications by the non-root user. This is a sound security measure for immutable assets.


35-37: Optimized Standalone Output Copy.
Copying the standalone output from /app/.next/standalone with explicit permissions and ownership ensures that only the necessary production artifacts are included. This aligns with the PR’s objective to significantly reduce image size and improve efficiency.


39-41: Secure Cache Directory Handling.
Creating the cache directory, setting its ownership to nextjs:nodejs, and assigning safe write permissions (chmod -R 755) ensures that the application can manage its cache without compromising security. The subsequent copying of static assets from .next/static maintains file integrity across stages.


43-43: Switching to Non-root User.
Changing the runtime user to nextjs is a crucial security enhancement that minimizes potential damage from vulnerabilities by running the application without superuser privileges.


47-47: Ensuring Broad Network Accessibility.
Setting ENV HOSTNAME="0.0.0.0" ensures the container binds to all network interfaces, which is necessary for correct operation in container orchestration environments.


48-48: Port Environment Consistency.
Defining ENV PORT=3000 maintains consistency with the Docker EXPOSE directive and ensures that the application listens on the correct port.


50-52: Appropriate Application Startup Command.
The CMD instruction correctly leverages the standalone output of Next.js by invoking node server.js to run the application. This complies with the documentation and matches the intended production behavior.


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 0

🧹 Nitpick comments (1)
frontend/docker/Dockerfile (1)

1-1: Consider using a more stable Node.js LTS version

While Node.js 22 is the latest version, it's very new (released in April 2024) and may have undiscovered issues. For production environments, consider using a more stable LTS version like Node.js 20.

-FROM node:22-alpine AS base
+FROM node:20-alpine AS base
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f76398 and 02e6924.

📒 Files selected for processing (2)
  • frontend/docker/Dockerfile (1 hunks)
  • frontend/next.config.ts (1 hunks)
🔇 Additional comments (11)
frontend/next.config.ts (1)

25-25: Great optimization with the standalone output mode!

Adding output: "standalone" enables Next.js to create a minimal production build with only necessary files. This optimization will significantly reduce the Docker image size (by 10x according to PR objectives) and build time (by 5x), which aligns perfectly with the optimization goals.

frontend/docker/Dockerfile (10)

1-4: Good implementation of multi-stage Docker build

Renaming the initial stage to "base" and creating a separate "builder" stage improves clarity and follows Docker best practices for multi-stage builds. This separation allows for better isolation between build and runtime environments.


5-6: Excellent addition of libc6-compat for Alpine compatibility

Adding libc6-compat is necessary when using Alpine-based Node.js images to ensure compatibility with native modules. The comment also provides helpful context about why this is needed.


9-11: Simplified dependency installation

The simplified approach to dependency installation using just pnpm install --ignore-scripts is cleaner and more maintainable than before. The --ignore-scripts flag is a good security practice to prevent potentially malicious scripts from running during installation.


13-13: Good use of explicit file permissions

Using the --chmod=444 option ensures configuration files are copied with read-only permissions, which is good for security. This prevents accidental modification of these files at runtime.


17-20: Privacy-focused configuration with telemetry disabled

Disabling Next.js telemetry is a good privacy practice, especially for production environments. The comments also provide clear context about what this setting does.


23-30: Well-structured runner stage with proper environment configuration

The runner stage is properly configured with production environment variables and telemetry settings. This separation of build and runtime environments is a best practice for Docker images.


31-32: Excellent security enhancement with non-root user

Creating a dedicated non-root user for running the application is a critical security best practice. This limits the potential impact if the application is compromised.


36-40: Effective use of Next.js standalone output

The approach of copying only the necessary files from the standalone output significantly reduces the final image size. The use of proper ownership with --chown=nextjs:nodejs ensures the application runs with the correct permissions.


41-41: Security best practice with user switching

Explicitly switching to the non-root user before running the application ensures that the container runs with minimal privileges.


47-50: Properly configured container startup

Setting HOSTNAME to "0.0.0.0" ensures the application binds to all network interfaces, which is appropriate for containerized environments. Using node server.js directly is efficient for the standalone output.

@abhi4578
Copy link
Contributor Author

abhi4578 commented Apr 9, 2025

Please review the PR @Rajgupta36 and @arkid15r .

Copy link
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: 0

🧹 Nitpick comments (4)
frontend/docker/Dockerfile (4)

3-3: Typographical Note:
The comment on line 3 has a minor typo—“depencies” should be “dependencies.”


4-6: Builder Stage Configuration:
The transition from the base image to a dedicated builder stage (FROM base AS builder) is well implemented. Additionally, installing libc6-compat (line 6) is appropriately documented with a helpful reference link. Consider verifying that this package is strictly necessary to avoid any unnecessary bloat.


13-15: File Copying with Specific Permissions:
Copying configuration files (.env, next.config.ts, etc.) with --chmod=444 and the directories (public and src) with --chmod=555 is a thoughtful security measure to enforce immutability in the production image. It’s worth double-checking that setting executable permissions on these directories aligns with your application's runtime needs.


39-41: Cache and Static Directory Setup:
The creation and permission assignment for the cache directory (/app/.next/cache on line 40) is well-handled, ensuring the nextjs user has the necessary write permissions. Similarly, copying static assets (line 41) with proper permissions fosters immutability. Note: There’s a minor typo in the comment on line 39—“cahce” should be “cache.”

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a0881f5 and fb09a84.

📒 Files selected for processing (1)
  • frontend/docker/Dockerfile (1 hunks)
🔇 Additional comments (11)
frontend/docker/Dockerfile (11)

1-1: Base Image Selection:
Using node:22-alpine as the base image is a solid choice for a lightweight and modern Node.js environment. Ensure that this version is fully compatible with your application's requirements.


9-11: Dependency Installation Process:
Using npm install --ignore-scripts -g pnpm (line 9) followed by copying the package files with enforced read-only permissions (line 10) and installing dependencies with pnpm install --ignore-scripts (line 11) provides a clear and efficient dependency setup. Make sure that ignoring scripts does not omit any critical post-install steps your project might require.


17-20: Telemetry Configuration in Build Stage:
Disabling Next.js telemetry during the build via ENV NEXT_TELEMETRY_DISABLED=1 (line 20) helps to reduce unnecessary logging and data collection. Ensure this decision is in line with your analytics and telemetry strategy.


23-25: Production Stage Initialization:
The initiation of the production (runner) stage with FROM base AS runner (line 24) and setting the working directory (line 25) is a clear and efficient strategy for separating build and runtime environments. This not only reduces the final image size but also enhances security by excluding build dependencies.


27-29: Runtime Environment Setup:
Setting ENV NODE_ENV=production (line 27) coupled with disabling telemetry at runtime (line 29) ensures that the production environment is optimized for both performance and security. Consistency between build-time and runtime configurations is maintained well.


31-32: Non-root User Creation:
The commands to create a system group (nodejs) and user (nextjs) with fixed UID/GID (lines 31–32) are exemplary practices for running containers with non-root privileges. Just verify that the chosen UID/GID do not conflict with any mounted volumes or host configurations.


34-34: Public Directory Copying:
Copying the public directory with enforced ownership (--chown=root:root) and read-execute permissions (--chmod=555) ensures that these assets remain immutable during runtime. Confirm that these permissions are sufficient for file serving without needing write access.


36-38: Standalone Output Integration:
Leveraging the standalone output by copying /app/.next/standalone (line 38) significantly minimizes the final image size. This change relies on the Next.js configuration (output: "standalone" in next.config.ts), so ensure that it remains in sync with your build strategy.


43-43: Switch to Non-root User:
Changing the user context to nextjs (line 43) is a critical security improvement, ensuring the application does not run with root privileges.


47-48: Port Environment Configuration:
Defining ENV PORT=3000 (line 47) clarifies which port the application expects to serve traffic on. Ensure that this setting is consistent with your container orchestration and any external port mappings.


49-52: Application Startup Configuration:
The comments (lines 49–50) provide clear documentation that server.js is generated by Next.js from the standalone output. Setting the hostname (ENV HOSTNAME="0.0.0.0" on line 51) and specifying the startup command (CMD ["node", "server.js"] on line 52) correctly configure the container to listen on all interfaces and launch the application. Double-check that server.js exists and is functioning as expected in the production environment.

@abhi4578 abhi4578 changed the title Optimize docker image Optimize frontend docker image Apr 9, 2025
arkid15r
arkid15r previously approved these changes Apr 9, 2025
Copy link
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

LGTM 👍

Nicely done, let's try it in staging.

@arkid15r arkid15r enabled auto-merge April 9, 2025 19:10
Copy link
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: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fb09a84 and f8981a9.

📒 Files selected for processing (2)
  • frontend/docker/Dockerfile (1 hunks)
  • frontend/next.config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/next.config.ts
🔇 Additional comments (10)
frontend/docker/Dockerfile (10)

1-2: Base Image Definition is Clear
The use of the official node:22-alpine image as the base is appropriate. This lightweight image helps maintain a small overall size.


4-8: Builder Stage Setup with Compatibility Package
The builder stage is well established by extending from the base image. The installation of libc6-compat (with a useful reference comment) ensures compatibility in Alpine environments. This attention to detail avoids potential runtime issues.


10-12: Efficient Dependency Management
Installing pnpm globally with npm and then copying the package files with read-only permissions (--chmod=444) is a good practice. Running pnpm install --ignore-scripts immediately after ensures that your dependencies are installed in a controlled manner without unintended script execution.


14-16: Securing Configuration and Source Files via Permissions
Copying configuration files (like .env, next.config.ts, etc.) with strict (read-only) permissions and copying the public and src directories with executable permissions demonstrates an intentional approach to file security and immutability. This minimizes the risk of unwanted modifications in the built image.


18-21: Proper Build-Time Telemetry and Execution
Setting the NEXT_TELEMETRY_DISABLED=1 environment variable to disable telemetry (and documenting it) is good for privacy and predictability. The build command pnpm run build is correctly placed to generate the production assets.


24-29: Runner Stage Configuration Optimized for Production
Reusing the base image for the runner stage and explicitly setting production environment variables (i.e. NODE_ENV=production and telemetry disabled) is excellent. It supports a more secure and performant container runtime.


33-33: Copying of Static and Standalone Files is Well-Configured
The commands to copy the public directory and the Next.js standalone output (/app/.next/standalone) with specific ownership (--chown=root:root) and permissions (--chmod=555) are well thought out. This guarantees that files have the correct, secure permissions while reducing the final image size by including only necessary artifacts.

Also applies to: 35-37


39-41: Cache Directory Handling for Write Permissions
Creating the .next/cache directory, adjusting its ownership to nextjs:nodejs, and setting it to writable (755) is an important step to enable caching during runtime without compromising the rest of the filesystem's immutability.


43-45: Runtime Security and Port Exposure
Switching the user to nextjs before exposing the application port (3000) is a solid security practice. Explicitly setting the HOSTNAME and PORT environment variables further clarifies network binding details.


50-52: Correct Final Command for Standalone Output
The comment clearly explains that server.js is generated during the build. Using CMD ["node", "server.js"] directly is appropriate for running the standalone Next.js server. Verify that the standalone output includes server.js as expected.

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@sonarqubecloud
Copy link

sonarqubecloud bot commented Apr 9, 2025

@arkid15r arkid15r added this pull request to the merge queue Apr 9, 2025
Merged via the queue into OWASP:main with commit 9310bfa Apr 9, 2025
22 checks passed
shdwcodr pushed a commit to shdwcodr/Nest that referenced this pull request Jun 5, 2025
* Optimize docker image

1. Automatic copying of traced files to standalone
folder by enabling it in next.config.ts
Ref: https://nextjs.org/docs/pages/api-reference/config/next-config-js/output#automatically-copying-traced-files
  - Next.js  automatically creates a standalone folder
    that copies only the necessary files for a
    production deployment including select files in
    node_modules.
  - Reduces the size by 10x for this frontend
  - Reduces build time by 5x for this frontend
2. Added non-root users
3. Added multi-stage docker image with build and
   run stage as final image

* Hardening the image

1. Resolve no write permission to running user for the
   copied resource, to make container immutable and
   reproducible
   Ref: https://sonarcloud.io/project/security_hotspots?id=OWASP_Nest&pullRequest=1323&issueStatuses=OPEN%2CCONFIRMED&sinceLeakPeriod=true&tab=code

* Merge RUN instuctions for maintainability

- Resolves issue :
https://sonarcloud.io/project/issues?id=OWASP_Nest&pullRequest=1323&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true

* Update code

* Update frontend/docker/Dockerfile

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: Arkadii Yakovets <[email protected]>
Co-authored-by: Arkadii Yakovets <[email protected]>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants