From d75b754d05907898bb8750f35f8a23d8b655f3ae Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Mon, 8 Dec 2025 15:46:05 +0200 Subject: [PATCH 1/3] [feat] added dhi for react.js sample guide --- content/guides/reactjs/containerize.md | 95 +++++++++++++++++++++++--- content/guides/reactjs/develop.md | 2 +- 2 files changed, 86 insertions(+), 11 deletions(-) diff --git a/content/guides/reactjs/containerize.md b/content/guides/reactjs/containerize.md index 4e1e787665aa..4d62dd498c92 100644 --- a/content/guides/reactjs/containerize.md +++ b/content/guides/reactjs/containerize.md @@ -78,7 +78,7 @@ For consistency, please use the same responses shown in the example below when p | Question | Answer | |------------------------------------------------------------|-----------------| | What application platform does your project use? | Node | -| What version of Node do you want to use? | 24.7.0-alpine | +| What version of Node do you want to use? | 24.11.1-alpine | | Which package manager do you want to use? | npm | | Do you want to run "npm run build" before starting server? | yes | | What directory is your build output to? | dist | @@ -118,13 +118,78 @@ These updates help ensure your app is easy to deploy, fast to load, and producti ### Step 2: Configure the Dockerfile file -Copy and replace the contents of your existing `Dockerfile` with the configuration below: +Before creating a Dockerfile, you need to choose a base image. You can either use the [Node.js Official Image](https://hub.docker.com/_/node) or a Docker Hardened Image (DHI) from the [Hardened Image catalog](https://hub.docker.com/hardened-images/catalog). + +Choosing DHI offers the advantage of a production-ready image that is lightweight and secure. For more information, see [Docker Hardened Images](https://docs.docker.com/dhi/). + +> [!IMPORTANT] +> This guide uses a stable Node.js LTS image tag that is considered secure when the guide is written. Because new releases and security patches are published regularly, the tag shown here may no longer be the safest option when you follow the guide. Always review the latest available image tags and select a secure, up-to-date version before building or deploying your application. +> +> Official Node.js Docker Images: https://hub.docker.com/_/node + +{{< tabs >}} +{{< tab name="Using Docker Hardened Images" >}} +Docker Hardened Images (DHIs) are available for Node.js on [Docker Hub](https://hub.docker.com/hardened-images/catalog/dhi/node). Unlike using the Docker Official Image, you must first mirror the Node.js image into your organization and then use it as your base image. Follow the instructions in the [DHI quickstart](/dhi/get-started/) to create a mirrored repository for Node.js. + +Mirrored repositories must start with `dhi-`, for example: `FROM /dhi-node:`. In the following Dockerfile, the `FROM` instruction uses `/dhi-node:24-alpine3.22-dev` as the base image. ```dockerfile # ========================================= -# Stage 1: Build the React.js Application +# Stage 1: Build the Angular Application +# ========================================= + +# Use a lightweight Node.js image for building (customizable via ARG) +FROM /dhi-node:24-alpine3.22-dev AS builder + +# Set the working directory inside the container +WORKDIR /app + +# Copy package-related files first to leverage Docker's caching mechanism +COPY package.json package-lock.json ./ + +# Install project dependencies using npm ci (ensures a clean, reproducible install) +RUN --mount=type=cache,target=/root/.npm npm ci + +# Copy the rest of the application source code into the container +COPY . . + +# Build the Angular application +RUN npm run build + +# ========================================= +# Stage 2: Prepare Nginx to Serve Static Files # ========================================= -ARG NODE_VERSION=24.7.0-alpine + +FROM /dhi-nginx:1.28.0-alpine3.21-dev AS runner + +# Copy custom Nginx config +COPY nginx.conf /etc/nginx/nginx.conf + +# Copy the static build output from the build stage to Nginx's default HTML serving directory +COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html + +# Use a non-root user for security best practices +USER nginx + +# Expose port 8080 to allow HTTP traffic +# Note: The default NGINX container now listens on port 8080 instead of 80 +EXPOSE 8080 + +# Start Nginx directly with custom config +ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] +CMD ["-g", "daemon off;"] +``` + +{{< /tab >}} +{{< tab name="Using the Docker Official Image" >}} + +Now you need to create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following optimized configuration: + +```dockerfile +# ========================================= +# Stage 1: Build the Angular Application +# ========================================= +ARG NODE_VERSION=24.11.1-alpine ARG NGINX_VERSION=alpine3.22 # Use a lightweight Node.js image for building (customizable via ARG) @@ -142,8 +207,8 @@ RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . -# Build the React.js application (outputs to /app/dist) -RUN npm run build +# Build the Angular application +RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files @@ -151,14 +216,14 @@ RUN npm run build FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner -# Use a built-in non-root user for security best practices -USER nginx - # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory -COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html +COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html + +# Use a built-in non-root user for security best practices +USER nginx # Expose port 8080 to allow HTTP traffic # Note: The default NGINX container now listens on port 8080 instead of 80 @@ -169,6 +234,16 @@ ENTRYPOINT ["nginx", "-c", "/etc/nginx/nginx.conf"] CMD ["-g", "daemon off;"] ``` +> [!NOTE] +> We are using nginx-unprivileged instead of the standard NGINX image to follow security best practices. +> Running as a non-root user in the final image: +>- Reduces the attack surface +>- Aligns with Docker’s recommendations for container hardening +>- Helps comply with stricter security policies in production environments + +{{< /tab >}} +{{< /tabs >}} + ### Step 3: Configure the .dockerignore file The `.dockerignore` file tells Docker which files and folders to exclude when building the image. diff --git a/content/guides/reactjs/develop.md b/content/guides/reactjs/develop.md index 3811d9cf79ca..c8bf7ab2d02e 100644 --- a/content/guides/reactjs/develop.md +++ b/content/guides/reactjs/develop.md @@ -36,7 +36,7 @@ Create a file named `Dockerfile.dev` in your project root with the following con # ========================================= # Stage 1: Develop the React.js Application # ========================================= -ARG NODE_VERSION=24.7.0-alpine +ARG NODE_VERSION=24.11.1-alpine # Use a lightweight Node.js image for development FROM node:${NODE_VERSION} AS dev From e381f0f88eb18a9b295843aa534dfa7fe5fa0b62 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Mon, 8 Dec 2025 15:55:33 +0200 Subject: [PATCH 2/3] [fix] typos in the dhi example --- content/guides/reactjs/containerize.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/content/guides/reactjs/containerize.md b/content/guides/reactjs/containerize.md index 4d62dd498c92..ef2051e90318 100644 --- a/content/guides/reactjs/containerize.md +++ b/content/guides/reactjs/containerize.md @@ -135,7 +135,7 @@ Mirrored repositories must start with `dhi-`, for example: `FROM /dhi-nginx:1.28.0-alpine3.21-dev AS runner COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory -COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html +COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html # Use a non-root user for security best practices USER nginx @@ -187,7 +187,7 @@ Now you need to create a production-ready multi-stage Dockerfile. Replace the ge ```dockerfile # ========================================= -# Stage 1: Build the Angular Application +# Stage 1: Build the React.js Application # ========================================= ARG NODE_VERSION=24.11.1-alpine ARG NGINX_VERSION=alpine3.22 @@ -207,8 +207,8 @@ RUN --mount=type=cache,target=/root/.npm npm ci # Copy the rest of the application source code into the container COPY . . -# Build the Angular application -RUN npm run build +# Build the React.js application (outputs to /app/dist) +RUN npm run build # ========================================= # Stage 2: Prepare Nginx to Serve Static Files @@ -220,7 +220,7 @@ FROM nginxinc/nginx-unprivileged:${NGINX_VERSION} AS runner COPY nginx.conf /etc/nginx/nginx.conf # Copy the static build output from the build stage to Nginx's default HTML serving directory -COPY --chown=nginx:nginx --from=builder /app/dist/*/browser /usr/share/nginx/html +COPY --chown=nginx:nginx --from=builder /app/dist /usr/share/nginx/html # Use a built-in non-root user for security best practices USER nginx From 4b3fd39c7ce4dbcbe0f94ae510c6b303a5da3aaf Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Thu, 18 Dec 2025 11:13:55 +0200 Subject: [PATCH 3/3] [feat] update the guide to use dhi properly --- content/guides/reactjs/containerize.md | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/content/guides/reactjs/containerize.md b/content/guides/reactjs/containerize.md index ef2051e90318..91086a91fd24 100644 --- a/content/guides/reactjs/containerize.md +++ b/content/guides/reactjs/containerize.md @@ -129,9 +129,24 @@ Choosing DHI offers the advantage of a production-ready image that is lightweigh {{< tabs >}} {{< tab name="Using Docker Hardened Images" >}} -Docker Hardened Images (DHIs) are available for Node.js on [Docker Hub](https://hub.docker.com/hardened-images/catalog/dhi/node). Unlike using the Docker Official Image, you must first mirror the Node.js image into your organization and then use it as your base image. Follow the instructions in the [DHI quickstart](/dhi/get-started/) to create a mirrored repository for Node.js. +Docker Hardened Images (DHIs) are available for Node.js in the [Docker Hardened Images catalog](https://hub.docker.com/hardened-images/catalog/dhi/node). Docker Hardened Images are freely available to everyone with no subscription required. You can pull and use them like any other Docker image after signing in to the DHI registry. For more information, see the [DHI quickstart](/dhi/get-started/) guide. -Mirrored repositories must start with `dhi-`, for example: `FROM /dhi-node:`. In the following Dockerfile, the `FROM` instruction uses `/dhi-node:24-alpine3.22-dev` as the base image. +1. Sign in to the DHI registry: + ```console + $ docker login dhi.io + ``` + +2. Pull the Node.js DHI (check the catalog for available versions): + ```console + $ docker pull dhi.io/node:24-alpine3.22-dev + ``` + +3. Pull the Nginx DHI (check the catalog for available versions): + ```console + $ docker pull dhi.io/nginx:1.28.0-alpine3.21-dev + ``` + +In the following Dockerfile, the `FROM` instructions use `dhi.io/node:24-alpine3.22-dev` and `dhi.io/nginx:1.28.0-alpine3.21-dev` as the base images. ```dockerfile # ========================================= @@ -139,7 +154,7 @@ Mirrored repositories must start with `dhi-`, for example: `FROM /dhi-node:24-alpine3.22-dev AS builder +FROM dhi.io/node:24-alpine3.22-dev AS builder # Set the working directory inside the container WORKDIR /app @@ -160,7 +175,7 @@ RUN npm run build # Stage 2: Prepare Nginx to Serve Static Files # ========================================= -FROM /dhi-nginx:1.28.0-alpine3.21-dev AS runner +FROM dhi.io/nginx:1.28.0-alpine3.21-dev AS runner # Copy custom Nginx config COPY nginx.conf /etc/nginx/nginx.conf