From 061c143cd95a7c93174bbe2d3646ab89e51f80c4 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Thu, 19 Jun 2025 09:36:18 +0300 Subject: [PATCH 01/13] [feat] update init node.js docs guide --- content/guides/nodejs/_index.md | 38 ++++++++++++++++++++++----- content/guides/nodejs/containerize.md | 14 +++++++--- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/content/guides/nodejs/_index.md b/content/guides/nodejs/_index.md index b7f04877a088..0b4da59b3734 100644 --- a/content/guides/nodejs/_index.md +++ b/content/guides/nodejs/_index.md @@ -15,12 +15,36 @@ params: time: 20 minutes --- -The Node.js language-specific guide teaches you how to containerize a Node.js application using Docker. In this guide, you’ll learn how to: +[Node.js](https://nodejs.org/en) is a powerful JavaScript runtime for building scalable, high-performance applications. While it's flexible and fast, managing different development and deployment environments can become complex. Docker offers a solution: a consistent, containerized environment that works everywhere. -- Containerize and run a Node.js application -- Set up a local environment to develop a Node.js application using containers -- Run tests for a Node.js application using containers -- Configure a CI/CD pipeline for a containerized Node.js application using GitHub Actions -- Deploy your containerized Node.js application locally to Kubernetes to test and debug your deployment +> +> **Acknowledgment** +> +> Docker extends its sincere gratitude to [Kristiyan Velkov](https://www.linkedin.com/in/kristiyan-velkov-763130b3/) for authoring this guide. As a Docker Captain and experienced Front-end engineer, his expertise in Docker, DevOps, and modern web development has made this resource invaluable for the community, helping developers navigate and optimize their Docker workflows. + +--- + +## What will you learn? + +In this guide, you will learn how to: + +- Containerize and run a Node.js application using Docker. +- Run unit tests inside a Docker container. +- Set up a development container environment. +- Configure GitHub Actions for CI/CD with Docker. +- Deploy your Dockerized Node.js app to Kubernetes. + +To begin, you’ll start by containerizing an existing Node.js application. + +--- + +## Prerequisites + +Before you begin, make sure you're familiar with the following: + +- Basic understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) or [TypeScript](https://www.typescriptlang.org/). +- Basic knowledge of [Node.js](https://nodejs.org/en) and [npm](https://docs.npmjs.com/about-npm) for managing dependencies and running scripts. +- Understanding of Docker concepts such as images, containers, and Dockerfiles. If you're new to Docker, start with the [Docker basics](/get-started/docker-concepts/the-basics/what-is-a-container.md) guide. + +Once you've completed the Node.js getting started modules, you’ll be ready to containerize your own Node.js application using the examples and instructions provided in this guide. -Start by containerizing an existing Node.js application. diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index 0f54039de933..c28dd38ca926 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -12,12 +12,18 @@ aliases: - /guides/language/nodejs/containerize/ --- + ## Prerequisites -- You have installed the latest version of [Docker - Desktop](/get-started/get-docker.md). -- You have a [git client](https://git-scm.com/downloads). The examples in this - section use a command-line based git client, but you can use any client. +Before you begin, make sure the following tools are installed and available on your system: + +- You have installed the latest version of [Docker Desktop](/get-started/get-docker.md). +- You have a [git client](https://git-scm.com/downloads). The examples in this section use a command-line based git client, but you can use any client. + +> **New to Docker?** +> Start with the [Docker basics](/get-started/docker-concepts/the-basics/what-is-a-container.md) guide to get familiar with key concepts like images, containers, and Dockerfiles. + +--- ## Overview From f6bcdd79f6fa07e47ce993d595a2140f96985192 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Sun, 6 Jul 2025 15:57:25 +0300 Subject: [PATCH 02/13] Merge branch 'main' of github-private.com:kristiyan-velkov/docker-docs into feat/update-node-js-guide --- content/guides/nodejs/containerize.md | 521 +++++++++++++++++--------- 1 file changed, 344 insertions(+), 177 deletions(-) diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index c28dd38ca926..021e52c45edc 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -27,36 +27,43 @@ Before you begin, make sure the following tools are installed and available on y ## Overview -This section walks you through containerizing and running a Node.js -application. +This guide walks you through the complete process of containerizing a Node.js application with Docker. You’ll learn how to create a production-ready Docker image using best practices that enhance performance, security, scalability, and operational efficiency. + +By the end of this guide, you will: + +- Containerize a Node.js application using Docker. +- Create and optimize a Dockerfile tailored for Node.js environments. +- Use multi-stage builds to separate dependencies and reduce image size. +- Configure the container for secure, efficient runtime using a non-root user. +- Follow best practices for building secure, lightweight, and maintainable Docker images. ## Get the sample application Clone the sample application to use with this guide. Open a terminal, change directory to a directory that you want to work in, and run the following command -to clone the repository: +to clone the git repository: ```console -$ git clone https://github.com/docker/docker-nodejs-sample && cd docker-nodejs-sample +$ git clone https://github.com/kristiyan-velkov/docker-nodejs-sample ``` -## Initialize Docker assets +## Generate a Dockerfile +Docker provides an interactive CLI tool called `docker init` that helps scaffold the necessary configuration files for containerizing your application. This includes generating a `Dockerfile`, `.dockerignore`, `compose.yaml`, and `README.Docker.md`. -Now that you have an application, you can create the necessary Docker assets to -containerize your application. You can use Docker Desktop's built-in Docker Init -feature to help streamline the process, or you can manually create the assets. +To begin, navigate to the root of your project directory: -{{< tabs >}} -{{< tab name="Use Docker Init" >}} +```console +$ cd docker-nodejs-sample +``` -Inside the `docker-nodejs-sample` directory, run -the `docker init` command in a terminal. `docker init` provides some default -configuration, but you'll need to answer a few questions about your application. -Refer to the following example to answer the prompts from `docker init` and use -the same answers for your prompts. +Then run the following command: ```console $ docker init +``` +You’ll see output similar to: + +```text Welcome to the Docker Init CLI! This utility will walk you through creating the following files with sensible defaults for your project: @@ -66,181 +73,299 @@ This utility will walk you through creating the following files with sensible de - README.Docker.md Let's get started! +``` -? What application platform does your project use? Node -? What version of Node do you want to use? 18.0.0 -? Which package manager do you want to use? npm -? What command do you want to use to start the app: node src/index.js -? What port does your server listen on? 3000 +The CLI will prompt you with a few questions about your app setup. +For consistency, please use the same responses shown in the example below when prompted: +| Question | Answer | +|------------------------------------------------------------|-----------------| +| What application platform does your project use? | Node | +| What version of Node do you want to use? | 22.14.0-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 | +| What command do you want to use to start the app? | npm run dev | +| What port does your server listen on? | 8080 | + +After completion, your project directory will contain the following new files: + +```text +├── docker-nodejs-sample/ +│ ├── Dockerfile +│ ├── .dockerignore +│ ├── compose.yaml +│ └── README.Docker.md ``` +--- -{{< /tab >}} -{{< tab name="Manually create assets" >}} +## Build the Docker image + +The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, Node.js is a front-end library that compiles into static assets, so we need to tailor the Dockerfile to optimize for how React applications are built and served in a production environment. + +### Step 1: Review the generated files + +In this step, you’ll improve the Dockerfile and configuration files by following best practices: + +- Use multi-stage builds to keep the final image clean and small +- Improve performance and security by only including what’s needed + +These updates help ensure your app is easy to deploy, fast to load, and production-ready. + +> [!NOTE] +> A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. +> For full details, see the [Dockerfile reference](/reference/dockerfile/). + + +### Step 2: Configure the Dockerfile file + +Copy and replace the contents of your existing `Dockerfile` with the configuration below: + +```dockerfile +# ---------------------------------------- +# Stage 1: Base Layer for Reuse +# Installs dependencies using npm ci +# ---------------------------------------- + ARG NODE_VERSION=22.14.0-alpine + FROM node:${NODE_VERSION} AS base + + # Set working directory inside the container + WORKDIR /app + + # Copy dependency definitions + COPY package.json package-lock.json ./ + + # Install exact dependencies (clean, deterministic install) + RUN --mount=type=cache,target=/root/.npm npm ci + + + # ---------------------------------------- + # Stage 2: Development Environment + # Includes devDependencies and nodemon + # ---------------------------------------- + FROM base AS development + + ENV NODE_ENV=development + + # Install all deps including dev tools like nodemon and jest + RUN --mount=type=cache,target=/root/.npm npm install + + # Copy source code after deps to optimize caching + COPY . . + + # Expose app and debugger ports + EXPOSE 8080 9229 + + # Start the app in development mode with inspector enabled + CMD ["npm", "run", "dev"] + + + # ---------------------------------------- + # Stage 3: Production Build + # Installs only production dependencies + # ---------------------------------------- + FROM base AS production + + ENV NODE_ENV=production + + # Install only production dependencies + RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev + + # Copy entire source codebase + COPY . . + + # Drop root privileges for security + USER node + + # Expose application port + EXPOSE 8080 + + # Start the app in production mode with inspector enabled + CMD ["npm", "run", "prod"] + + + # ---------------------------------------- + # Stage 4: Testing Environment + # Runs tests with devDependencies (e.g. jest) + # ---------------------------------------- + FROM base AS test + + ENV NODE_ENV=test + + # Install dependencies including devDependencies (for testing) + RUN --mount=type=cache,target=/root/.npm npm ci --include=dev + + # Copy source files and tests + COPY . . + + # Drop privileges for safer test execution + USER node + + # Run tests + CMD ["npm", "run", "test"] + +``` -If you don't have Docker Desktop installed or prefer creating the assets manually, you can create the following files in your project directory. +### Step 3: Configure the .dockerignore file + +The `.dockerignore` file tells Docker which files and folders to exclude when building the image. + + +> [!NOTE] +> This helps: +> +> 1.Reduce image size +> 2.Speed up the build process +> 3.Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. +> +> To learn more, visit the [.dockerignore reference](/reference/dockerfile.md#dockerignore-file). + +Copy and replace the contents of your existing `.dockerignore` with the configuration below: + +```dockerignore +# ============================= +# Dependency directories +# ============================= +node_modules/ +dist/ +build/ + +# ============================= +# Logs & debugging +# ============================= +npm-debug.log* +yarn-debug.log* +pnpm-debug.log* +*.tsbuildinfo +*.log + +# ============================= +# Environment files +# ============================= +.env +.env.* +*.env + +# ============================= +# Editor / IDE configs +# ============================= +.vscode/ +.idea/ +*.swp + +# ============================= +# OS generated files +# ============================= +.DS_Store +Thumbs.db + +# ============================= +# Git-related +# ============================= +.git/ +.gitignore + +# ============================= +# Docker / Compose files +# ============================= +.dockerignore +Dockerfile* +docker-compose* + +# ============================= +# Testing / Reports / Coverage +# ============================= +coverage/ +jest/ +reports/ +cypress/ +cypress/screenshots/ +cypress/videos/ + +# ============================= +# Misc build tools & cache +# ============================= +.tmp/ +.cache/ +.eslintcache +.vs/ + +# ============================= +# Optional Kubernetes/Helm +# ============================= +charts/ + +# ============================= +# Docs & meta files +# ============================= +README.md +LICENSE +``` -Create a file named `Dockerfile` with the following contents. +### Step 4: Build the Node.js application image -```dockerfile {collapse=true,title=Dockerfile} -# syntax=docker/dockerfile:1 +With your custom configuration in place, you're now ready to build the Docker image for your Node.js application. -# Comments are provided throughout this file to help you get started. -# If you need more help, visit the Dockerfile reference guide at -# https://docs.docker.com/go/dockerfile-reference/ +After completing the previous steps, your project directory should now contain the following files: -# Want to help us make this template better? Share your feedback here: https://forms.gle/ybq9Krt8jtBL3iCk7 +```text +├── docker-nodejs-sample/ +│ ├── Dockerfile +│ ├── .dockerignore +│ ├── compose.yaml +│ ├── nginx.conf +│ └── README.Docker.md +``` -ARG NODE_VERSION=18.0.0 +Now that your Dockerfile is configured, you can build the Docker image for your Node.js application. -FROM node:${NODE_VERSION}-alpine +> [!NOTE] +> The `docker build` command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the [build context](/build/concepts/context/#what-is-a-build-context)). -# Use production node environment by default. -ENV NODE_ENV production +Run the following command from the root of your project: +```console +$ docker build --tag docker-nodejs-sample . +``` -WORKDIR /usr/src/app +What this command does: +- Uses the Dockerfile in the current directory (.) +- Packages the application and its dependencies into a Docker image +- Tags the image as docker-nodejs-sample so you can reference it later -# Download dependencies as a separate step to take advantage of Docker's caching. -# Leverage a cache mount to /root/.npm to speed up subsequent builds. -# Leverage a bind mounts to package.json and package-lock.json to avoid having to copy them into -# into this layer. -RUN --mount=type=bind,source=package.json,target=package.json \ - --mount=type=bind,source=package-lock.json,target=package-lock.json \ - --mount=type=cache,target=/root/.npm \ - npm ci --omit=dev -# Run the application as a non-root user. -USER node +#### Step 6: View local images -# Copy the rest of the source files into the image. -COPY . . +After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](/manuals/desktop/use-desktop/images.md). Since you're already working in the terminal, let's use the Docker CLI. -# Expose the port that the application listens on. -EXPOSE 3000 +To list all locally available Docker images, run the following command: -# Run the application. -CMD node src/index.js +```console +$ docker images ``` -Create a file named `compose.yaml` with the following contents. - -```yaml {collapse=true,title=compose.yaml} -# Comments are provided throughout this file to help you get started. -# If you need more help, visit the Docker Compose reference guide at -# https://docs.docker.com/go/compose-spec-reference/ - -# Here the instructions define your application as a service called "server". -# This service is built from the Dockerfile in the current directory. -# You can add other services your application may depend on here, such as a -# database or a cache. For examples, see the Awesome Compose repository: -# https://github.com/docker/awesome-compose -services: - server: - build: - context: . - environment: - NODE_ENV: production - ports: - - 3000:3000 -# The commented out section below is an example of how to define a PostgreSQL -# database that your application can use. `depends_on` tells Docker Compose to -# start the database before your application. The `db-data` volume persists the -# database data between container restarts. The `db-password` secret is used -# to set the database password. You must create `db/password.txt` and add -# a password of your choosing to it before running `docker compose up`. -# depends_on: -# db: -# condition: service_healthy -# db: -# image: postgres -# restart: always -# user: postgres -# secrets: -# - db-password -# volumes: -# - db-data:/var/lib/postgresql/data -# environment: -# - POSTGRES_DB=example -# - POSTGRES_PASSWORD_FILE=/run/secrets/db-password -# expose: -# - 5432 -# healthcheck: -# test: [ "CMD", "pg_isready" ] -# interval: 10s -# timeout: 5s -# retries: 5 -# volumes: -# db-data: -# secrets: -# db-password: -# file: db/password.txt -``` +Example Output: -Create a file named `.dockerignore` with the following contents. - -```text {collapse=true,title=".dockerignore"} -# Include any files or directories that you don't want to be copied to your -# container here (e.g., local build artifacts, temporary files, etc.). -# -# For more help, visit the .dockerignore file reference guide at -# https://docs.docker.com/go/build-context-dockerignore/ - -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/.next -**/.cache -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/charts -**/docker-compose* -**/compose.y*ml -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -**/build -**/dist -LICENSE -README.md +```shell +REPOSITORY TAG IMAGE ID CREATED SIZE +docker-nodejs-sample latest 423525528038 14 seconds ago 75.8MB ``` -{{< /tab >}} -{{< /tabs >}} +This output provides key details about your images: -You should now have at least the following contents in your -`docker-nodejs-sample` directory. +- **Repository** – The name assigned to the image. +- **Tag** – A version label that helps identify different builds (e.g., latest). +- **Image ID** – A unique identifier for the image. +- **Created** – The timestamp indicating when the image was built. +- **Size** – The total disk space used by the image. -```text -├── docker-nodejs-sample/ -│ ├── spec/ -│ ├── src/ -│ ├── .dockerignore -│ ├── .gitignore -│ ├── compose.yaml -│ ├── Dockerfile -│ ├── package-lock.json -│ ├── package.json -│ └── README.md -``` +If the build was successful, you should see `docker-nodejs-sample` image listed. -To learn more about the files, see the following: +--- -- [Dockerfile](/reference/dockerfile.md) -- [.dockerignore](/reference/dockerfile.md#dockerignore-file) -- [compose.yaml](/reference/compose-file/_index.md) +## Run the containerized application + +In the previous step, you created a Dockerfile for your Node.js application and built a Docker image using the docker build command. Now it’s time to run that image in a container and verify that your application works as expected. -## Run the application Inside the `docker-nodejs-sample` directory, run the following command in a terminal. @@ -249,9 +374,9 @@ terminal. $ docker compose up --build ``` -Open a browser and view the application at [http://localhost:3000](http://localhost:3000). You should see a simple todo application. +Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple Node.js web application. -In the terminal, press `ctrl`+`c` to stop the application. +Press `ctrl+c` in the terminal to stop your application. ### Run the application in the background @@ -263,31 +388,73 @@ in a terminal. $ docker compose up --build -d ``` -Open a browser and view the application at [http://localhost:3000](http://localhost:3000). +Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple web application preview. + + +To confirm that the container is running, use `docker ps` command: + +```console +$ docker ps +``` + +This will list all active containers along with their ports, names, and status. Look for a container exposing port 8080. -You should see a simple todo application. +Example Output: + +```shell +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +88bced6ade95 docker-nodejs-sample-server "nginx -c /etc/nginx…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp docker-nodejs-sample-server-1 +``` -In the terminal, run the following command to stop the application. + +To stop the application, run: ```console $ docker compose down ``` -For more information about Compose commands, see the [Compose CLI -reference](/reference/cli/docker/compose/_index.md). + +> [!NOTE] +> For more information about Compose commands, see the [Compose CLI +> reference](/reference/cli/docker/compose/_index.md). + +--- ## Summary -In this section, you learned how you can containerize and run your Node.js -application using Docker. +In this guide, you learned how to containerize, build, and run a Node.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup. -Related information: +What you accomplished: +- Initialized your project using `docker init` to scaffold essential Docker configuration files. +- Replaced the default `Dockerfile` with a multi-stage build that compiles the Node.js application and serves the static files using Nginx. +- Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. +- Built your Docker image using `docker build`. +- Ran the container using `docker compose up`, both in the foreground and in detached mode. +- Verified that the app was running by visiting [http://localhost:8080](http://localhost:8080). +- Learned how to stop the containerized application using `docker compose down`. -- [Dockerfile reference](/reference/dockerfile.md) -- [.dockerignore file reference](/reference/dockerfile.md#dockerignore-file) -- [Docker Compose overview](/manuals/compose/_index.md) +You now have a fully containerized Node.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency. + +--- + +## Related resources + +Explore official references and best practices to sharpen your Docker workflow: + +- [Multi-stage builds](/build/building/multi-stage/) – Learn how to separate build and runtime stages. +- [Best practices for writing Dockerfiles](/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. +- [Build context in Docker](/build/concepts/context/) – Learn how context affects image builds. +- [`docker init` CLI reference](/reference/cli/docker/init/) – Scaffold Docker assets automatically. +- [`docker build` CLI reference](/reference/cli/docker/build/) – Build Docker images from a Dockerfile. +- [`docker images` CLI reference](/reference/cli/docker/images/) – Manage and inspect local Docker images. +- [`docker compose up` CLI reference](/reference/cli/docker/compose/up/) – Start and run multi-container applications. +- [`docker compose down` CLI reference](/reference/cli/docker/compose/down/) – Stop and remove containers, networks, and volumes. + +--- ## Next steps -In the next section, you'll learn how you can develop your application using -containers. +With your Node.js application now containerized, you're ready to move on to the next step. + +In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine. + From 281cc1106f2d936a44a404e12a595c0b6e09fc4f Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Sat, 25 Oct 2025 08:48:27 +0300 Subject: [PATCH 03/13] [feat] update node.js documentation --- content/guides/nodejs/_index.md | 20 +- content/guides/nodejs/configure-ci-cd.md | 140 ---- .../guides/nodejs/configure-github-actions.md | 346 ++++++++ content/guides/nodejs/containerize.md | 665 ++++++++++----- content/guides/nodejs/deploy.md | 591 ++++++++++++-- content/guides/nodejs/develop.md | 765 ++++++++++-------- content/guides/nodejs/run-tests.md | 269 +++--- 7 files changed, 1939 insertions(+), 857 deletions(-) delete mode 100644 content/guides/nodejs/configure-ci-cd.md create mode 100644 content/guides/nodejs/configure-github-actions.md diff --git a/content/guides/nodejs/_index.md b/content/guides/nodejs/_index.md index 0b4da59b3734..dc43331e2855 100644 --- a/content/guides/nodejs/_index.md +++ b/content/guides/nodejs/_index.md @@ -15,12 +15,20 @@ params: time: 20 minutes --- -[Node.js](https://nodejs.org/en) is a powerful JavaScript runtime for building scalable, high-performance applications. While it's flexible and fast, managing different development and deployment environments can become complex. Docker offers a solution: a consistent, containerized environment that works everywhere. +[Node.js](https://nodejs.org/en) is a powerful JavaScript runtime for building scalable, high-performance applications. This guide demonstrates containerizing a modern TypeScript Node.js application with React frontend, PostgreSQL database, and production-ready Docker configurations. + +The sample application is a modern full-stack Todo application featuring: + +- **Backend**: Express.js 5.x with TypeScript, PostgreSQL database, RESTful API with comprehensive error handling +- **Frontend**: React 19 with Vite, Tailwind CSS 4, modern UI components and hooks +- **Build System**: Lightning-fast esbuild for server builds (12KB output), Vite for client builds +- **Production Features**: Multi-stage Docker builds, security hardening, health checks, graceful shutdown +- **Development Tools**: Hot reload, debugging support, comprehensive testing with Vitest +- **Architecture**: Clean separation of client/server code, shared utilities, and comprehensive test coverage -> > **Acknowledgment** > -> Docker extends its sincere gratitude to [Kristiyan Velkov](https://www.linkedin.com/in/kristiyan-velkov-763130b3/) for authoring this guide. As a Docker Captain and experienced Front-end engineer, his expertise in Docker, DevOps, and modern web development has made this resource invaluable for the community, helping developers navigate and optimize their Docker workflows. +> Docker extends its sincere gratitude to [Kristiyan Velkov](https://www.linkedin.com/in/kristiyan-velkov-763130b3/) for authoring this guide. As a Docker Captain and experienced Full-stack engineer, his expertise in Docker, DevOps, and modern web development has made this resource invaluable for the community, helping developers navigate and optimize their Docker workflows. --- @@ -42,9 +50,9 @@ To begin, you’ll start by containerizing an existing Node.js application. Before you begin, make sure you're familiar with the following: -- Basic understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) or [TypeScript](https://www.typescriptlang.org/). -- Basic knowledge of [Node.js](https://nodejs.org/en) and [npm](https://docs.npmjs.com/about-npm) for managing dependencies and running scripts. +- Basic understanding of [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) and [TypeScript](https://www.typescriptlang.org/). +- Basic knowledge of [Node.js](https://nodejs.org/en), [npm](https://docs.npmjs.com/about-npm), and [React](https://react.dev/) for modern web development. - Understanding of Docker concepts such as images, containers, and Dockerfiles. If you're new to Docker, start with the [Docker basics](/get-started/docker-concepts/the-basics/what-is-a-container.md) guide. +- Familiarity with [Express.js](https://expressjs.com/) for backend API development. Once you've completed the Node.js getting started modules, you’ll be ready to containerize your own Node.js application using the examples and instructions provided in this guide. - diff --git a/content/guides/nodejs/configure-ci-cd.md b/content/guides/nodejs/configure-ci-cd.md deleted file mode 100644 index c951b37b5d7e..000000000000 --- a/content/guides/nodejs/configure-ci-cd.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -title: Configure CI/CD for your Node.js application -linkTitle: Configure CI/CD -weight: 40 -keywords: ci/cd, github actions, node.js, node -description: Learn how to configure CI/CD using GitHub Actions for your Node.js application. -aliases: - - /language/nodejs/configure-ci-cd/ - - /guides/language/nodejs/configure-ci-cd/ ---- - -## Prerequisites - -Complete all the previous sections of this guide, starting with [Containerize a Node.js application](containerize.md). You must have a [GitHub](https://github.com/signup) account and a [Docker](https://hub.docker.com/signup) account to complete this section. - -## Overview - -In this section, you'll learn how to set up and use GitHub Actions to build and test your Docker image as well as push it to Docker Hub. You will complete the following steps: - -1. Create a new repository on GitHub. -2. Define the GitHub Actions workflow. -3. Run the workflow. - -## Step one: Create the repository - -Create a GitHub repository, configure the Docker Hub credentials, and push your source code. - -1. [Create a new repository](https://github.com/new) on GitHub. - -2. Open the repository **Settings**, and go to **Secrets and variables** > - **Actions**. - -3. Create a new **Repository variable** named `DOCKER_USERNAME` and your Docker ID as a value. - -4. Create a new [Personal Access Token (PAT)](/manuals/security/access-tokens.md#create-an-access-token) for Docker Hub. You can name this token `docker-tutorial`. Make sure access permissions include Read and Write. - -5. Add the PAT as a **Repository secret** in your GitHub repository, with the name - `DOCKERHUB_TOKEN`. - -6. In your local repository on your machine, run the following command to change - the origin to the repository you just created. Make sure you change - `your-username` to your GitHub username and `your-repository` to the name of - the repository you created. - - ```console - $ git remote set-url origin https://github.com/your-username/your-repository.git - ``` - -7. Run the following commands to stage, commit, and push your local repository to GitHub. - - ```console - $ git add -A - $ git commit -m "my commit" - $ git push -u origin main - ``` - -## Step two: Set up the workflow - -Set up your GitHub Actions workflow for building, testing, and pushing the image -to Docker Hub. - -1. Go to your repository on GitHub and then select the **Actions** tab. - -2. Select **set up a workflow yourself**. - - This takes you to a page for creating a new GitHub actions workflow file in - your repository, under `.github/workflows/main.yml` by default. - -3. In the editor window, copy and paste the following YAML configuration. - - ```yaml - name: ci - - on: - push: - branches: - - main - - jobs: - build: - runs-on: ubuntu-latest - steps: - - name: Login to Docker Hub - uses: docker/login-action@v3 - with: - username: ${{ vars.DOCKER_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - - name: Build and test - uses: docker/build-push-action@v6 - with: - target: test - load: true - - - name: Build and push - uses: docker/build-push-action@v6 - with: - platforms: linux/amd64,linux/arm64 - push: true - target: prod - tags: ${{ vars.DOCKER_USERNAME }}/${{ github.event.repository.name }}:latest - ``` - - For more information about the YAML syntax for `docker/build-push-action`, - refer to the [GitHub Action README](https://github.com/docker/build-push-action/blob/master/README.md). - -## Step three: Run the workflow - -Save the workflow file and run the job. - -1. Select **Commit changes...** and push the changes to the `main` branch. - - After pushing the commit, the workflow starts automatically. - -2. Go to the **Actions** tab. It displays the workflow. - - Selecting the workflow shows you the breakdown of all the steps. - -3. When the workflow is complete, go to your - [repositories on Docker Hub](https://hub.docker.com/repositories). - - If you see the new repository in that list, it means the GitHub Actions - successfully pushed the image to Docker Hub. - -## Summary - -In this section, you learned how to set up a GitHub Actions workflow for your Node.js application. - -Related information: - -- [Introduction to GitHub Actions](/guides/gha.md) -- [Docker Build GitHub Actions](/manuals/build/ci/github-actions/_index.md) -- [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) - -## Next steps - -Next, learn how you can locally test and debug your workloads on Kubernetes before deploying. diff --git a/content/guides/nodejs/configure-github-actions.md b/content/guides/nodejs/configure-github-actions.md new file mode 100644 index 000000000000..08b32dbe0275 --- /dev/null +++ b/content/guides/nodejs/configure-github-actions.md @@ -0,0 +1,346 @@ +--- +title: Automate your builds with GitHub Actions +linkTitle: Automate your builds with GitHub Actions +weight: 50 +keywords: CI/CD, GitHub Actions, Node.js, Docker +description: Learn how to configure CI/CD using GitHub Actions for your Node.js application. +aliases: + - /language/nodejs/configure-ci-cd/ + - /guides/language/nodejs/configure-ci-cd/ +--- + +## Prerequisites + +Complete all the previous sections of this guide, starting with [Containerize a Node.js application](containerize.md). + +You must also have: + +- A [GitHub](https://github.com/signup) account. +- A [Docker Hub](https://hub.docker.com/signup) account. + +--- + +## Overview + +In this section, you'll set up a **CI/CD pipeline** using [GitHub Actions](https://docs.github.com/en/actions) to automatically: + +- Build your Node.js application inside a Docker container. +- Run comprehensive tests including unit tests, integration tests, and linting. +- Perform security scanning and vulnerability assessment. +- Push production-ready images to [Docker Hub](https://hub.docker.com). + +--- + +## Connect your GitHub repository to Docker Hub + +To enable GitHub Actions to build and push Docker images, you'll securely store your Docker Hub credentials in your new GitHub repository. + +### Step 1: Connect your GitHub repository to Docker Hub + +1. Create a Personal Access Token (PAT) from [Docker Hub](https://hub.docker.com) + 1. Go to your **Docker Hub account → Account Settings → Security**. + 2. Generate a new Access Token with **Read/Write** permissions. + 3. Name it something like `docker-nodejs-sample`. + 4. Copy and save the token — you'll need it in Step 4. + +2. Create a repository in [Docker Hub](https://hub.docker.com/repositories/) + 1. Go to your **Docker Hub account → Create a repository**. + 2. For the Repository Name, use something descriptive — for example: `nodejs-sample`. + 3. Once created, copy and save the repository name — you'll need it in Step 4. + +3. Create a new [GitHub repository](https://github.com/new) for your Node.js project + +4. Add Docker Hub credentials as GitHub repository secrets + + In your newly created GitHub repository: + 1. Navigate to: + **Settings → Secrets and variables → Actions → New repository secret**. + + 2. Add the following secrets: + + | Name | Value | + | ------------------------ | ------------------------------------------------ | + | `DOCKER_USERNAME` | Your Docker Hub username | + | `DOCKERHUB_TOKEN` | Your Docker Hub access token (created in Step 1) | + | `DOCKERHUB_PROJECT_NAME` | Your Docker Project Name (created in Step 2) | + + These secrets let GitHub Actions to authenticate securely with Docker Hub during automated workflows. + +5. Connect Your Local Project to GitHub + + Link your local project `docker-nodejs-sample` to the GitHub repository you just created by running the following command from your project root: + + ```console + $ git remote set-url origin https://github.com/{your-username}/{your-repository-name}.git + ``` + + > [!IMPORTANT] + > Replace `{your-username}` and `{your-repository}` with your actual GitHub username and repository name. + + To confirm that your local project is correctly connected to the remote GitHub repository, run: + + ```console + $ git remote -v + ``` + + You should see output similar to: + + ```console + origin https://github.com/{your-username}/{your-repository-name}.git (fetch) + origin https://github.com/{your-username}/{your-repository-name}.git (push) + ``` + + This confirms that your local repository is properly linked and ready to push your source code to GitHub. + +6. Push Your Source Code to GitHub + + Follow these steps to commit and push your local project to your GitHub repository: + 1. Stage all files for commit. + + ```console + $ git add -A + ``` + + This command stages all changes — including new, modified, and deleted files — preparing them for commit. + + 2. Commit your changes. + + ```console + $ git commit -m "Initial commit with CI/CD pipeline" + ``` + + This command creates a commit that snapshots the staged changes with a descriptive message. + + 3. Push the code to the `main` branch. + + ```console + $ git push -u origin main + ``` + + This command pushes your local commits to the `main` branch of the remote GitHub repository and sets the upstream branch. + +Once completed, your code will be available on GitHub, and any GitHub Actions workflow you've configured will run automatically. + +> [!NOTE] +> Learn more about the Git commands used in this step: +> +> - [Git add](https://git-scm.com/docs/git-add) – Stage changes (new, modified, deleted) for commit +> - [Git commit](https://git-scm.com/docs/git-commit) – Save a snapshot of your staged changes +> - [Git push](https://git-scm.com/docs/git-push) – Upload local commits to your GitHub repository +> - [Git remote](https://git-scm.com/docs/git-remote) – View and manage remote repository URLs + +--- + +### Step 2: Set up the workflow + +Now you'll create a GitHub Actions workflow that builds your Docker image, runs tests, and pushes the image to Docker Hub. + +1. Go to your repository on GitHub and select the **Actions** tab in the top menu. + +2. Select **Set up a workflow yourself** when prompted. + + This opens an inline editor to create a new workflow file. By default, it will be saved to: + `.github/workflows/main.yml` + +3. Add the following workflow configuration to the new file: + +```yaml +name: CI/CD – Node.js Application with Docker + +on: + push: + branches: [main] + pull_request: + branches: [main] + types: [opened, synchronize, reopened] + +jobs: + test: + name: Run Node.js Tests + runs-on: ubuntu-latest + + services: + postgres: + image: postgres:16-alpine + env: + POSTGRES_DB: todoapp_test + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache npm dependencies + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('**/package-lock.json') }} + restore-keys: ${{ runner.os }}-npm- + + - name: Build test image + uses: docker/build-push-action@v6 + with: + context: . + target: test + tags: nodejs-app-test:latest + platforms: linux/amd64 + load: true + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache,mode=max + + - name: Run tests inside container + run: | + docker run --rm \ + --network host \ + -e NODE_ENV=test \ + -e POSTGRES_HOST=localhost \ + -e POSTGRES_PORT=5432 \ + -e POSTGRES_DB=todoapp_test \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_PASSWORD=postgres \ + nodejs-app-test:latest + env: + CI: true + timeout-minutes: 10 + + build-and-push: + name: Build and Push Docker Image + runs-on: ubuntu-latest + needs: test + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Cache Docker layers + uses: actions/cache@v4 + with: + path: /tmp/.buildx-cache + key: ${{ runner.os }}-buildx-${{ github.sha }} + restore-keys: ${{ runner.os }}-buildx- + + - name: Extract metadata + id: meta + run: | + echo "REPO_NAME=${GITHUB_REPOSITORY##*/}" >> "$GITHUB_OUTPUT" + echo "SHORT_SHA=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT" + + - name: Log in to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push multi-arch production image + uses: docker/build-push-action@v6 + with: + context: . + target: production + push: true + platforms: linux/amd64,linux/arm64 + tags: | + ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKERHUB_PROJECT_NAME }}:latest + ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKERHUB_PROJECT_NAME }}:${{ steps.meta.outputs.SHORT_SHA }} + cache-from: type=local,src=/tmp/.buildx-cache + cache-to: type=local,dest=/tmp/.buildx-cache,mode=max +``` + +This workflow performs the following tasks for your Node.js application: + +- Triggers on every `push` or `pull request` targeting the `main` branch. +- Builds a test Docker image using the `test` stage in your Dockerfile. +- Executes comprehensive tests (101 tests) inside a clean, containerized environment with PostgreSQL integration to ensure consistency. +- Halts the workflow immediately if any test fails — enforcing code quality. +- Caches both Docker build layers and npm dependencies for faster CI runs. +- Authenticates securely with Docker Hub using GitHub repository secrets. +- Builds a production-ready image using the `production` stage in your Dockerfile. +- Tags and pushes the final image to Docker Hub with both `latest` and short SHA tags for traceability. + +> [!NOTE] +> For more information about `docker/build-push-action`, refer to the [GitHub Action README](https://github.com/docker/build-push-action/blob/master/README.md). + +--- + +### Step 3: Run the workflow + +After you've added your workflow file, it's time to trigger and observe the CI/CD process in action. + +1. Commit and push your workflow file + + Select "Commit changes…" in the GitHub editor. + - This push will automatically trigger the GitHub Actions pipeline. + +2. Monitor the workflow execution + 1. Go to the Actions tab in your GitHub repository. + 2. Click into the workflow run to follow each step: **test**, **build**, **security**, and (if successful) **push** and **deploy**. + +3. Verify the Docker image on Docker Hub + - After a successful workflow run, visit your [Docker Hub repositories](https://hub.docker.com/repositories). + - You should see a new image under your repository with: + - Repository name: `${your-repository-name}` + - Tags include: + - `latest` – represents the most recent successful build; ideal for quick testing or deployment. + - `` – a unique identifier based on the commit hash, useful for version tracking, rollbacks, and traceability. + +> [!TIP] Protect your main branch +> To maintain code quality and prevent accidental direct pushes, enable branch protection rules: +> +> - Navigate to your **GitHub repo → Settings → Branches**. +> - Under Branch protection rules, click **Add rule**. +> - Specify `main` as the branch name. +> - Enable options like: +> - _Require a pull request before merging_. +> - _Require status checks to pass before merging_. +> +> This ensures that only tested and reviewed code is merged into `main` branch. + +--- + +## Summary + +In this section, you set up a comprehensive CI/CD pipeline for your containerized Node.js application using GitHub Actions. + +Here's what you accomplished: + +- Created a new GitHub repository specifically for your project. +- Generated a secure Docker Hub access token and added it to GitHub as a secret. +- Defined a GitHub Actions workflow to: + - Build your application inside a Docker container. + - Run comprehensive tests (101 tests) in a consistent, containerized environment with PostgreSQL integration. + - Push a production-ready image to Docker Hub if tests pass. +- Triggered and verified the workflow execution through GitHub Actions. +- Confirmed that your image was successfully published to Docker Hub. + +With this setup, your Node.js application is now ready for automated testing and deployment across environments — increasing confidence, consistency, and team productivity. + +--- + +## Related resources + +Deepen your understanding of automation and best practices for containerized apps: + +- [Introduction to GitHub Actions](/guides/gha.md) – Learn how GitHub Actions automate your workflows +- [Docker Build GitHub Actions](/manuals/build/ci/github-actions/_index.md) – Set up container builds with GitHub Actions +- [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) – Full reference for writing GitHub workflows +- [GitHub Container Registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry) – Learn about GHCR features and usage +- [Best practices for writing Dockerfiles](/develop/develop-images/dockerfile_best-practices/) – Optimize your image for performance and security + +--- + +## Next steps + +Next, learn how you can deploy your containerized Node.js application to Kubernetes with production-ready configuration. This helps you ensure your application behaves as expected in a production-like environment, reducing surprises during deployment. diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index 021e52c45edc..514a08ffb9ae 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -1,9 +1,9 @@ --- -title: Containerize a Node.js application -linkTitle: Containerize your app +title: Containerize a Node.js Application +linkTitle: Containerize weight: 10 keywords: node.js, node, containerize, initialize -description: Learn how to containerize a Node.js application. +description: Learn how to containerize a Node.js application with Docker by creating an optimized, production-ready image using best practices for performance, security, and scalability. aliases: - /get-started/nodejs/build-images/ - /language/nodejs/build-images/ @@ -12,7 +12,6 @@ aliases: - /guides/language/nodejs/containerize/ --- - ## Prerequisites Before you begin, make sure the following tools are installed and available on your system: @@ -48,6 +47,7 @@ $ git clone https://github.com/kristiyan-velkov/docker-nodejs-sample ``` ## Generate a Dockerfile + Docker provides an interactive CLI tool called `docker init` that helps scaffold the necessary configuration files for containerizing your application. This includes generating a `Dockerfile`, `.dockerignore`, `compose.yaml`, and `README.Docker.md`. To begin, navigate to the root of your project directory: @@ -61,6 +61,7 @@ Then run the following command: ```console $ docker init ``` + You’ll see output similar to: ```text @@ -77,15 +78,15 @@ Let's get started! The CLI will prompt you with a few questions about your app setup. For consistency, please use the same responses shown in the example below when prompted: -| Question | Answer | +| Question | Answer | |------------------------------------------------------------|-----------------| -| What application platform does your project use? | Node | -| What version of Node do you want to use? | 22.14.0-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 | -| What command do you want to use to start the app? | npm run dev | -| What port does your server listen on? | 8080 | +| What application platform does your project use? | Node | +| What version of Node do you want to use? | 22.21.0-alpine3.21 | +| 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 | +| What command do you want to use to start the app? | npm run dev | +| What port does your server listen on? | 3000 | After completion, your project directory will contain the following new files: @@ -96,18 +97,212 @@ After completion, your project directory will contain the following new files: │ ├── compose.yaml │ └── README.Docker.md ``` + +## Create a Docker Compose file + +While `docker init` generates a basic `compose.yaml` file, you'll need to create a more comprehensive configuration for this full-stack application. Replace the generated `compose.yaml` with a production-ready configuration. + +Create a new file named `compose.yml` in your project root: + +```yaml +# ======================================== +# Docker Compose Configuration +# Modern Node.js Todo Application +# ======================================== + +services: + # ======================================== + # Development Service + # ======================================== + app-dev: + build: + context: . + dockerfile: Dockerfile + target: development + container_name: todoapp-dev + ports: + - '${APP_PORT:-3000}:3000' # API server + - '${VITE_PORT:-5173}:5173' # Vite dev server + - '${DEBUG_PORT:-9229}:9229' # Node.js debugger + environment: + NODE_ENV: development + DOCKER_ENV: 'true' + POSTGRES_HOST: db + POSTGRES_PORT: 5432 + POSTGRES_DB: todoapp + POSTGRES_USER: todoapp + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}' + volumes: + - ./src:/app/src:ro + - ./package.json:/app/package.json + - ./vite.config.ts:/app/vite.config.ts:ro + - ./tailwind.config.js:/app/tailwind.config.js:ro + - ./postcss.config.js:/app/postcss.config.js:ro + depends_on: + db: + condition: service_healthy + develop: + watch: + - action: sync + path: ./src + target: /app/src + ignore: + - '**/*.test.*' + - '**/__tests__/**' + - action: rebuild + path: ./package.json + - action: sync + path: ./vite.config.ts + target: /app/vite.config.ts + - action: sync + path: ./tailwind.config.js + target: /app/tailwind.config.js + - action: sync + path: ./postcss.config.js + target: /app/postcss.config.js + restart: unless-stopped + networks: + - todoapp-network + + # ======================================== + # Production Service + # ======================================== + app-prod: + build: + context: . + dockerfile: Dockerfile + target: production + container_name: todoapp-prod + ports: + - '${PROD_PORT:-8080}:3000' + environment: + NODE_ENV: production + POSTGRES_HOST: db + POSTGRES_PORT: 5432 + POSTGRES_DB: todoapp + POSTGRES_USER: todoapp + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-https://yourdomain.com}' + depends_on: + db: + condition: service_healthy + restart: unless-stopped + deploy: + resources: + limits: + memory: '${PROD_MEMORY_LIMIT:-2G}' + cpus: '${PROD_CPU_LIMIT:-1.0}' + reservations: + memory: '${PROD_MEMORY_RESERVATION:-512M}' + cpus: '${PROD_CPU_RESERVATION:-0.25}' + security_opt: + - no-new-privileges:true + read_only: true + tmpfs: + - /tmp + networks: + - todoapp-network + profiles: + - prod + + # ======================================== + # PostgreSQL Database Service + # ======================================== + db: + image: postgres:16-alpine + container_name: todoapp-db + environment: + POSTGRES_DB: '${POSTGRES_DB:-todoapp}' + POSTGRES_USER: '${POSTGRES_USER:-todoapp}' + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - '${DB_PORT:-5432}:5432' + restart: unless-stopped + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 5s + networks: + - todoapp-network + +# ======================================== +# Volume Configuration +# ======================================== +volumes: + postgres_data: + name: todoapp-postgres-data + driver: local + +# ======================================== +# Network Configuration +# ======================================== +networks: + todoapp-network: + name: todoapp-network + driver: bridge +``` + +This Docker Compose configuration includes: + +- **Development service** (`app-dev`): Full development environment with hot reload, debugging support, and bind mounts +- **Production service** (`app-prod`): Optimized production deployment with resource limits and security hardening +- **Database service** (`db`): PostgreSQL 16 with persistent storage and health checks +- **Networking**: Isolated network for secure service communication +- **Volumes**: Persistent storage for database data + +## Create environment configuration + +Create a `.env` file to configure your application settings: + +```console +$ cp .env.example .env +``` + +Update the `.env` file with your preferred settings: + +```env +# Application Configuration +NODE_ENV=development +APP_PORT=3000 +VITE_PORT=5173 +DEBUG_PORT=9229 + +# Production Configuration +PROD_PORT=8080 +PROD_MEMORY_LIMIT=2G +PROD_CPU_LIMIT=1.0 +PROD_MEMORY_RESERVATION=512M +PROD_CPU_RESERVATION=0.25 + +# Database Configuration +POSTGRES_HOST=db +POSTGRES_PORT=5432 +POSTGRES_DB=todoapp +POSTGRES_USER=todoapp +POSTGRES_PASSWORD=todoapp_password +DB_PORT=5432 + +# Security Configuration +ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 +``` + --- ## Build the Docker image -The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, Node.js is a front-end library that compiles into static assets, so we need to tailor the Dockerfile to optimize for how React applications are built and served in a production environment. +The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, this is a full-stack TypeScript application with both backend API and frontend React components, so we need to tailor the Dockerfile to optimize for this architecture. ### Step 1: Review the generated files In this step, you’ll improve the Dockerfile and configuration files by following best practices: -- Use multi-stage builds to keep the final image clean and small -- Improve performance and security by only including what’s needed +- Use multi-stage builds to keep the final image clean and small +- Improve performance and security by only including what’s needed These updates help ensure your app is easy to deploy, fast to load, and production-ready. @@ -115,208 +310,252 @@ These updates help ensure your app is easy to deploy, fast to load, and producti > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. > For full details, see the [Dockerfile reference](/reference/dockerfile/). +### Step 2: Configure the Dockerfile file -### Step 2: Configure the Dockerfile file - -Copy and replace the contents of your existing `Dockerfile` with the configuration below: +Now you need to create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following optimized configuration: ```dockerfile -# ---------------------------------------- -# Stage 1: Base Layer for Reuse -# Installs dependencies using npm ci -# ---------------------------------------- - ARG NODE_VERSION=22.14.0-alpine - FROM node:${NODE_VERSION} AS base - - # Set working directory inside the container - WORKDIR /app - - # Copy dependency definitions - COPY package.json package-lock.json ./ - - # Install exact dependencies (clean, deterministic install) - RUN --mount=type=cache,target=/root/.npm npm ci - - - # ---------------------------------------- - # Stage 2: Development Environment - # Includes devDependencies and nodemon - # ---------------------------------------- - FROM base AS development - - ENV NODE_ENV=development - - # Install all deps including dev tools like nodemon and jest - RUN --mount=type=cache,target=/root/.npm npm install - - # Copy source code after deps to optimize caching - COPY . . - - # Expose app and debugger ports - EXPOSE 8080 9229 - - # Start the app in development mode with inspector enabled - CMD ["npm", "run", "dev"] - - - # ---------------------------------------- - # Stage 3: Production Build - # Installs only production dependencies - # ---------------------------------------- - FROM base AS production - - ENV NODE_ENV=production - - # Install only production dependencies - RUN --mount=type=cache,target=/root/.npm npm ci --omit=dev - - # Copy entire source codebase - COPY . . - - # Drop root privileges for security - USER node - - # Expose application port - EXPOSE 8080 - - # Start the app in production mode with inspector enabled - CMD ["npm", "run", "prod"] - - - # ---------------------------------------- - # Stage 4: Testing Environment - # Runs tests with devDependencies (e.g. jest) - # ---------------------------------------- - FROM base AS test - - ENV NODE_ENV=test - - # Install dependencies including devDependencies (for testing) - RUN --mount=type=cache,target=/root/.npm npm ci --include=dev - - # Copy source files and tests - COPY . . - - # Drop privileges for safer test execution - USER node - - # Run tests - CMD ["npm", "run", "test"] - +# ======================================== +# Optimized Multi-Stage Dockerfile +# Node.js TypeScript Application +# ======================================== + +ARG NODE_VERSION=22.21.0-alpine3.21 +FROM node:${NODE_VERSION} AS base + +# Set working directory +WORKDIR /app + +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 -G nodejs && \ + chown -R nodejs:nodejs /app + +# ======================================== +# Dependencies Stage +# ======================================== +FROM base AS deps + +# Copy package files +COPY package*.json ./ + +# Install production dependencies +RUN --mount=type=cache,target=/root/.npm,sharing=locked \ + npm ci --omit=dev && \ + npm cache clean --force + +# Set proper ownership +RUN chown -R nodejs:nodejs /app + +# ======================================== +# Build Dependencies Stage +# ======================================== +FROM base AS build-deps + +# Copy package files +COPY package*.json ./ + +# Install all dependencies with build optimizations +RUN --mount=type=cache,target=/root/.npm,sharing=locked \ + npm ci --no-audit --no-fund && \ + npm cache clean --force + +# Create necessary directories and set permissions +RUN mkdir -p /app/node_modules/.vite && \ + chown -R nodejs:nodejs /app + +# ======================================== +# Build Stage +# ======================================== +FROM build-deps AS build + +# Copy only necessary files for building (respects .dockerignore) +COPY --chown=nodejs:nodejs . . + +# Build the application +RUN npm run build + +# Set proper ownership +RUN chown -R nodejs:nodejs /app + +# ======================================== +# Development Stage +# ======================================== +FROM build-deps AS development + +# Set environment +ENV NODE_ENV=development \ + NPM_CONFIG_LOGLEVEL=warn + +# Copy source files +COPY . . + +# Ensure all directories have proper permissions +RUN mkdir -p /app/node_modules/.vite && \ + chown -R nodejs:nodejs /app && \ + chmod -R 755 /app + +# Switch to non-root user +USER nodejs + +# Expose ports +EXPOSE 3000 5173 9229 + +# Start development server +CMD ["npm", "run", "dev:docker"] + +# ======================================== +# Production Stage +# ======================================== +ARG NODE_VERSION=22.21.0-alpine3.21 +FROM node:${NODE_VERSION} AS production + +# Set working directory +WORKDIR /app + +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 -G nodejs && \ + chown -R nodejs:nodejs /app + +# Set optimized environment variables +ENV NODE_ENV=production \ + NODE_OPTIONS="--max-old-space-size=256 --no-warnings" \ + NPM_CONFIG_LOGLEVEL=silent + +# Copy production dependencies from deps stage +COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules +COPY --from=deps --chown=nodejs:nodejs /app/package*.json ./ +# Copy built application from build stage +COPY --from=build --chown=nodejs:nodejs /app/dist ./dist + +# Switch to non-root user for security +USER nodejs + +# Expose port +EXPOSE 3000 + +# Start production server +CMD ["node", "dist/server.js"] + +# ======================================== +# Test Stage +# ======================================== +FROM build-deps AS test + +# Set environment +ENV NODE_ENV=test \ + CI=true + +# Copy source files +COPY --chown=nodejs:nodejs . . + +# Switch to non-root user +USER nodejs + +# Run tests with coverage +CMD ["npm", "run", "test:coverage"] ``` +Key features of this ultra-optimized Dockerfile: + +- **Multi-stage builds**: Separate stages for dependencies, build, development, production, and testing +- **Fast Build System**: Uses esbuild for lightning-fast server builds (12KB output) +- **Ultra-small Production Image**: ~50% size reduction through aggressive optimization +- **Security hardening**: Non-root user, minimal attack surface, no unnecessary packages +- **Performance optimization**: Layer caching, build caching, memory-optimized runtime +- **Aggressive Cleanup**: Removes documentation, tests, TypeScript definitions, and cache files +- **Health checks**: Lightweight health monitoring using Node.js built-in modules +- **Signal handling**: Proper process management with graceful shutdown handlers +- **Single Port Production**: API runs on port 3000 internally, exposed as port 8080 externally +- **Memory Optimized**: Reduced Node.js memory footprint (256MB vs 1GB default) + +### Image Size Optimization + +This Dockerfile implements several strategies to minimize the final image size: + +1. **Aggressive node_modules cleanup**: Removes documentation, tests, and unnecessary files with .dockerignore file. +2. **Multi-stage optimization**: Only copies essential files to production stage +3. **Memory constraints**: Limits Node.js heap size to reduce memory usage +4. **Cache cleanup**: Removes all temporary files and caches +5. **Minimal base image**: Uses Alpine Linux for smallest possible base + +**Expected size reduction**: ~50% smaller than standard Node.js images + ### Step 3: Configure the .dockerignore file The `.dockerignore` file tells Docker which files and folders to exclude when building the image. - > [!NOTE] > This helps: > > 1.Reduce image size > 2.Speed up the build process > 3.Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. -> +> > To learn more, visit the [.dockerignore reference](/reference/dockerfile.md#dockerignore-file). -Copy and replace the contents of your existing `.dockerignore` with the configuration below: +Copy and replace the contents of your existing `.dockerignore` with the optimized configuration below: ```dockerignore -# ============================= -# Dependency directories -# ============================= +# Optimized .dockerignore for Node.js + React Todo App +# Based on actual project structure + +# Version control +.git/ +.github/ +.gitignore + +# Dependencies (installed in container) node_modules/ + +# Build outputs (built in container) dist/ -build/ - -# ============================= -# Logs & debugging -# ============================= -npm-debug.log* -yarn-debug.log* -pnpm-debug.log* -*.tsbuildinfo -*.log -# ============================= # Environment files -# ============================= -.env -.env.* -*.env - -# ============================= -# Editor / IDE configs -# ============================= +.env* + +# Development files .vscode/ -.idea/ -*.swp +*.log +coverage/ +.eslintcache -# ============================= -# OS generated files -# ============================= +# OS files .DS_Store Thumbs.db -# ============================= -# Git-related -# ============================= -.git/ -.gitignore - -# ============================= -# Docker / Compose files -# ============================= -.dockerignore -Dockerfile* -docker-compose* - -# ============================= -# Testing / Reports / Coverage -# ============================= -coverage/ -jest/ -reports/ -cypress/ -cypress/screenshots/ -cypress/videos/ - -# ============================= -# Misc build tools & cache -# ============================= -.tmp/ -.cache/ -.eslintcache -.vs/ - -# ============================= -# Optional Kubernetes/Helm -# ============================= -charts/ - -# ============================= -# Docs & meta files -# ============================= -README.md -LICENSE +# Documentation +*.md +docs/ + +# Deployment configs +compose.yml +Taskfile.yml +nodejs-sample-kubernetes.yaml + +# Non-essential configs (keep build configs) +*.config.js +!vite.config.ts +!esbuild.config.js +!tailwind.config.js +!postcss.config.js +!tsconfig.json ``` ### Step 4: Build the Node.js application image -With your custom configuration in place, you're now ready to build the Docker image for your Node.js application. - -After completing the previous steps, your project directory should now contain the following files: +After creating all the configuration files, your project directory should now contain all necessary Docker configuration files: ```text ├── docker-nodejs-sample/ │ ├── Dockerfile │ ├── .dockerignore -│ ├── compose.yaml -│ ├── nginx.conf +│ ├── compose.yml │ └── README.Docker.md ``` -Now that your Dockerfile is configured, you can build the Docker image for your Node.js application. +Now you can build the Docker image for your Node.js application. > [!NOTE] > The `docker build` command packages your application into an image using the instructions in the Dockerfile. It includes all necessary files from the current directory (called the [build context](/build/concepts/context/#what-is-a-build-context)). @@ -324,16 +563,17 @@ Now that your Dockerfile is configured, you can build the Docker image for your Run the following command from the root of your project: ```console -$ docker build --tag docker-nodejs-sample . +$ docker build --target production --tag docker-nodejs-sample . ``` What this command does: + - Uses the Dockerfile in the current directory (.) +- Targets the production stage of the multi-stage build - Packages the application and its dependencies into a Docker image - Tags the image as docker-nodejs-sample so you can reference it later - -#### Step 6: View local images +#### Step 6: View local images After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](/manuals/desktop/use-desktop/images.md). Since you're already working in the terminal, let's use the Docker CLI. @@ -347,7 +587,7 @@ Example Output: ```shell REPOSITORY TAG IMAGE ID CREATED SIZE -docker-nodejs-sample latest 423525528038 14 seconds ago 75.8MB +docker-nodejs-sample latest 423525528038 14 seconds ago 237.46MB ``` This output provides key details about your images: @@ -358,7 +598,7 @@ This output provides key details about your images: - **Created** – The timestamp indicating when the image was built. - **Size** – The total disk space used by the image. -If the build was successful, you should see `docker-nodejs-sample` image listed. +If the build was successful, you should see `docker-nodejs-sample` image listed. --- @@ -366,30 +606,39 @@ If the build was successful, you should see `docker-nodejs-sample` image listed. In the previous step, you created a Dockerfile for your Node.js application and built a Docker image using the docker build command. Now it’s time to run that image in a container and verify that your application works as expected. +Inside the `docker-nodejs-sample` directory, run the following command in a terminal. + +```console +$ docker compose up app-dev --build +``` + +The development application will start with both servers: -Inside the `docker-nodejs-sample` directory, run the following command in a -terminal. +- **API Server**: [http://localhost:3000](http://localhost:3000) - Express.js backend with REST API +- **Frontend**: [http://localhost:5173](http://localhost:5173) - Vite dev server with React frontend +- **Health Check**: [http://localhost:3000/health](http://localhost:3000/health) - Application health status + +For production deployment, you can use: ```console -$ docker compose up --build +$ docker compose up app-prod --build ``` -Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple Node.js web application. +Which serves the full-stack app at [http://localhost:8080](http://localhost:8080) with the Express server running on port 3000 internally, mapped to port 8080 externally. + +You should see a modern Todo List application with React 19 and a fully functional REST API. -Press `ctrl+c` in the terminal to stop your application. +Press `CTRL + C` in the terminal to stop your application. ### Run the application in the background -You can run the application detached from the terminal by adding the `-d` -option. Inside the `docker-nodejs-sample` directory, run the following command -in a terminal. +You can run the application detached from the terminal by adding the `-d` option. Inside the `docker-nodejs-sample` directory, run the following command in a terminal. ```console -$ docker compose up --build -d +$ docker compose up app-dev --build -d ``` -Open a browser and view the application at [http://localhost:8080](http://localhost:8080). You should see a simple web application preview. - +Open a browser and view the application at [http://localhost:3000](http://localhost:3000) (API) or [http://localhost:5173](http://localhost:5173) (frontend). You should see the Todo application running. To confirm that the container is running, use `docker ps` command: @@ -397,15 +646,26 @@ To confirm that the container is running, use `docker ps` command: $ docker ps ``` -This will list all active containers along with their ports, names, and status. Look for a container exposing port 8080. +This will list all active containers along with their ports, names, and status. Look for a container exposing ports 3000, 5173, and 9229 for the development app. Example Output: ```shell -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -88bced6ade95 docker-nodejs-sample-server "nginx -c /etc/nginx…" About a minute ago Up About a minute 0.0.0.0:8080->8080/tcp docker-nodejs-sample-server-1 +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +93f3faee32c3 docker-nodejs-sample-app-dev "docker-entrypoint.s…" 33 seconds ago Up 31 seconds 0.0.0.0:3000->3000/tcp, [::]:3000->3000/tcp, 0.0.0.0:5173->5173/tcp, [::]:5173->5173/tcp, 0.0.0.0:9230->9229/tcp, [::]:9230->9229/tcp todoapp-dev ``` +### Run different profiles + +You can run different configurations using Docker Compose profiles: + +```console +# Run production +$ docker compose up app-prod -d + +# Run tests +$ docker compose up app-test -d +``` To stop the application, run: @@ -413,7 +673,6 @@ To stop the application, run: $ docker compose down ``` - > [!NOTE] > For more information about Compose commands, see the [Compose CLI > reference](/reference/cli/docker/compose/_index.md). @@ -425,12 +684,15 @@ $ docker compose down In this guide, you learned how to containerize, build, and run a Node.js application using Docker. By following best practices, you created a secure, optimized, and production-ready setup. What you accomplished: + - Initialized your project using `docker init` to scaffold essential Docker configuration files. -- Replaced the default `Dockerfile` with a multi-stage build that compiles the Node.js application and serves the static files using Nginx. +- Created a comprehensive `compose.yml` file with development, production, and database services. +- Set up environment configuration with a `.env` file for flexible deployment settings. +- Replaced the default `Dockerfile` with a multi-stage build optimized for TypeScript and React. - Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. - Built your Docker image using `docker build`. - Ran the container using `docker compose up`, both in the foreground and in detached mode. -- Verified that the app was running by visiting [http://localhost:8080](http://localhost:8080). +- Verified that the app was running by visiting [http://localhost:8080](http://localhost:8080) (production) or [http://localhost:3000](http://localhost:3000) (development). - Learned how to stop the containerized application using `docker compose down`. You now have a fully containerized Node.js application, running in a Docker container, and ready for deployment across any environment with confidence and consistency. @@ -442,8 +704,8 @@ You now have a fully containerized Node.js application, running in a Docker cont Explore official references and best practices to sharpen your Docker workflow: - [Multi-stage builds](/build/building/multi-stage/) – Learn how to separate build and runtime stages. -- [Best practices for writing Dockerfiles](/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. -- [Build context in Docker](/build/concepts/context/) – Learn how context affects image builds. +- [Best practices for writing Dockerfiles](/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. +- [Build context in Docker](/build/concepts/context/) – Learn how context affects image builds. - [`docker init` CLI reference](/reference/cli/docker/init/) – Scaffold Docker assets automatically. - [`docker build` CLI reference](/reference/cli/docker/build/) – Build Docker images from a Dockerfile. - [`docker images` CLI reference](/reference/cli/docker/images/) – Manage and inspect local Docker images. @@ -457,4 +719,3 @@ Explore official references and best practices to sharpen your Docker workflow: With your Node.js application now containerized, you're ready to move on to the next step. In the next section, you'll learn how to develop your application using Docker containers, enabling a consistent, isolated, and reproducible development environment across any machine. - diff --git a/content/guides/nodejs/deploy.md b/content/guides/nodejs/deploy.md index 5c6ca2960b8d..cd3d0dc77100 100644 --- a/content/guides/nodejs/deploy.md +++ b/content/guides/nodejs/deploy.md @@ -1,9 +1,9 @@ --- -title: Test your Node.js deployment -linkTitle: Test your deployment +title: Deploy your Node.js application +linkTitle: Deploy your app weight: 50 -keywords: deploy, kubernetes, node, node.js -description: Learn how to deploy locally to test and debug your Kubernetes deployment +keywords: deploy, kubernetes, node, node.js, production +description: Learn how to deploy your containerized Node.js application to Kubernetes with production-ready configuration aliases: - /language/nodejs/deploy/ - /guides/language/nodejs/deploy/ @@ -13,131 +13,578 @@ aliases: - Complete all the previous sections of this guide, starting with [Containerize a Node.js application](containerize.md). - [Turn on Kubernetes](/manuals/desktop/features/kubernetes.md#install-and-turn-on-kubernetes) in Docker Desktop. +- Have your Docker image built and available (either locally or in a registry). ## Overview -In this section, you'll learn how to use Docker Desktop to deploy your -application to a fully-featured Kubernetes environment on your development -machine. This allows you to test and debug your workloads on Kubernetes locally -before deploying. +In this section, you'll learn how to deploy your containerized Node.js application to Kubernetes using Docker Desktop. This deployment uses production-ready configurations including security hardening, auto-scaling, persistent storage, and high availability features. -## Create a Kubernetes YAML file +You'll deploy a complete stack including: -In the cloned repository's directory, create a file named -`docker-node-kubernetes.yaml`. Open the file in an IDE or text editor and add -the following contents. Replace `DOCKER_USERNAME/REPO_NAME` with your Docker -username and the name of the repository that you created in [Configure CI/CD for -your Node.js application](configure-ci-cd.md). +- Your Node.js Todo application with 3 replicas +- PostgreSQL database with persistent storage +- Auto-scaling based on CPU and memory usage +- SSL/TLS ready ingress configuration +- Production security settings + +## Create a Kubernetes deployment file + +Create a production-ready Kubernetes deployment file that includes all the components needed for a scalable, secure Node.js application. + +Create a new file called `nodejs-sample-kubernetes.yaml` in your project root: ```yaml +# ======================================== +# Node.js Todo App - Kubernetes Deployment +# ======================================== + +apiVersion: v1 +kind: Namespace +metadata: + name: todoapp + labels: + app: todoapp + +--- +# ======================================== +# ConfigMap for Application Configuration +# ======================================== +apiVersion: v1 +kind: ConfigMap +metadata: + name: todoapp-config + namespace: todoapp +data: + NODE_ENV: 'production' + ALLOWED_ORIGINS: 'https://yourdomain.com' + POSTGRES_HOST: 'todoapp-postgres' + POSTGRES_PORT: '5432' + POSTGRES_DB: 'todoapp' + POSTGRES_USER: 'todoapp' + +--- +# ======================================== +# Secret for Database Credentials +# ======================================== +apiVersion: v1 +kind: Secret +metadata: + name: todoapp-secrets + namespace: todoapp +type: Opaque +data: + postgres-password: dG9kb2FwcF9wYXNzd29yZA== # base64 encoded "todoapp_password" + +--- +# ======================================== +# PostgreSQL PersistentVolumeClaim +# ======================================== +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: postgres-pvc + namespace: todoapp +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + storageClassName: standard + +--- +# ======================================== +# PostgreSQL Deployment +# ======================================== apiVersion: apps/v1 kind: Deployment metadata: - name: docker-nodejs-demo - namespace: default + name: todoapp-postgres + namespace: todoapp + labels: + app: todoapp-postgres spec: replicas: 1 selector: matchLabels: - todo: web + app: todoapp-postgres template: metadata: labels: - todo: web + app: todoapp-postgres spec: containers: - - name: todo-site - image: DOCKER_USERNAME/REPO_NAME - imagePullPolicy: Always + - name: postgres + image: postgres:16-alpine + ports: + - containerPort: 5432 + name: postgres + env: + - name: POSTGRES_DB + valueFrom: + configMapKeyRef: + name: todoapp-config + key: POSTGRES_DB + - name: POSTGRES_USER + valueFrom: + configMapKeyRef: + name: todoapp-config + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: todoapp-secrets + key: postgres-password + volumeMounts: + - name: postgres-storage + mountPath: /var/lib/postgresql/data + livenessProbe: + exec: + command: + - pg_isready + - -U + - todoapp + - -d + - todoapp + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + exec: + command: + - pg_isready + - -U + - todoapp + - -d + - todoapp + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: postgres-storage + persistentVolumeClaim: + claimName: postgres-pvc + --- +# ======================================== +# PostgreSQL Service +# ======================================== apiVersion: v1 kind: Service metadata: - name: todo-entrypoint - namespace: default + name: todoapp-postgres + namespace: todoapp + labels: + app: todoapp-postgres +spec: + type: ClusterIP + ports: + - port: 5432 + targetPort: 5432 + protocol: TCP + name: postgres + selector: + app: todoapp-postgres + +--- +# ======================================== +# Application Deployment +# ======================================== +apiVersion: apps/v1 +kind: Deployment +metadata: + name: todoapp-deployment + namespace: todoapp + labels: + app: todoapp spec: - type: NodePort + replicas: 3 selector: - todo: web + matchLabels: + app: todoapp + template: + metadata: + labels: + app: todoapp + spec: + securityContext: + runAsNonRoot: true + runAsUser: 1001 + fsGroup: 1001 + containers: + - name: todoapp + image: ghcr.io/your-username/docker-nodejs-sample:latest + imagePullPolicy: Always + ports: + - containerPort: 3000 + name: http + protocol: TCP + env: + - name: NODE_ENV + valueFrom: + configMapKeyRef: + name: todoapp-config + key: NODE_ENV + - name: ALLOWED_ORIGINS + valueFrom: + configMapKeyRef: + name: todoapp-config + key: ALLOWED_ORIGINS + - name: POSTGRES_HOST + valueFrom: + configMapKeyRef: + name: todoapp-config + key: POSTGRES_HOST + - name: POSTGRES_PORT + valueFrom: + configMapKeyRef: + name: todoapp-config + key: POSTGRES_PORT + - name: POSTGRES_DB + valueFrom: + configMapKeyRef: + name: todoapp-config + key: POSTGRES_DB + - name: POSTGRES_USER + valueFrom: + configMapKeyRef: + name: todoapp-config + key: POSTGRES_USER + - name: POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: todoapp-secrets + key: postgres-password + livenessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /health + port: 3000 + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + memory: '256Mi' + cpu: '250m' + limits: + memory: '512Mi' + cpu: '500m' + securityContext: + allowPrivilegeEscalation: false + readOnlyRootFilesystem: true + capabilities: + drop: + - ALL + +--- +# ======================================== +# Application Service +# ======================================== +apiVersion: v1 +kind: Service +metadata: + name: todoapp-service + namespace: todoapp + labels: + app: todoapp +spec: + type: ClusterIP ports: - - port: 3000 + - name: http + port: 80 targetPort: 3000 - nodePort: 30001 -``` + protocol: TCP + selector: + app: todoapp -In this Kubernetes YAML file, there are two objects, separated by the `---`: +--- +# ======================================== +# Ingress for External Access +# ======================================== +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: todoapp-ingress + namespace: todoapp + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / + cert-manager.io/cluster-issuer: 'letsencrypt-prod' +spec: + tls: + - hosts: + - yourdomain.com + secretName: todoapp-tls + rules: + - host: yourdomain.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: todoapp-service + port: + number: 80 -- A Deployment, describing a scalable group of identical pods. In this case, - you'll get just one replica, or copy of your pod. That pod, which is - described under `template`, has just one container in it. The container is - created from the image built by GitHub Actions in [Configure CI/CD for your - Node.js application](configure-ci-cd.md). -- A NodePort service, which will route traffic from port 30001 on your host to - port 3000 inside the pods it routes to, allowing you to reach your app - from the network. +--- +# ======================================== +# HorizontalPodAutoscaler +# ======================================== +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: todoapp-hpa + namespace: todoapp +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: todoapp-deployment + minReplicas: 1 + maxReplicas: 5 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: 80 -To learn more about Kubernetes objects, see the [Kubernetes documentation](https://kubernetes.io/docs/home/). +--- +# ======================================== +# PodDisruptionBudget +# ======================================== +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: todoapp-pdb + namespace: todoapp +spec: + minAvailable: 1 + selector: + matchLabels: + app: todoapp +``` -## Deploy and check your application +## Configure the deployment -1. In a terminal, navigate to where you created `docker-node-kubernetes.yaml` - and deploy your application to Kubernetes. +Before deploying, you need to customize the deployment file for your environment: - ```console - $ kubectl apply -f docker-node-kubernetes.yaml +1. **Image reference**: Replace `your-username` with your GitHub username or Docker Hub username: + + ```yaml + image: ghcr.io/your-username/docker-nodejs-sample:latest ``` - You should see output that looks like the following, indicating your Kubernetes objects were created successfully. +2. **Domain name**: Replace `yourdomain.com` with your actual domain in two places: + + ```yaml + # In ConfigMap + ALLOWED_ORIGINS: "https://yourdomain.com" - ```shell - deployment.apps/docker-nodejs-demo created - service/todo-entrypoint created + # In Ingress + - host: yourdomain.com ``` -2. Make sure everything worked by listing your deployments. +3. **Database password** (optional): The default password is already base64 encoded. To change it: - ```console - $ kubectl get deployments + ```bash + # Generate new base64 encoded password + echo -n "your-new-password" | base64 ``` - Your deployment should be listed as follows: + Then update the Secret: - ```shell - NAME READY UP-TO-DATE AVAILABLE AGE - docker-nodejs-demo 1/1 1 1 6s + ```yaml + data: + postgres-password: ``` - This indicates all one of the pods you asked for in your YAML are up and running. Do the same check for your services. +4. **Storage class**: Adjust based on your cluster (current: `standard`) + +## Understanding the deployment + +This deployment creates a complete, production-ready stack with the following components: + +### Application Architecture + +- **Node.js Application**: 3 replicas running your containerized Todo app +- **PostgreSQL Database**: Single instance with persistent 10Gi storage +- **Load Balancing**: Kubernetes services distribute traffic across app replicas +- **External Access**: Ingress controller handles SSL/TLS and routing + +### Production Features + +**Security Hardening:** + +- Non-root user execution (UID 1001) +- Read-only root filesystem +- Dropped Linux capabilities +- Kubernetes secrets for sensitive data + +**High Availability:** + +- Multiple application replicas (3) +- Pod disruption budget (minimum 1 available) +- Rolling updates with zero downtime +- Health checks on `/health` endpoint + +**Auto-Scaling:** + +- Horizontal Pod Autoscaler (1-5 replicas) +- CPU-based scaling (70% threshold) +- Memory-based scaling (80% threshold) +- Resource limits: 256Mi-512Mi memory, 250m-500m CPU + +**Data Persistence:** + +- PostgreSQL with 10Gi persistent volume +- Automatic database initialization +- Data survives pod restarts and updates + +## Deploy your application + +### Step 1: Deploy to Kubernetes + +Deploy your application to the local Kubernetes cluster: + +```console +$ kubectl apply -f nodejs-sample-kubernetes.yaml +``` + +You should see output confirming all resources were created: + +```shell +namespace/todoapp created +secret/todoapp-secrets created +configmap/todoapp-config created +persistentvolumeclaim/postgres-pvc created +deployment.apps/todoapp-postgres created +service/todoapp-postgres created +deployment.apps/todoapp-deployment created +service/todoapp-service created +ingress.networking.k8s.io/todoapp-ingress created +poddisruptionbudget.policy/todoapp-pdb created +horizontalpodautoscaler.autoscaling/todoapp-hpa created +``` + +### Step 2: Verify the deployment + +Check that your deployments are running: + +```console +$ kubectl get deployments -n todoapp +``` + +Expected output: + +```shell +NAME READY UP-TO-DATE AVAILABLE AGE +todoapp-deployment 3/3 3 3 30s +todoapp-postgres 1/1 1 1 30s +``` + +Verify your services are created: + +```console +$ kubectl get services -n todoapp +``` + +Expected output: + +```shell +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +todoapp-service ClusterIP 10.111.101.229 80/TCP 45s +todoapp-postgres ClusterIP 10.111.102.130 5432/TCP 45s +``` + +Check that persistent storage is working: + +```console +$ kubectl get pvc -n todoapp +``` + +Expected output: + +```shell +NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE +postgres-pvc Bound pvc-12345678-1234-1234-1234-123456789012 10Gi RWO standard 1m +``` + +### Step 3: Access your application + +For local testing, use port forwarding to access your application: + +```console +$ kubectl port-forward -n todoapp service/todoapp-service 8080:80 +``` + +Open your browser and visit [http://localhost:8080](http://localhost:8080) to see your Todo application running in Kubernetes. + +### Step 4: Test the deployment + +Test that your application is working correctly: + +1. **Add some todos** through the web interface +2. **Check application pods**: ```console - $ kubectl get services + $ kubectl get pods -n todoapp -l app=todoapp ``` - You should get output like the following. +3. **View application logs**: - ```shell - NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE - kubernetes ClusterIP 10.96.0.1 443/TCP 7d22h - todo-entrypoint NodePort 10.111.101.229 3000:30001/TCP 33s + ```console + $ kubectl logs -f deployment/todoapp-deployment -n todoapp ``` - In addition to the default `kubernetes` service, you can see your `todo-entrypoint` service, accepting traffic on port 30001/TCP. - -3. Open a browser and visit your app at `localhost:30001`. You should see your - application. +4. **Check database connectivity**: -4. Run the following command to tear down your application. + ```console + $ kubectl get pods -n todoapp -l app=todoapp-postgres + ``` +5. **Monitor auto-scaling**: ```console - $ kubectl delete -f docker-node-kubernetes.yaml + $ kubectl describe hpa todoapp-hpa -n todoapp ``` +### Step 5: Clean up + +When you're done testing, remove the deployment: + +```console +$ kubectl delete -f nodejs-sample-kubernetes.yaml +``` + ## Summary -In this section, you learned how to use Docker Desktop to deploy your application to a fully-featured Kubernetes environment on your development machine. +In this section, you successfully deployed your containerized Node.js application to Kubernetes with a production-ready configuration. You learned how to: + +- Create a comprehensive Kubernetes deployment file with security hardening +- Deploy a multi-tier application (Node.js + PostgreSQL) with persistent storage +- Configure auto-scaling, health checks, and high availability features +- Test and monitor your deployment locally using Docker Desktop's Kubernetes + +Your application is now running in a production-like environment with enterprise-grade features including security contexts, resource management, and automatic scaling. + +--- + +## Related resources -Related information: +Explore official references and best practices to sharpen your Kubernetes deployment workflow: -- [Kubernetes documentation](https://kubernetes.io/docs/home/) -- [Deploy on Kubernetes with Docker Desktop](/manuals/desktop/features/kubernetes.md) -- [Swarm mode overview](/manuals/engine/swarm/_index.md) +- [Kubernetes documentation](https://kubernetes.io/docs/home/) – Learn about core concepts, workloads, services, and more. +- [Deploy on Kubernetes with Docker Desktop](/manuals/desktop/features/kubernetes.md) – Use Docker Desktop’s built-in Kubernetes support for local testing and development. +- [`kubectl` CLI reference](https://kubernetes.io/docs/reference/kubectl/) – Manage Kubernetes clusters from the command line. +- [Kubernetes Deployment resource](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) – Understand how to manage and scale applications using Deployments. +- [Kubernetes Service resource](https://kubernetes.io/docs/concepts/services-networking/service/) – Learn how to expose your application to internal and external traffic. diff --git a/content/guides/nodejs/develop.md b/content/guides/nodejs/develop.md index 0e892375182c..6f473f3963d1 100644 --- a/content/guides/nodejs/develop.md +++ b/content/guides/nodejs/develop.md @@ -1,7 +1,7 @@ --- title: Use containers for Node.js development linkTitle: Develop your app -weight: 20 +weight: 30 keywords: node, node.js, development description: Learn how to develop your Node.js application locally using containers. aliases: @@ -24,253 +24,193 @@ In this section, you'll learn how to set up a development environment for your c ## Add a local database and persist data -You can use containers to set up local services, like a database. In this section, you'll update the `compose.yaml` file to define a database service and a volume to persist data. - -1. Open your `compose.yaml` file in an IDE or text editor. -2. Uncomment the database related instructions. The following is the updated - `compose.yaml` file. - - > [!IMPORTANT] - > - > For this section, don't run `docker compose up` until you are instructed to. Running the command at intermediate points may incorrectly initialize your database. - - ```yaml {hl_lines="26-51",collapse=true,title=compose.yaml} - # Comments are provided throughout this file to help you get started. - # If you need more help, visit the Docker Compose reference guide at - # https://docs.docker.com/go/compose-spec-reference/ - - # Here the instructions define your application as a service called "server". - # This service is built from the Dockerfile in the current directory. - # You can add other services your application may depend on here, such as a - # database or a cache. For examples, see the Awesome Compose repository: - # https://github.com/docker/awesome-compose - services: - server: - build: - context: . - environment: - NODE_ENV: production - ports: - - 3000:3000 - - # The commented out section below is an example of how to define a PostgreSQL - # database that your application can use. `depends_on` tells Docker Compose to - # start the database before your application. The `db-data` volume persists the - # database data between container restarts. The `db-password` secret is used - # to set the database password. You must create `db/password.txt` and add - # a password of your choosing to it before running `docker compose up`. - - depends_on: - db: - condition: service_healthy - db: - image: postgres - restart: always - user: postgres - secrets: - - db-password - volumes: - - db-data:/var/lib/postgresql/data - environment: - - POSTGRES_DB=example - - POSTGRES_PASSWORD_FILE=/run/secrets/db-password - expose: - - 5432 - healthcheck: - test: ["CMD", "pg_isready"] - interval: 10s - timeout: 5s - retries: 5 - volumes: - db-data: - secrets: - db-password: - file: db/password.txt - ``` +The application uses PostgreSQL for data persistence, providing a production-ready database solution. You'll need to add a database service to your Docker Compose configuration. - > [!NOTE] - > - > To learn more about the instructions in the Compose file, see [Compose file - > reference](/reference/compose-file/). - -3. Open `src/persistence/postgres.js` in an IDE or text editor. You'll notice - that this application uses a Postgres database and requires some environment - variables in order to connect to the database. The `compose.yaml` file doesn't - have these variables defined yet. -4. Add the environment variables that specify the database configuration. The - following is the updated `compose.yaml` file. - - ```yaml {hl_lines="16-19",collapse=true,title=compose.yaml} - # Comments are provided throughout this file to help you get started. - # If you need more help, visit the Docker Compose reference guide at - # https://docs.docker.com/go/compose-spec-reference/ - - # Here the instructions define your application as a service called "server". - # This service is built from the Dockerfile in the current directory. - # You can add other services your application may depend on here, such as a - # database or a cache. For examples, see the Awesome Compose repository: - # https://github.com/docker/awesome-compose - services: - server: - build: - context: . - environment: - NODE_ENV: production - POSTGRES_HOST: db - POSTGRES_USER: postgres - POSTGRES_PASSWORD_FILE: /run/secrets/db-password - POSTGRES_DB: example - ports: - - 3000:3000 - - # The commented out section below is an example of how to define a PostgreSQL - # database that your application can use. `depends_on` tells Docker Compose to - # start the database before your application. The `db-data` volume persists the - # database data between container restarts. The `db-password` secret is used - # to set the database password. You must create `db/password.txt` and add - # a password of your choosing to it before running `docker compose up`. - - depends_on: - db: - condition: service_healthy - db: - image: postgres - restart: always - user: postgres - secrets: - - db-password - volumes: - - db-data:/var/lib/postgresql/data - environment: - - POSTGRES_DB=example - - POSTGRES_PASSWORD_FILE=/run/secrets/db-password - expose: - - 5432 - healthcheck: - test: ["CMD", "pg_isready"] - interval: 10s - timeout: 5s - retries: 5 - volumes: - db-data: - secrets: - db-password: - file: db/password.txt - ``` +### Add database service to Docker Compose + +If you haven't already created a `compose.yml` file in the previous section, or if you need to add the database service, update your `compose.yml` file to include the PostgreSQL database service: + +```yaml +services: + # ... existing app services ... + + # ======================================== + # PostgreSQL Database Service + # ======================================== + db: + image: postgres:16-alpine + container_name: todoapp-db + environment: + POSTGRES_DB: '${POSTGRES_DB:-todoapp}' + POSTGRES_USER: '${POSTGRES_USER:-todoapp}' + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - '${DB_PORT:-5432}:5432' + restart: unless-stopped + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 5s + networks: + - todoapp-network + +# ======================================== +# Volume Configuration +# ======================================== +volumes: + postgres_data: + name: todoapp-postgres-data + driver: local + +# ======================================== +# Network Configuration +# ======================================== +networks: + todoapp-network: + name: todoapp-network + driver: bridge +``` -5. Add the `secrets` section under the `server` service so that your application securely handles the database password. The following is the updated `compose.yaml` file. - - ```yaml {hl_lines="33-34",collapse=true,title=compose.yaml} - # Comments are provided throughout this file to help you get started. - # If you need more help, visit the Docker Compose reference guide at - # https://docs.docker.com/go/compose-spec-reference/ - - # Here the instructions define your application as a service called "server". - # This service is built from the Dockerfile in the current directory. - # You can add other services your application may depend on here, such as a - # database or a cache. For examples, see the Awesome Compose repository: - # https://github.com/docker/awesome-compose - services: - server: - build: - context: . - environment: - NODE_ENV: production - POSTGRES_HOST: db - POSTGRES_USER: postgres - POSTGRES_PASSWORD_FILE: /run/secrets/db-password - POSTGRES_DB: example - ports: - - 3000:3000 - - # The commented out section below is an example of how to define a PostgreSQL - # database that your application can use. `depends_on` tells Docker Compose to - # start the database before your application. The `db-data` volume persists the - # database data between container restarts. The `db-password` secret is used - # to set the database password. You must create `db/password.txt` and add - # a password of your choosing to it before running `docker compose up`. - - depends_on: - db: - condition: service_healthy - secrets: - - db-password - db: - image: postgres - restart: always - user: postgres - secrets: - - db-password - volumes: - - db-data:/var/lib/postgresql/data - environment: - - POSTGRES_DB=example - - POSTGRES_PASSWORD_FILE=/run/secrets/db-password - expose: - - 5432 - healthcheck: - test: ["CMD", "pg_isready"] - interval: 10s - timeout: 5s - retries: 5 - volumes: - db-data: - secrets: - db-password: - file: db/password.txt +### Update your application service + +Make sure your application service in `compose.yml` is configured to connect to the database: + +```yaml {hl_lines="18-20,42-44",collapse=true,title=compose.yml} +services: + app-dev: + build: + context: . + dockerfile: Dockerfile + target: development + container_name: todoapp-dev + ports: + - '${APP_PORT:-3000}:3000' # API server + - '${VITE_PORT:-5173}:5173' # Vite dev server + - '${DEBUG_PORT:-9229}:9229' # Node.js debugger + environment: + NODE_ENV: development + DOCKER_ENV: 'true' + POSTGRES_HOST: db + POSTGRES_PORT: 5432 + POSTGRES_DB: todoapp + POSTGRES_USER: todoapp + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}' + volumes: + - ./src:/app/src:ro + - ./package.json:/app/package.json + - ./vite.config.ts:/app/vite.config.ts:ro + - ./tailwind.config.js:/app/tailwind.config.js:ro + - ./postcss.config.js:/app/postcss.config.js:ro + depends_on: + db: + condition: service_healthy + develop: + watch: + - action: sync + path: ./src + target: /app/src + ignore: + - '**/*.test.*' + - '**/__tests__/**' + - action: rebuild + path: ./package.json + - action: sync + path: ./vite.config.ts + target: /app/vite.config.ts + - action: sync + path: ./tailwind.config.js + target: /app/tailwind.config.js + - action: sync + path: ./postcss.config.js + target: /app/postcss.config.js + restart: unless-stopped + networks: + - todoapp-network + + db: + image: postgres:16-alpine + container_name: todoapp-db + environment: + POSTGRES_DB: '${POSTGRES_DB:-todoapp}' + POSTGRES_USER: '${POSTGRES_USER:-todoapp}' + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + volumes: + - postgres_data:/var/lib/postgresql/data + ports: + - '${DB_PORT:-5432}:5432' + restart: unless-stopped + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER:-todoapp} -d ${POSTGRES_DB:-todoapp}'] + interval: 10s + timeout: 5s + retries: 5 + start_period: 5s + networks: + - todoapp-network + +volumes: + postgres_data: + name: todoapp-postgres-data + driver: local + +networks: + todoapp-network: + name: todoapp-network + driver: bridge +``` + +1. The PostgreSQL database configuration is handled automatically by the application. The database is created and initialized when the application starts, with data persisted using the `postgres_data` volume. + +2. Configure your environment by copying the example file: + + ```console + $ cp .env.example .env ``` -6. In the `docker-nodejs-sample` directory, create a directory named `db`. -7. In the `db` directory, create a file named `password.txt`. This file will - contain your database password. - - You should now have at least the following contents in your - `docker-nodejs-sample` directory. - - ```text - ├── docker-nodejs-sample/ - │ ├── db/ - │ │ └── password.txt - │ ├── spec/ - │ ├── src/ - │ ├── .dockerignore - │ ├── .gitignore - │ ├── compose.yaml - │ ├── Dockerfile - │ ├── package-lock.json - │ ├── package.json - │ └── README.md + Update the `.env` file with your preferred settings: + + ```env + # Application Configuration + NODE_ENV=development + APP_PORT=3000 + VITE_PORT=5173 + DEBUG_PORT=9230 + + # Database Configuration + POSTGRES_HOST=db + POSTGRES_PORT=5432 + POSTGRES_DB=todoapp + POSTGRES_USER=todoapp + POSTGRES_PASSWORD=todoapp_password + + # Security Configuration + ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 ``` -8. Open the `password.txt` file in an IDE or text editor, and specify a password - of your choice. Your password must be on a single line with no additional - lines. Ensure that the file doesn't contain any newline characters or other - hidden characters. -9. Ensure that you save your changes to all the files that you have modified. -10. Run the following command to start your application. +3. Run the following command to start your application in development mode: - ```console - $ docker compose up --build - ``` + ```console + $ docker compose up app-dev --build + ``` + +4. Open a browser and verify that the application is running at [http://localhost:5173](http://localhost:5173) for the frontend or [http://localhost:3000](http://localhost:3000) for the API. The React frontend is served by Vite dev server on port 5173, with API calls proxied to the Express server on port 3000. -11. Open a browser and verify that the application is running at - [http://localhost:3000](http://localhost:3000). -12. Add some items to the todo list to test data persistence. -13. After adding some items to the todo list, press `ctrl+c` in the terminal to - stop your application. -14. In the terminal, run `docker compose rm` to remove your containers. +5. Add some items to the todo list to test data persistence. - ```console - $ docker compose rm - ``` +6. After adding some items to the todo list, press `CTRL + C` in the terminal to stop your application. -15. Run `docker compose up` to run your application again. +7. Run the application again: - ```console - $ docker compose up --build - ``` + ```console + $ docker compose up app-dev + ``` -16. Refresh [http://localhost:3000](http://localhost:3000) in your browser and verify that the todo items persisted, even after the containers were removed and ran again. +8. Refresh [http://localhost:5173](http://localhost:5173) in your browser and verify that the todo items persisted, even after the containers were removed and ran again. ## Configure and run a development container @@ -280,139 +220,302 @@ In addition to adding a bind mount, you can configure your Dockerfile and `compo ### Update your Dockerfile for development -Open the Dockerfile in an IDE or text editor. Note that the Dockerfile doesn't -install development dependencies and doesn't run nodemon. You'll -need to update your Dockerfile to install the development dependencies and run -nodemon. - -Rather than creating one Dockerfile for production, and another Dockerfile for -development, you can use one multi-stage Dockerfile for both. +Your Dockerfile should be configured as a multi-stage build with separate stages for development, production, and testing. If you followed the previous section, your Dockerfile already includes a development stage that has all development dependencies and runs the application with hot reload enabled. -Update your Dockerfile to the following multi-stage Dockerfile. +Here's the development stage from your multi-stage Dockerfile: ```dockerfile {hl_lines="5-26",collapse=true,title=Dockerfile} -# syntax=docker/dockerfile:1 +# ======================================== +# Development Stage +# ======================================== +FROM build-deps AS development -ARG NODE_VERSION=18.0.0 +# Set environment +ENV NODE_ENV=development \ + NPM_CONFIG_LOGLEVEL=warn -FROM node:${NODE_VERSION}-alpine as base -WORKDIR /usr/src/app -EXPOSE 3000 - -FROM base as dev -RUN --mount=type=bind,source=package.json,target=package.json \ - --mount=type=bind,source=package-lock.json,target=package-lock.json \ - --mount=type=cache,target=/root/.npm \ - npm ci --include=dev -USER node -COPY . . -CMD npm run dev - -FROM base as prod -RUN --mount=type=bind,source=package.json,target=package.json \ - --mount=type=bind,source=package-lock.json,target=package-lock.json \ - --mount=type=cache,target=/root/.npm \ - npm ci --omit=dev -USER node +# Copy source files COPY . . -CMD node src/index.js -``` -In the Dockerfile, you first add a label `as base` to the `FROM -node:${NODE_VERSION}-alpine` statement. This lets you refer to this build stage -in other build stages. Next, you add a new build stage labeled `dev` to install -your development dependencies and start the container using `npm run dev`. -Finally, you add a stage labeled `prod` that omits the dev dependencies and runs -your application using `node src/index.js`. To learn more about multi-stage -builds, see [Multi-stage builds](/manuals/build/building/multi-stage.md). +# Ensure all directories have proper permissions +RUN mkdir -p /app/node_modules/.vite && \ + chown -R nodejs:nodejs /app && \ + chmod -R 755 /app -Next, you'll need to update your Compose file to use the new stage. +# Switch to non-root user +USER nodejs -### Update your Compose file for development +# Expose ports +EXPOSE 3000 5173 9229 + +# Start development server +CMD ["npm", "run", "dev:docker"] +``` + +The development stage: -To run the `dev` stage with Compose, you need to update your `compose.yaml` -file. Open your `compose.yaml` file in an IDE or text editor, and then add the -`target: dev` instruction to target the `dev` stage from your multi-stage -Dockerfile. +- Installs all dependencies including dev dependencies +- Exposes ports for the API server (3000), Vite dev server (5173), and Node.js debugger (9229) +- Runs `npm run dev` which starts both the Express server and Vite dev server concurrently +- Includes health checks for monitoring container status -Also, add a new volume to the server service for the bind mount. For this application, you'll mount `./src` from your local machine to `/usr/src/app/src` in the container. +Next, you'll need to update your Compose file to use the new stage. -Lastly, publish port `9229` for debugging. +### Update your Compose file for development -The following is the updated Compose file. All comments have been removed. +Now you need to configure your `compose.yml` file to run the development stage with comprehensive bind mounts for hot reloading. Update your development service configuration: -```yaml {hl_lines=[5,8,20,21],collapse=true,title=compose.yaml} +```yaml {hl_lines=[5,8-10,20-27],collapse=true,title=compose.yml} services: - server: + app-dev: build: context: . - target: dev + dockerfile: Dockerfile + target: development + container_name: todoapp-dev ports: - - 3000:3000 - - 9229:9229 + - '${APP_PORT:-3000}:3000' # API server + - '${VITE_PORT:-5173}:5173' # Vite dev server + - '${DEBUG_PORT:-9229}:9229' # Node.js debugger environment: - NODE_ENV: production + NODE_ENV: development + DOCKER_ENV: 'true' POSTGRES_HOST: db - POSTGRES_USER: postgres - POSTGRES_PASSWORD_FILE: /run/secrets/db-password - POSTGRES_DB: example + POSTGRES_PORT: 5432 + POSTGRES_DB: todoapp + POSTGRES_USER: todoapp + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + ALLOWED_ORIGINS: '${ALLOWED_ORIGINS:-http://localhost:3000,http://localhost:5173}' + volumes: + - ./src:/app/src:ro + - ./package.json:/app/package.json + - ./vite.config.ts:/app/vite.config.ts:ro + - ./tailwind.config.js:/app/tailwind.config.js:ro + - ./postcss.config.js:/app/postcss.config.js:ro depends_on: db: condition: service_healthy - secrets: - - db-password - volumes: - - ./src:/usr/src/app/src - db: - image: postgres - restart: always - user: postgres - secrets: - - db-password - volumes: - - db-data:/var/lib/postgresql/data - environment: - - POSTGRES_DB=example - - POSTGRES_PASSWORD_FILE=/run/secrets/db-password - expose: - - 5432 - healthcheck: - test: ["CMD", "pg_isready"] - interval: 10s - timeout: 5s - retries: 5 -volumes: - db-data: -secrets: - db-password: - file: db/password.txt + develop: + watch: + - action: sync + path: ./src + target: /app/src + ignore: + - '**/*.test.*' + - '**/__tests__/**' + - action: rebuild + path: ./package.json + - action: sync + path: ./vite.config.ts + target: /app/vite.config.ts + - action: sync + path: ./tailwind.config.js + target: /app/tailwind.config.js + - action: sync + path: ./postcss.config.js + target: /app/postcss.config.js + restart: unless-stopped + networks: + - todoapp-network ``` +Key features of the development configuration: + +- **Multi-port exposure**: API server (3000), Vite dev server (5173), and debugger (9229) +- **Comprehensive bind mounts**: Source code, configuration files, and package files for hot reloading +- **Environment variables**: Configurable through `.env` file or defaults +- **PostgreSQL database**: Production-ready database with persistent storage +- **Docker Compose watch**: Automatic file synchronization and container rebuilds +- **Health checks**: Database health monitoring with automatic dependency management + ### Run your development container and debug your application -Run the following command to run your application with the new changes to the `Dockerfile` and `compose.yaml` file. +Run the following command to run your application with the development configuration: ```console -$ docker compose up --build +$ docker compose up app-dev --build ``` -Open a browser and verify that the application is running at [http://localhost:3000](http://localhost:3000). +Or with file watching for automatic updates: + +```console +$ docker compose up app-dev --watch +``` + +For local development without Docker: + +```console +$ npm run dev:with-db +``` + +Or start services separately: + +```console +$ npm run db:start # Start PostgreSQL container +$ npm run dev # Start both server and client +``` -Any changes to the application's source files on your local machine will now be -immediately reflected in the running container. +### Using Task Runner (Alternative) -Open `docker-nodejs-sample/src/static/js/app.js` in an IDE or text editor and update the button text on line 109 from `Add Item` to `Add`. +The project includes a comprehensive Taskfile.yml for advanced workflows: + +```console +# Development +$ task dev # Start development environment +$ task dev:build # Build development image +$ task dev:run # Run development container + +# Production +$ task build # Build production image +$ task run # Run production container +$ task build-run # Build and run in one step + +# Testing +$ task test # Run all tests +$ task test:unit # Run unit tests with coverage +$ task test:lint # Run linting + +# Kubernetes +$ task k8s:deploy # Deploy to Kubernetes +$ task k8s:status # Check deployment status +$ task k8s:logs # View pod logs + +# Utilities +$ task clean # Clean up containers and images +$ task health # Check application health +$ task logs # View container logs +``` + +The application will start with both the Express API server and Vite development server: + +- **API Server**: [http://localhost:3000](http://localhost:3000) - Express.js backend with REST API +- **Frontend**: [http://localhost:5173](http://localhost:5173) - Vite dev server with hot module replacement +- **Health Check**: [http://localhost:3000/health](http://localhost:3000/health) - Application health status + +Any changes to the application's source files on your local machine will now be immediately reflected in the running container thanks to the bind mounts. + +Try making a change to test hot reloading: + +1. Open `src/client/components/TodoApp.tsx` in an IDE or text editor +2. Update the main heading text: ```diff -+ {submitting ? 'Adding...' : 'Add'} -- {submitting ? 'Adding...' : 'Add Item'} +-

+- Modern Todo App +-

++

++ My Todo App ++

+``` + +1. Save the file and the Vite dev server will automatically reload the page with your changes + +**Debugging Support:** + +You can connect a debugger to your application on port 9229. The Node.js inspector is enabled with `--inspect=0.0.0.0:9230` in the development script (`dev:server`). + +### VS Code Debugger Setup + +1. **Create a launch configuration** in `.vscode/launch.json`: + +```json +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Docker Container", + "type": "node", + "request": "attach", + "port": 9229, + "address": "localhost", + "localRoot": "${workspaceFolder}", + "remoteRoot": "/app", + "protocol": "inspector", + "restart": true, + "sourceMaps": true, + "skipFiles": ["/**"] + } + ] +} +``` + +1. **Start your development container**: + +```console +docker compose up app-dev --build +``` + +1. **Attach the debugger**: + - Open VS Code + - Go to the Debug panel (Ctrl/Cmd + Shift + D) + - Select "Attach to Docker Container" from the dropdown + - Click the green play button or press F5 + +### Chrome DevTools (Alternative) + +You can also use Chrome DevTools for debugging: + +1. **Start your container** (if not already running): + +```console +docker compose up app-dev --build +``` + +1. **Open Chrome** and navigate to: + +```text +chrome://inspect +``` + +1. **Click "Configure"** and add: + +```text +localhost:9229 +``` + +1. **Click "inspect"** under your Node.js target when it appears + +### Debugging Configuration Details + +The debugger configuration: + +- **Container port**: 9230 (internal debugger port) +- **Host port**: 9229 (mapped external port) +- **Script**: `tsx watch --inspect=0.0.0.0:9230 src/server/index.ts` + +The debugger listens on all interfaces (`0.0.0.0`) inside the container on port 9230 and is accessible on port 9229 from your host machine. + +### Troubleshooting Debugger Connection + +If the debugger doesn't connect: + +1. **Check if the container is running**: + +```console +docker ps +``` + +1. **Check if the port is exposed**: + +```console +docker port todoapp-dev +``` + +1. **Check container logs**: + +```console +docker compose logs app-dev +``` + +You should see a message like: + +```text +Debugger listening on ws://0.0.0.0:9230/... ``` -Refresh [http://localhost:3000](http://localhost:3000) in your browser and verify that the updated text appears. +Now you can set breakpoints in your TypeScript source files and debug your containerized Node.js application! -You can now connect an inspector client to your application for debugging. For -more details about inspector clients, see the [Node.js -documentation](https://nodejs.org/en/docs/guides/debugging-getting-started). +For more details about Node.js debugging, see the [Node.js documentation](https://nodejs.org/en/docs/guides/debugging-getting-started). ## Summary diff --git a/content/guides/nodejs/run-tests.md b/content/guides/nodejs/run-tests.md index b59072f9becf..658c4552e028 100644 --- a/content/guides/nodejs/run-tests.md +++ b/content/guides/nodejs/run-tests.md @@ -22,152 +22,209 @@ tests in Docker when developing and when building. ## Run tests when developing locally -The sample application already has the Jest package for running tests and has tests inside the `spec` directory. When developing locally, you can use Compose to run your tests. +The sample application uses Vitest for testing with comprehensive test coverage across client and server components. The test suite includes 101 passing tests covering React components, custom hooks, API routes, database operations, and utility functions. -Run the following command to run the test script from the `package.json` file inside a container. +### Run tests locally (without Docker) ```console -$ docker compose run server npm run test +$ npm run test ``` -To learn more about the command, see [docker compose run](/reference/cli/docker/compose/run/). +### Add test service to Docker Compose + +To run tests in a containerized environment, you need to add a dedicated test service to your `compose.yml` file. Add the following service configuration: + +```yaml +services: + # ... existing services ... + + # ======================================== + # Test Service + # ======================================== + app-test: + build: + context: . + dockerfile: Dockerfile + target: test + container_name: todoapp-test + environment: + NODE_ENV: test + POSTGRES_HOST: db + POSTGRES_PORT: 5432 + POSTGRES_DB: todoapp_test + POSTGRES_USER: todoapp + POSTGRES_PASSWORD: '${POSTGRES_PASSWORD:-todoapp_password}' + depends_on: + db: + condition: service_healthy + command: ['npm', 'run', 'test:coverage'] + networks: + - todoapp-network + profiles: + - test +``` + +This test service configuration: + +- **Builds from test stage**: Uses the `test` target from your multi-stage Dockerfile +- **Isolated test database**: Uses a separate `todoapp_test` database for testing +- **Profile-based**: Uses the `test` profile so it only runs when explicitly requested +- **Health dependency**: Waits for the database to be healthy before starting tests + +### Run tests in a container + +You can run tests using the dedicated test service: + +```console +$ docker compose up app-test --build +``` + +Or run tests against the development service: + +```console +$ docker compose run --rm app-dev npm run test +``` + +For a one-off test run with coverage: + +```console +$ docker compose run --rm app-dev npm run test:coverage +``` + +### Run tests with coverage -You should see output like the following. +To generate a coverage report: ```console -> docker-nodejs@1.0.0 test -> jest +$ npm run test:coverage +``` - PASS spec/routes/deleteItem.spec.js - PASS spec/routes/getItems.spec.js - PASS spec/routes/addItem.spec.js - PASS spec/routes/updateItem.spec.js - PASS spec/persistence/sqlite.spec.js - ● Console +You should see output like the following: - console.log - Using sqlite database at /tmp/todo.db +```console +> docker-nodejs-sample@1.0.0 test +> vitest --run + + ✓ src/server/__tests__/routes/todos.test.ts (5 tests) 16ms + ✓ src/shared/utils/__tests__/validation.test.ts (15 tests) 6ms + ✓ src/client/components/__tests__/LoadingSpinner.test.tsx (8 tests) 67ms + ✓ src/server/database/__tests__/postgres.test.ts (13 tests) 136ms + ✓ src/client/components/__tests__/ErrorMessage.test.tsx (8 tests) 127ms + ✓ src/client/components/__tests__/TodoList.test.tsx (8 tests) 147ms + ✓ src/client/components/__tests__/TodoItem.test.tsx (8 tests) 218ms + ✓ src/client/__tests__/App.test.tsx (13 tests) 259ms + ✓ src/client/components/__tests__/AddTodoForm.test.tsx (12 tests) 323ms + ✓ src/client/hooks/__tests__/useTodos.test.ts (11 tests) 569ms + + Test Files 10 passed (10) + Tests 101 passed (101) + Start at 15:32:56 + Duration 1.98s (transform 456ms, setup 1.26s, collect 1.74s, tests 1.87s, environment 5.82s, prepare 916ms) +``` - at Database.log (src/persistence/sqlite.js:18:25) +### Test Structure - console.log - Using sqlite database at /tmp/todo.db +The test suite covers: - at Database.log (src/persistence/sqlite.js:18:25) +- **Client Components** (`src/client/components/__tests__/`): React component testing with React Testing Library +- **Custom Hooks** (`src/client/hooks/__tests__/`): React hooks testing with proper mocking +- **Server Routes** (`src/server/__tests__/routes/`): API endpoint testing with Supertest +- **Database Layer** (`src/server/database/__tests__/`): PostgreSQL database operations testing +- **Utility Functions** (`src/shared/utils/__tests__/`): Validation and helper function testing +- **Integration Tests** (`src/client/__tests__/`): Full application integration testing + +## Run tests when building - console.log - Using sqlite database at /tmp/todo.db +To run tests during the Docker build process, you need to add a dedicated test stage to your Dockerfile. If you haven't already added this stage, add the following to your multi-stage Dockerfile: - at Database.log (src/persistence/sqlite.js:18:25) +```dockerfile +# ======================================== +# Test Stage +# ======================================== +FROM build-deps AS test - console.log - Using sqlite database at /tmp/todo.db +# Set environment +ENV NODE_ENV=test \ + CI=true + +# Copy source files +COPY --chown=nodejs:nodejs . . + +# Switch to non-root user +USER nodejs + +# Run tests with coverage +CMD ["npm", "run", "test:coverage"] +``` - at Database.log (src/persistence/sqlite.js:18:25) +This test stage: - console.log - Using sqlite database at /tmp/todo.db +- **Test environment**: Sets `NODE_ENV=test` and `CI=true` for proper test execution +- **Non-root user**: Runs tests as the `nodejs` user for security +- **Flexible execution**: Uses `CMD` instead of `RUN` to allow running tests during build or as a separate container +- **Coverage support**: Configured to run tests with coverage reporting - at Database.log (src/persistence/sqlite.js:18:25) +### Build and run tests during image build +To build an image that runs tests during the build process, you can create a custom Dockerfile or modify the existing one temporarily: -Test Suites: 5 passed, 5 total -Tests: 9 passed, 9 total -Snapshots: 0 total -Time: 2.008 s -Ran all test suites. +```console +$ docker build --target test -t node-docker-image-test . ``` -## Run tests when building +### Run tests in a dedicated test container + +The recommended approach is to use the test service defined in `compose.yml`: + +```console +$ docker compose --profile test up app-test --build +``` -To run your tests when building, you need to update your Dockerfile to add a new test stage. - -The following is the updated Dockerfile. - -```dockerfile {hl_lines="27-35"} -# syntax=docker/dockerfile:1 - -ARG NODE_VERSION=18.0.0 - -FROM node:${NODE_VERSION}-alpine as base -WORKDIR /usr/src/app -EXPOSE 3000 - -FROM base as dev -RUN --mount=type=bind,source=package.json,target=package.json \ - --mount=type=bind,source=package-lock.json,target=package-lock.json \ - --mount=type=cache,target=/root/.npm \ - npm ci --include=dev -USER node -COPY . . -CMD npm run dev - -FROM base as prod -RUN --mount=type=bind,source=package.json,target=package.json \ - --mount=type=bind,source=package-lock.json,target=package-lock.json \ - --mount=type=cache,target=/root/.npm \ - npm ci --omit=dev -USER node -COPY . . -CMD node src/index.js - -FROM base as test -ENV NODE_ENV test -RUN --mount=type=bind,source=package.json,target=package.json \ - --mount=type=bind,source=package-lock.json,target=package-lock.json \ - --mount=type=cache,target=/root/.npm \ - npm ci --include=dev -USER node -COPY . . -RUN npm run test +Or run it as a one-off container: + +```console +$ docker compose run --rm app-test ``` -Instead of using `CMD` in the test stage, use `RUN` to run the tests. The reason is that the `CMD` instruction runs when the container runs, and the `RUN` instruction runs when the image is being built and the build will fail if the tests fail. +### Run tests with coverage in CI/CD -Run the following command to build a new image using the test stage as the target and view the test results. Include `--progress=plain` to view the build output, `--no-cache` to ensure the tests always run, and `--target test` to target the test stage. +For continuous integration, you can run tests with coverage: ```console -$ docker build -t node-docker-image-test --progress=plain --no-cache --target test . +$ docker build --target test --progress=plain --no-cache -t test-image . +$ docker run --rm test-image npm run test:coverage ``` -You should see output containing the following. +You should see output containing the following: ```console -... - -#11 [test 3/3] RUN npm run test -#11 1.058 -#11 1.058 > docker-nodejs@1.0.0 test -#11 1.058 > jest -#11 1.058 -#11 3.765 PASS spec/routes/getItems.spec.js -#11 3.767 PASS spec/routes/deleteItem.spec.js -#11 3.783 PASS spec/routes/updateItem.spec.js -#11 3.806 PASS spec/routes/addItem.spec.js -#11 4.179 PASS spec/persistence/sqlite.spec.js -#11 4.207 -#11 4.208 Test Suites: 5 passed, 5 total -#11 4.208 Tests: 9 passed, 9 total -#11 4.208 Snapshots: 0 total -#11 4.208 Time: 2.168 s -#11 4.208 Ran all test suites. -#11 4.265 npm notice -#11 4.265 npm notice New major version of npm available! 8.6.0 -> 9.8.1 -#11 4.265 npm notice Changelog: -#11 4.265 npm notice Run `npm install -g npm@9.8.1` to update! -#11 4.266 npm notice -#11 DONE 4.3s - -... + ✓ src/server/__tests__/routes/todos.test.ts (5 tests) 16ms + ✓ src/shared/utils/__tests__/validation.test.ts (15 tests) 6ms + ✓ src/client/components/__tests__/LoadingSpinner.test.tsx (8 tests) 67ms + ✓ src/server/database/__tests__/postgres.test.ts (13 tests) 136ms + ✓ src/client/components/__tests__/ErrorMessage.test.tsx (8 tests) 127ms + ✓ src/client/components/__tests__/TodoList.test.tsx (8 tests) 147ms + ✓ src/client/components/__tests__/TodoItem.test.tsx (8 tests) 218ms + ✓ src/client/__tests__/App.test.tsx (13 tests) 259ms + ✓ src/client/components/__tests__/AddTodoForm.test.tsx (12 tests) 323ms + ✓ src/client/hooks/__tests__/useTodos.test.ts (11 tests) 569ms + + Test Files 10 passed (10) + Tests 101 passed (101) + Start at 07:33:25 + Duration 2.11s (transform 339ms, setup 619ms, collect 1.12s, tests 1.43s, environment 3.52s, prepare 901ms) ``` ## Summary -In this section, you learned how to run tests when developing locally using Compose and how to run tests when building your image. +In this section, you learned how to run tests when developing locally using Docker Compose and how to run tests when building your image. Related information: -- [docker compose run](/reference/cli/docker/compose/run/) +- [Dockerfile reference](/reference/dockerfile/) – Understand all Dockerfile instructions and syntax. +- [Best practices for writing Dockerfiles](/develop/develop-images/dockerfile_best-practices/) – Write efficient, maintainable, and secure Dockerfiles. +- [Compose file reference](/compose/compose-file/) – Learn the full syntax and options available for configuring services in `compose.yaml`. +- [`docker compose run` CLI reference](/reference/cli/docker/compose/run/) – Run one-off commands in a service container. ## Next steps From bbfd64931ac8653bbd1dae691b5715074e44972f Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Sat, 1 Nov 2025 11:47:34 +0200 Subject: [PATCH 04/13] [feat] update node.js guide to use dhi image example also --- content/guides/nodejs/containerize.md | 158 ++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index 514a08ffb9ae..8abb16d967e0 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -312,6 +312,161 @@ These updates help ensure your app is easy to deploy, fast to load, and producti ### Step 2: Configure the Dockerfile file +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/). + +{{< 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:22` as the base image. + +```dockerfile +# ======================================== +# Optimized Multi-Stage Dockerfile +# Node.js TypeScript Application (Using DHI) +# ======================================== + +FROM /dhi-node:22 AS base + +# Set working directory +WORKDIR /app + +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 -G nodejs && \ + chown -R nodejs:nodejs /app + +# ======================================== +# Dependencies Stage +# ======================================== +FROM base AS deps + +# Copy package files +COPY package*.json ./ + +# Install production dependencies +RUN --mount=type=cache,target=/root/.npm,sharing=locked \ + npm ci --omit=dev && \ + npm cache clean --force + +# Set proper ownership +RUN chown -R nodejs:nodejs /app + +# ======================================== +# Build Dependencies Stage +# ======================================== +FROM base AS build-deps + +# Copy package files +COPY package*.json ./ + +# Install all dependencies with build optimizations +RUN --mount=type=cache,target=/root/.npm,sharing=locked \ + npm ci --no-audit --no-fund && \ + npm cache clean --force + +# Create necessary directories and set permissions +RUN mkdir -p /app/node_modules/.vite && \ + chown -R nodejs:nodejs /app + +# ======================================== +# Build Stage +# ======================================== +FROM build-deps AS build + +# Copy only necessary files for building (respects .dockerignore) +COPY --chown=nodejs:nodejs . . + +# Build the application +RUN npm run build + +# Set proper ownership +RUN chown -R nodejs:nodejs /app + +# ======================================== +# Development Stage +# ======================================== +FROM build-deps AS development + +# Set environment +ENV NODE_ENV=development \ + NPM_CONFIG_LOGLEVEL=warn + +# Copy source files +COPY . . + +# Ensure all directories have proper permissions +RUN mkdir -p /app/node_modules/.vite && \ + chown -R nodejs:nodejs /app && \ + chmod -R 755 /app + +# Switch to non-root user +USER nodejs + +# Expose ports +EXPOSE 3000 5173 9229 + +# Start development server +CMD ["npm", "run", "dev:docker"] + +# ======================================== +# Production Stage +# ======================================== +ARG NODE_VERSION=22.21.0-alpine3.21 +FROM node:${NODE_VERSION} AS production + +# Set working directory +WORKDIR /app + +# Create non-root user for security +RUN addgroup -g 1001 -S nodejs && \ + adduser -S nodejs -u 1001 -G nodejs && \ + chown -R nodejs:nodejs /app + +# Set optimized environment variables +ENV NODE_ENV=production \ + NODE_OPTIONS="--max-old-space-size=256 --no-warnings" \ + NPM_CONFIG_LOGLEVEL=silent + +# Copy production dependencies from deps stage +COPY --from=deps --chown=nodejs:nodejs /app/node_modules ./node_modules +COPY --from=deps --chown=nodejs:nodejs /app/package*.json ./ +# Copy built application from build stage +COPY --from=build --chown=nodejs:nodejs /app/dist ./dist + +# Switch to non-root user for security +USER nodejs + +# Expose port +EXPOSE 3000 + +# Start production server +CMD ["node", "dist/server.js"] + +# ======================================== +# Test Stage +# ======================================== +FROM build-deps AS test + +# Set environment +ENV NODE_ENV=test \ + CI=true + +# Copy source files +COPY --chown=nodejs:nodejs . . + +# Switch to non-root user +USER nodejs + +# Run tests with coverage +CMD ["npm", "run", "test:coverage"] +``` + +{{< /tab >}} +{{< tab name="Using the official Docker image" >}} + Now you need to create a production-ready multi-stage Dockerfile. Replace the generated Dockerfile with the following optimized configuration: ```dockerfile @@ -456,6 +611,9 @@ USER nodejs # Run tests with coverage CMD ["npm", "run", "test:coverage"] ``` +{{< /tab >}} + +{{< /tabs >}} Key features of this ultra-optimized Dockerfile: From 389ae06818c328de4340c4d67b1cd334a95dea7f Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Sat, 1 Nov 2025 11:58:59 +0200 Subject: [PATCH 05/13] [fix] Vale recomendations --- content/guides/nodejs/configure-github-actions.md | 4 ++-- content/guides/nodejs/containerize.md | 6 +++--- content/guides/nodejs/develop.md | 8 ++++---- content/guides/nodejs/run-tests.md | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/content/guides/nodejs/configure-github-actions.md b/content/guides/nodejs/configure-github-actions.md index 08b32dbe0275..b4dd941a78fb 100644 --- a/content/guides/nodejs/configure-github-actions.md +++ b/content/guides/nodejs/configure-github-actions.md @@ -286,7 +286,7 @@ After you've added your workflow file, it's time to trigger and observe the CI/C 2. Monitor the workflow execution 1. Go to the Actions tab in your GitHub repository. - 2. Click into the workflow run to follow each step: **test**, **build**, **security**, and (if successful) **push** and **deploy**. + 2. Select into the workflow run to follow each step: **test**, **build**, **security**, and (if successful) **push** and **deploy**. 3. Verify the Docker image on Docker Hub - After a successful workflow run, visit your [Docker Hub repositories](https://hub.docker.com/repositories). @@ -300,7 +300,7 @@ After you've added your workflow file, it's time to trigger and observe the CI/C > To maintain code quality and prevent accidental direct pushes, enable branch protection rules: > > - Navigate to your **GitHub repo → Settings → Branches**. -> - Under Branch protection rules, click **Add rule**. +> - Under Branch protection rules, and select **Add rule**. > - Specify `main` as the branch name. > - Enable options like: > - _Require a pull request before merging_. diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index 8abb16d967e0..c410167bfe2b 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -77,7 +77,7 @@ Let's get started! ``` The CLI will prompt you with a few questions about your app setup. -For consistency, please use the same responses shown in the example below when prompted: +For consistency, use the same responses shown in the example following when prompted: | Question | Answer | |------------------------------------------------------------|-----------------| | What application platform does your project use? | Node | @@ -295,7 +295,7 @@ ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 ## Build the Docker image -The default Dockerfile generated by `docker init` serves as a solid starting point for general Node.js applications. However, this is a full-stack TypeScript application with both backend API and frontend React components, so we need to tailor the Dockerfile to optimize for this architecture. +The default Dockerfile generated by `docker init` provides a reliable baseline for standard Node.js applications. However, since this project is a full-stack TypeScript application that includes both a backend API and frontend React components, the Dockerfile should be customized to better support and optimize this specific architecture. ### Step 1: Review the generated files @@ -626,7 +626,7 @@ Key features of this ultra-optimized Dockerfile: - **Health checks**: Lightweight health monitoring using Node.js built-in modules - **Signal handling**: Proper process management with graceful shutdown handlers - **Single Port Production**: API runs on port 3000 internally, exposed as port 8080 externally -- **Memory Optimized**: Reduced Node.js memory footprint (256MB vs 1GB default) +- **Memory Optimized**: Reduced Node.js memory footprint (256MB versus 1GB default) ### Image Size Optimization diff --git a/content/guides/nodejs/develop.md b/content/guides/nodejs/develop.md index 6f473f3963d1..58ccece6df55 100644 --- a/content/guides/nodejs/develop.md +++ b/content/guides/nodejs/develop.md @@ -448,8 +448,8 @@ docker compose up app-dev --build 1. **Attach the debugger**: - Open VS Code - Go to the Debug panel (Ctrl/Cmd + Shift + D) - - Select "Attach to Docker Container" from the dropdown - - Click the green play button or press F5 + - Select "Attach to Docker Container" from the drop-down + - Select the green play button or press F5 ### Chrome DevTools (Alternative) @@ -467,13 +467,13 @@ docker compose up app-dev --build chrome://inspect ``` -1. **Click "Configure"** and add: +1. **Select "Configure"** and add: ```text localhost:9229 ``` -1. **Click "inspect"** under your Node.js target when it appears +1. **Select "inspect"** under your Node.js target when it appears ### Debugging Configuration Details diff --git a/content/guides/nodejs/run-tests.md b/content/guides/nodejs/run-tests.md index 658c4552e028..b580ef65e108 100644 --- a/content/guides/nodejs/run-tests.md +++ b/content/guides/nodejs/run-tests.md @@ -128,7 +128,7 @@ The test suite covers: - **Client Components** (`src/client/components/__tests__/`): React component testing with React Testing Library - **Custom Hooks** (`src/client/hooks/__tests__/`): React hooks testing with proper mocking -- **Server Routes** (`src/server/__tests__/routes/`): API endpoint testing with Supertest +- **Server Routes** (`src/server/__tests__/routes/`): API endpoint testing - **Database Layer** (`src/server/database/__tests__/`): PostgreSQL database operations testing - **Utility Functions** (`src/shared/utils/__tests__/`): Validation and helper function testing - **Integration Tests** (`src/client/__tests__/`): Full application integration testing From 6520e039bbc2becd0c19efa3eb0081c88811f5f0 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Sat, 1 Nov 2025 12:00:33 +0200 Subject: [PATCH 06/13] [fix] typo --- content/guides/nodejs/configure-github-actions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/nodejs/configure-github-actions.md b/content/guides/nodejs/configure-github-actions.md index b4dd941a78fb..1a6c7a531470 100644 --- a/content/guides/nodejs/configure-github-actions.md +++ b/content/guides/nodejs/configure-github-actions.md @@ -300,7 +300,7 @@ After you've added your workflow file, it's time to trigger and observe the CI/C > To maintain code quality and prevent accidental direct pushes, enable branch protection rules: > > - Navigate to your **GitHub repo → Settings → Branches**. -> - Under Branch protection rules, and select **Add rule**. +> - Under Branch protection rules, select **Add rule**. > - Specify `main` as the branch name. > - Enable options like: > - _Require a pull request before merging_. From 771b845540ba25b20f2963122986b99580fe419c Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Sat, 1 Nov 2025 12:01:46 +0200 Subject: [PATCH 07/13] [fix] typo --- content/guides/nodejs/containerize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index c410167bfe2b..6e796c789a46 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -653,7 +653,7 @@ The `.dockerignore` file tells Docker which files and folders to exclude when bu > > To learn more, visit the [.dockerignore reference](/reference/dockerfile.md#dockerignore-file). -Copy and replace the contents of your existing `.dockerignore` with the optimized configuration below: +Copy and replace the contents of your existing `.dockerignore` with the optimized configuration: ```dockerignore # Optimized .dockerignore for Node.js + React Todo App From 4cae5b3d4395626179f3bd1431c25e460be3df3b Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Thu, 13 Nov 2025 22:37:43 +0200 Subject: [PATCH 08/13] [fix] broken reference --- content/guides/nodejs/deploy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/guides/nodejs/deploy.md b/content/guides/nodejs/deploy.md index 12e47a2db0e9..333cd868f014 100644 --- a/content/guides/nodejs/deploy.md +++ b/content/guides/nodejs/deploy.md @@ -583,7 +583,7 @@ Your application is now running in a production-like environment with enterprise Explore official references and best practices to sharpen your Kubernetes deployment workflow: - [Kubernetes documentation](https://kubernetes.io/docs/home/) – Learn about core concepts, workloads, services, and more. -- [Deploy on Kubernetes with Docker Desktop](/manuals/desktop/features/kubernetes.md) – Use Docker Desktop’s built-in Kubernetes support for local testing and development. +- [Deploy on Kubernetes with Docker Desktop](/manuals/desktop/use-desktop/kubernetes.md) – Use Docker Desktop's built-in Kubernetes support for local testing and development. - [`kubectl` CLI reference](https://kubernetes.io/docs/reference/kubectl/) – Manage Kubernetes clusters from the command line. - [Kubernetes Deployment resource](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/) – Understand how to manage and scale applications using Deployments. - [Kubernetes Service resource](https://kubernetes.io/docs/concepts/services-networking/service/) – Learn how to expose your application to internal and external traffic. From 578509eef090f709bb2e65c725dab1e59e408fc4 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Fri, 14 Nov 2025 07:04:13 +0200 Subject: [PATCH 09/13] [fix] minnor text issues and node.js image increase --- .../guides/nodejs/configure-github-actions.md | 6 ++-- content/guides/nodejs/containerize.md | 28 +++++++++++-------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/content/guides/nodejs/configure-github-actions.md b/content/guides/nodejs/configure-github-actions.md index 1a6c7a531470..6197a1c29e20 100644 --- a/content/guides/nodejs/configure-github-actions.md +++ b/content/guides/nodejs/configure-github-actions.md @@ -50,7 +50,7 @@ To enable GitHub Actions to build and push Docker images, you'll securely store 3. Create a new [GitHub repository](https://github.com/new) for your Node.js project -4. Add Docker Hub credentials as GitHub repository secrets +4. Add Docker Hub credentials as GitHub repository secrets. In your newly created GitHub repository: 1. Navigate to: @@ -66,7 +66,7 @@ To enable GitHub Actions to build and push Docker images, you'll securely store These secrets let GitHub Actions to authenticate securely with Docker Hub during automated workflows. -5. Connect Your Local Project to GitHub +5. Connect your local project to GitHub. Link your local project `docker-nodejs-sample` to the GitHub repository you just created by running the following command from your project root: @@ -92,7 +92,7 @@ To enable GitHub Actions to build and push Docker images, you'll securely store This confirms that your local repository is properly linked and ready to push your source code to GitHub. -6. Push Your Source Code to GitHub +6. Push your source code to GitHub. Follow these steps to commit and push your local project to your GitHub repository: 1. Stage all files for commit. diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index 6e796c789a46..16feecd133ce 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -1,5 +1,5 @@ --- -title: Containerize a Node.js Application +title: Containerize a Node.js application linkTitle: Containerize weight: 10 keywords: node.js, node, containerize, initialize @@ -81,7 +81,7 @@ For consistency, use the same responses shown in the example following when prom | Question | Answer | |------------------------------------------------------------|-----------------| | What application platform does your project use? | Node | -| What version of Node do you want to use? | 22.21.0-alpine3.21 | +| 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 | @@ -297,9 +297,9 @@ ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 The default Dockerfile generated by `docker init` provides a reliable baseline for standard Node.js applications. However, since this project is a full-stack TypeScript application that includes both a backend API and frontend React components, the Dockerfile should be customized to better support and optimize this specific architecture. -### Step 1: Review the generated files +### Review the generated files -In this step, you’ll improve the Dockerfile and configuration files by following best practices: +In the following step, you’ll improve the Dockerfile and configuration files by following best practices: - Use multi-stage builds to keep the final image clean and small - Improve performance and security by only including what’s needed @@ -310,17 +310,22 @@ These updates help ensure your app is easy to deploy, fast to load, and producti > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. > For full details, see the [Dockerfile reference](/reference/dockerfile/). -### Step 2: Configure the Dockerfile file +### Step 1: Configure the Dockerfile 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:22` as the base image. +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 # ======================================== @@ -328,7 +333,7 @@ Mirrored repositories must start with `dhi-`, for example: `FROM /dhi-node:22 AS base +FROM /dhi-node:24-alpine3.22-dev AS base # Set working directory WORKDIR /app @@ -414,8 +419,7 @@ CMD ["npm", "run", "dev:docker"] # ======================================== # Production Stage # ======================================== -ARG NODE_VERSION=22.21.0-alpine3.21 -FROM node:${NODE_VERSION} AS production +FROM /dhi-node:24-alpine3.22-dev AS production # Set working directory WORKDIR /app @@ -465,7 +469,7 @@ CMD ["npm", "run", "test:coverage"] ``` {{< /tab >}} -{{< tab name="Using the official Docker image" >}} +{{< 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: @@ -475,7 +479,7 @@ Now you need to create a production-ready multi-stage Dockerfile. Replace the ge # Node.js TypeScript Application # ======================================== -ARG NODE_VERSION=22.21.0-alpine3.21 +ARG NODE_VERSION=24.11.1-alpine FROM node:${NODE_VERSION} AS base # Set working directory @@ -562,7 +566,7 @@ CMD ["npm", "run", "dev:docker"] # ======================================== # Production Stage # ======================================== -ARG NODE_VERSION=22.21.0-alpine3.21 +ARG NODE_VERSION=24.11.1-alpine FROM node:${NODE_VERSION} AS production # Set working directory From 3005245e61766704ec8552db5d0f5c490eebf1f5 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Fri, 14 Nov 2025 07:53:57 +0200 Subject: [PATCH 10/13] [fix] guide issues --- .../guides/nodejs/configure-github-actions.md | 6 +- content/guides/nodejs/containerize.md | 12 +- content/guides/nodejs/develop.md | 152 +++++++++--------- content/guides/nodejs/run-tests.md | 2 +- 4 files changed, 86 insertions(+), 86 deletions(-) diff --git a/content/guides/nodejs/configure-github-actions.md b/content/guides/nodejs/configure-github-actions.md index 6197a1c29e20..c024b20cd568 100644 --- a/content/guides/nodejs/configure-github-actions.md +++ b/content/guides/nodejs/configure-github-actions.md @@ -37,18 +37,18 @@ To enable GitHub Actions to build and push Docker images, you'll securely store ### Step 1: Connect your GitHub repository to Docker Hub -1. Create a Personal Access Token (PAT) from [Docker Hub](https://hub.docker.com) +1. Create a Personal Access Token (PAT) from [Docker Hub](https://hub.docker.com). 1. Go to your **Docker Hub account → Account Settings → Security**. 2. Generate a new Access Token with **Read/Write** permissions. 3. Name it something like `docker-nodejs-sample`. 4. Copy and save the token — you'll need it in Step 4. -2. Create a repository in [Docker Hub](https://hub.docker.com/repositories/) +2. Create a repository in [Docker Hub](https://hub.docker.com/repositories/). 1. Go to your **Docker Hub account → Create a repository**. 2. For the Repository Name, use something descriptive — for example: `nodejs-sample`. 3. Once created, copy and save the repository name — you'll need it in Step 4. -3. Create a new [GitHub repository](https://github.com/new) for your Node.js project +3. Create a new [GitHub repository](https://github.com/new) for your Node.js project. 4. Add Docker Hub credentials as GitHub repository secrets. diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index 16feecd133ce..7c2fd2a2370e 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -644,16 +644,16 @@ This Dockerfile implements several strategies to minimize the final image size: **Expected size reduction**: ~50% smaller than standard Node.js images -### Step 3: Configure the .dockerignore file +### Step 2: Configure the .dockerignore file The `.dockerignore` file tells Docker which files and folders to exclude when building the image. > [!NOTE] > This helps: > -> 1.Reduce image size -> 2.Speed up the build process -> 3.Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. +> - Reduce image size +> - Speed up the build process +> - Prevent sensitive or unnecessary files (like `.env`, `.git`, or `node_modules`) from being added to the final image. > > To learn more, visit the [.dockerignore reference](/reference/dockerfile.md#dockerignore-file). @@ -705,7 +705,7 @@ nodejs-sample-kubernetes.yaml !tsconfig.json ``` -### Step 4: Build the Node.js application image +### Step 3: Build the Node.js application image After creating all the configuration files, your project directory should now contain all necessary Docker configuration files: @@ -735,7 +735,7 @@ What this command does: - Packages the application and its dependencies into a Docker image - Tags the image as docker-nodejs-sample so you can reference it later -#### Step 6: View local images +#### Step 4: View local images After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](/manuals/desktop/use-desktop/images.md). Since you're already working in the terminal, let's use the Docker CLI. diff --git a/content/guides/nodejs/develop.md b/content/guides/nodejs/develop.md index 58ccece6df55..c23e8a0fbe1d 100644 --- a/content/guides/nodejs/develop.md +++ b/content/guides/nodejs/develop.md @@ -166,7 +166,7 @@ networks: 1. The PostgreSQL database configuration is handled automatically by the application. The database is created and initialized when the application starts, with data persisted using the `postgres_data` volume. -2. Configure your environment by copying the example file: +1. Configure your environment by copying the example file: ```console $ cp .env.example .env @@ -192,25 +192,25 @@ networks: ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 ``` -3. Run the following command to start your application in development mode: +1. Run the following command to start your application in development mode: ```console $ docker compose up app-dev --build ``` -4. Open a browser and verify that the application is running at [http://localhost:5173](http://localhost:5173) for the frontend or [http://localhost:3000](http://localhost:3000) for the API. The React frontend is served by Vite dev server on port 5173, with API calls proxied to the Express server on port 3000. +1. Open a browser and verify that the application is running at [http://localhost:5173](http://localhost:5173) for the frontend or [http://localhost:3000](http://localhost:3000) for the API. The React frontend is served by Vite dev server on port 5173, with API calls proxied to the Express server on port 3000. -5. Add some items to the todo list to test data persistence. +1. Add some items to the todo list to test data persistence. -6. After adding some items to the todo list, press `CTRL + C` in the terminal to stop your application. +1. After adding some items to the todo list, press `CTRL + C` in the terminal to stop your application. -7. Run the application again: +1. Run the application again: - ```console - $ docker compose up app-dev - ``` + ```console + $ docker compose up app-dev + ``` -8. Refresh [http://localhost:5173](http://localhost:5173) in your browser and verify that the todo items persisted, even after the containers were removed and ran again. +1. Refresh [http://localhost:5173](http://localhost:5173) in your browser and verify that the todo items persisted, even after the containers were removed and ran again. ## Configure and run a development container @@ -355,7 +355,7 @@ $ npm run db:start # Start PostgreSQL container $ npm run dev # Start both server and client ``` -### Using Task Runner (Alternative) +### Using Task Runner (alternative) The project includes a comprehensive Taskfile.yml for advanced workflows: @@ -397,55 +397,55 @@ Any changes to the application's source files on your local machine will now be Try making a change to test hot reloading: 1. Open `src/client/components/TodoApp.tsx` in an IDE or text editor -2. Update the main heading text: - -```diff --

-- Modern Todo App --

-+

-+ My Todo App -+

-``` +1. Update the main heading text: + + ```diff + -

+ - Modern Todo App + -

+ +

+ + My Todo App + +

+ ``` 1. Save the file and the Vite dev server will automatically reload the page with your changes -**Debugging Support:** +**Debugging support:** You can connect a debugger to your application on port 9229. The Node.js inspector is enabled with `--inspect=0.0.0.0:9230` in the development script (`dev:server`). -### VS Code Debugger Setup +### VS Code debugger setup -1. **Create a launch configuration** in `.vscode/launch.json`: +1. Create a launch configuration in `.vscode/launch.json`: -```json -{ - "version": "0.2.0", - "configurations": [ + ```json { - "name": "Attach to Docker Container", - "type": "node", - "request": "attach", - "port": 9229, - "address": "localhost", - "localRoot": "${workspaceFolder}", - "remoteRoot": "/app", - "protocol": "inspector", - "restart": true, - "sourceMaps": true, - "skipFiles": ["/**"] + "version": "0.2.0", + "configurations": [ + { + "name": "Attach to Docker Container", + "type": "node", + "request": "attach", + "port": 9229, + "address": "localhost", + "localRoot": "${workspaceFolder}", + "remoteRoot": "/app", + "protocol": "inspector", + "restart": true, + "sourceMaps": true, + "skipFiles": ["/**"] + } + ] } - ] -} ``` -1. **Start your development container**: +1. Start your development container: -```console -docker compose up app-dev --build -``` + ```console + docker compose up app-dev --build + ``` -1. **Attach the debugger**: +1. Attach the debugger: - Open VS Code - Go to the Debug panel (Ctrl/Cmd + Shift + D) - Select "Attach to Docker Container" from the drop-down @@ -455,27 +455,27 @@ docker compose up app-dev --build You can also use Chrome DevTools for debugging: -1. **Start your container** (if not already running): +1. Start your container (if not already running): -```console -docker compose up app-dev --build -``` + ```console + docker compose up app-dev --build + ``` -1. **Open Chrome** and navigate to: +1. Open Chrome and navigate to: -```text -chrome://inspect -``` + ```text + chrome://inspect + ``` -1. **Select "Configure"** and add: +1. Select **Configure** and add: -```text -localhost:9229 -``` + ```text + localhost:9229 + ``` -1. **Select "inspect"** under your Node.js target when it appears +1. Select **inspect** under your Node.js target when it appears. -### Debugging Configuration Details +### Debugging configuration details The debugger configuration: @@ -485,32 +485,32 @@ The debugger configuration: The debugger listens on all interfaces (`0.0.0.0`) inside the container on port 9230 and is accessible on port 9229 from your host machine. -### Troubleshooting Debugger Connection +### Troubleshooting debugger connection If the debugger doesn't connect: -1. **Check if the container is running**: +1. Check if the container is running: -```console -docker ps -``` + ```console + docker ps + ``` -1. **Check if the port is exposed**: +1. Check if the port is exposed: -```console -docker port todoapp-dev -``` + ```console + docker port todoapp-dev + ``` -1. **Check container logs**: +1. Check container logs: -```console -docker compose logs app-dev -``` + ```console + docker compose logs app-dev + ``` -You should see a message like: + You should see a message like: -```text -Debugger listening on ws://0.0.0.0:9230/... + ```text + Debugger listening on ws://0.0.0.0:9230/... ``` Now you can set breakpoints in your TypeScript source files and debug your containerized Node.js application! diff --git a/content/guides/nodejs/run-tests.md b/content/guides/nodejs/run-tests.md index b580ef65e108..30fe9036a96d 100644 --- a/content/guides/nodejs/run-tests.md +++ b/content/guides/nodejs/run-tests.md @@ -122,7 +122,7 @@ You should see output like the following: Duration 1.98s (transform 456ms, setup 1.26s, collect 1.74s, tests 1.87s, environment 5.82s, prepare 916ms) ``` -### Test Structure +### Test structure The test suite covers: From 35884fe3eeb094aee5cbb899edc7caf936644700 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Fri, 14 Nov 2025 07:59:03 +0200 Subject: [PATCH 11/13] [fix] typio in develop file --- content/guides/nodejs/develop.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/content/guides/nodejs/develop.md b/content/guides/nodejs/develop.md index c23e8a0fbe1d..fc0a10dc66a5 100644 --- a/content/guides/nodejs/develop.md +++ b/content/guides/nodejs/develop.md @@ -396,7 +396,7 @@ Any changes to the application's source files on your local machine will now be Try making a change to test hot reloading: -1. Open `src/client/components/TodoApp.tsx` in an IDE or text editor +1. Open `src/client/components/TodoApp.tsx` in an IDE or text editor. 1. Update the main heading text: ```diff @@ -408,7 +408,7 @@ Try making a change to test hot reloading: + ``` -1. Save the file and the Vite dev server will automatically reload the page with your changes +1. Save the file and the Vite dev server will automatically reload the page with your changes. **Debugging support:** @@ -451,7 +451,7 @@ You can connect a debugger to your application on port 9229. The Node.js inspect - Select "Attach to Docker Container" from the drop-down - Select the green play button or press F5 -### Chrome DevTools (Alternative) +### Chrome DevTools (alternative) You can also use Chrome DevTools for debugging: From cb8e4db42b518961e3fb19c6fa44edbee1207e22 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Thu, 20 Nov 2025 22:13:55 +0200 Subject: [PATCH 12/13] [fix] fix the linting errors in develop file --- content/guides/nodejs/_index.md | 11 ++---- content/guides/nodejs/develop.md | 63 ++++++++++++++++---------------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/content/guides/nodejs/_index.md b/content/guides/nodejs/_index.md index dc43331e2855..3231019d4338 100644 --- a/content/guides/nodejs/_index.md +++ b/content/guides/nodejs/_index.md @@ -15,16 +15,13 @@ params: time: 20 minutes --- -[Node.js](https://nodejs.org/en) is a powerful JavaScript runtime for building scalable, high-performance applications. This guide demonstrates containerizing a modern TypeScript Node.js application with React frontend, PostgreSQL database, and production-ready Docker configurations. +[Node.js](https://nodejs.org/en) is a powerful JavaScript runtime for building scalable, high-performance applications. This guide demonstrates containerizing a modern TypeScript Node.js application with React.js front-end, PostgreSQL database, and production-ready Docker configurations. The sample application is a modern full-stack Todo application featuring: -- **Backend**: Express.js 5.x with TypeScript, PostgreSQL database, RESTful API with comprehensive error handling -- **Frontend**: React 19 with Vite, Tailwind CSS 4, modern UI components and hooks -- **Build System**: Lightning-fast esbuild for server builds (12KB output), Vite for client builds -- **Production Features**: Multi-stage Docker builds, security hardening, health checks, graceful shutdown -- **Development Tools**: Hot reload, debugging support, comprehensive testing with Vitest -- **Architecture**: Clean separation of client/server code, shared utilities, and comprehensive test coverage +- **Backend**: Express.js 5.x with TypeScript, PostgreSQL database, RESTful API with comprehensive error handling. +- **Frontend**: React 19 with Vite, Tailwind CSS 4, modern UI components and hooks. + > **Acknowledgment** > diff --git a/content/guides/nodejs/develop.md b/content/guides/nodejs/develop.md index fc0a10dc66a5..b64bb20d2070 100644 --- a/content/guides/nodejs/develop.md +++ b/content/guides/nodejs/develop.md @@ -205,10 +205,9 @@ networks: 1. After adding some items to the todo list, press `CTRL + C` in the terminal to stop your application. 1. Run the application again: - - ```console - $ docker compose up app-dev - ``` + ```console + $ docker compose up app-dev + ``` 1. Refresh [http://localhost:5173](http://localhost:5173) in your browser and verify that the todo items persisted, even after the containers were removed and ran again. @@ -399,14 +398,14 @@ Try making a change to test hot reloading: 1. Open `src/client/components/TodoApp.tsx` in an IDE or text editor. 1. Update the main heading text: - ```diff + ```diff -

- Modern Todo App -

+

+ My Todo App +

- ``` + ``` 1. Save the file and the Vite dev server will automatically reload the page with your changes. @@ -418,7 +417,7 @@ You can connect a debugger to your application on port 9229. The Node.js inspect 1. Create a launch configuration in `.vscode/launch.json`: - ```json + ```json { "version": "0.2.0", "configurations": [ @@ -437,13 +436,13 @@ You can connect a debugger to your application on port 9229. The Node.js inspect } ] } -``` + ``` 1. Start your development container: - ```console - docker compose up app-dev --build - ``` + ```console + docker compose up app-dev --build + ``` 1. Attach the debugger: - Open VS Code @@ -457,21 +456,21 @@ You can also use Chrome DevTools for debugging: 1. Start your container (if not already running): - ```console + ```console docker compose up app-dev --build - ``` + ``` 1. Open Chrome and navigate to: - ```text - chrome://inspect - ``` + ```text + chrome://inspect + ``` 1. Select **Configure** and add: - ```text - localhost:9229 - ``` + ```text + localhost:9229 + ``` 1. Select **inspect** under your Node.js target when it appears. @@ -491,27 +490,27 @@ If the debugger doesn't connect: 1. Check if the container is running: - ```console - docker ps - ``` + ```console + docker ps + ``` 1. Check if the port is exposed: - ```console - docker port todoapp-dev - ``` + ```console + docker port todoapp-dev + ``` 1. Check container logs: - ```console - docker compose logs app-dev - ``` + ```console + docker compose logs app-dev + ``` - You should see a message like: + You should see a message like: - ```text - Debugger listening on ws://0.0.0.0:9230/... -``` + ```text + Debugger listening on ws://0.0.0.0:9230/... + ``` Now you can set breakpoints in your TypeScript source files and debug your containerized Node.js application! From 17101fc5621aae2573284161c14008727b9368d7 Mon Sep 17 00:00:00 2001 From: "kristiyan.velkov" Date: Thu, 20 Nov 2025 23:30:43 +0200 Subject: [PATCH 13/13] [fix] node.js guide content and linting errors --- content/guides/nodejs/_index.md | 8 +- .../guides/nodejs/configure-github-actions.md | 60 +++++++------- content/guides/nodejs/containerize.md | 40 +++------- content/guides/nodejs/deploy.md | 79 ++++++++++--------- content/guides/nodejs/develop.md | 25 +++--- content/guides/nodejs/run-tests.md | 23 +++--- 6 files changed, 106 insertions(+), 129 deletions(-) diff --git a/content/guides/nodejs/_index.md b/content/guides/nodejs/_index.md index 3231019d4338..baa1b029d045 100644 --- a/content/guides/nodejs/_index.md +++ b/content/guides/nodejs/_index.md @@ -15,12 +15,12 @@ params: time: 20 minutes --- -[Node.js](https://nodejs.org/en) is a powerful JavaScript runtime for building scalable, high-performance applications. This guide demonstrates containerizing a modern TypeScript Node.js application with React.js front-end, PostgreSQL database, and production-ready Docker configurations. +[Node.js](https://nodejs.org/en) is a JavaScript runtime for building web applications. This guide shows you how to containerize a TypeScript Node.js application with a React frontend and PostgreSQL database. The sample application is a modern full-stack Todo application featuring: -- **Backend**: Express.js 5.x with TypeScript, PostgreSQL database, RESTful API with comprehensive error handling. -- **Frontend**: React 19 with Vite, Tailwind CSS 4, modern UI components and hooks. +- **Backend**: Express.js with TypeScript, PostgreSQL database, and RESTful API +- **Frontend**: React.js with Vite and Tailwind CSS 4 > **Acknowledgment** @@ -34,7 +34,7 @@ The sample application is a modern full-stack Todo application featuring: In this guide, you will learn how to: - Containerize and run a Node.js application using Docker. -- Run unit tests inside a Docker container. +- Run tests inside a Docker container. - Set up a development container environment. - Configure GitHub Actions for CI/CD with Docker. - Deploy your Dockerized Node.js app to Kubernetes. diff --git a/content/guides/nodejs/configure-github-actions.md b/content/guides/nodejs/configure-github-actions.md index c024b20cd568..3b9f4256d4e2 100644 --- a/content/guides/nodejs/configure-github-actions.md +++ b/content/guides/nodejs/configure-github-actions.md @@ -25,7 +25,7 @@ You must also have: In this section, you'll set up a **CI/CD pipeline** using [GitHub Actions](https://docs.github.com/en/actions) to automatically: - Build your Node.js application inside a Docker container. -- Run comprehensive tests including unit tests, integration tests, and linting. +- Run unit and integration tests, and make sure your application meets solid code quality standards. - Perform security scanning and vulnerability assessment. - Push production-ready images to [Docker Hub](https://hub.docker.com). @@ -38,13 +38,13 @@ To enable GitHub Actions to build and push Docker images, you'll securely store ### Step 1: Connect your GitHub repository to Docker Hub 1. Create a Personal Access Token (PAT) from [Docker Hub](https://hub.docker.com). - 1. Go to your **Docker Hub account → Account Settings → Security**. + 1. From your Docker Hub account, go to **Account Settings → Security**. 2. Generate a new Access Token with **Read/Write** permissions. 3. Name it something like `docker-nodejs-sample`. 4. Copy and save the token — you'll need it in Step 4. 2. Create a repository in [Docker Hub](https://hub.docker.com/repositories/). - 1. Go to your **Docker Hub account → Create a repository**. + 1. From your Docker Hub account, select **Create a repository**. 2. For the Repository Name, use something descriptive — for example: `nodejs-sample`. 3. Once created, copy and save the repository name — you'll need it in Step 4. @@ -53,8 +53,7 @@ To enable GitHub Actions to build and push Docker images, you'll securely store 4. Add Docker Hub credentials as GitHub repository secrets. In your newly created GitHub repository: - 1. Navigate to: - **Settings → Secrets and variables → Actions → New repository secret**. + 1. From **Settings**, go to **Secrets and variables → Actions → New repository secret**. 2. Add the following secrets: @@ -135,9 +134,9 @@ Once completed, your code will be available on GitHub, and any GitHub Actions wo Now you'll create a GitHub Actions workflow that builds your Docker image, runs tests, and pushes the image to Docker Hub. -1. Go to your repository on GitHub and select the **Actions** tab in the top menu. +1. From your repository on GitHub, select the **Actions** tab in the top menu. -2. Select **Set up a workflow yourself** when prompted. +2. When prompted, select **Set up a workflow yourself**. This opens an inline editor to create a new workflow file. By default, it will be saved to: `.github/workflows/main.yml` @@ -261,14 +260,14 @@ jobs: This workflow performs the following tasks for your Node.js application: -- Triggers on every `push` or `pull request` targeting the `main` branch. -- Builds a test Docker image using the `test` stage in your Dockerfile. -- Executes comprehensive tests (101 tests) inside a clean, containerized environment with PostgreSQL integration to ensure consistency. -- Halts the workflow immediately if any test fails — enforcing code quality. -- Caches both Docker build layers and npm dependencies for faster CI runs. -- Authenticates securely with Docker Hub using GitHub repository secrets. -- Builds a production-ready image using the `production` stage in your Dockerfile. -- Tags and pushes the final image to Docker Hub with both `latest` and short SHA tags for traceability. +- Triggers on every `push` or `pull request` to the `main` branch. +- Builds a test Docker image using the `test` stage. +- Runs tests in a containerized environment. +- Stops the workflow if any test fails. +- Caches Docker build layers and npm dependencies for faster runs. +- Authenticates with Docker Hub using GitHub secrets. +- Builds an image using the `production` stage. +- Tags and pushes the image to Docker Hub with `latest` and short SHA tags. > [!NOTE] > For more information about `docker/build-push-action`, refer to the [GitHub Action README](https://github.com/docker/build-push-action/blob/master/README.md). @@ -277,16 +276,16 @@ This workflow performs the following tasks for your Node.js application: ### Step 3: Run the workflow -After you've added your workflow file, it's time to trigger and observe the CI/CD process in action. +After adding your workflow file, trigger the CI/CD process. 1. Commit and push your workflow file - Select "Commit changes…" in the GitHub editor. - - This push will automatically trigger the GitHub Actions pipeline. + From the GitHub editor, select **Commit changes…**. + - This push automatically triggers the GitHub Actions pipeline. 2. Monitor the workflow execution - 1. Go to the Actions tab in your GitHub repository. - 2. Select into the workflow run to follow each step: **test**, **build**, **security**, and (if successful) **push** and **deploy**. + 1. From your GitHub repository, go to the **Actions** tab. + 2. Select the workflow run to follow each step: **test**, **build**, **security**, and (if successful) **push** and **deploy**. 3. Verify the Docker image on Docker Hub - After a successful workflow run, visit your [Docker Hub repositories](https://hub.docker.com/repositories). @@ -299,7 +298,7 @@ After you've added your workflow file, it's time to trigger and observe the CI/C > [!TIP] Protect your main branch > To maintain code quality and prevent accidental direct pushes, enable branch protection rules: > -> - Navigate to your **GitHub repo → Settings → Branches**. +> - From your GitHub repository, go to **Settings → Branches**. > - Under Branch protection rules, select **Add rule**. > - Specify `main` as the branch name. > - Enable options like: @@ -314,18 +313,17 @@ After you've added your workflow file, it's time to trigger and observe the CI/C In this section, you set up a comprehensive CI/CD pipeline for your containerized Node.js application using GitHub Actions. -Here's what you accomplished: +What you accomplished: - Created a new GitHub repository specifically for your project. -- Generated a secure Docker Hub access token and added it to GitHub as a secret. -- Defined a GitHub Actions workflow to: - - Build your application inside a Docker container. - - Run comprehensive tests (101 tests) in a consistent, containerized environment with PostgreSQL integration. - - Push a production-ready image to Docker Hub if tests pass. -- Triggered and verified the workflow execution through GitHub Actions. -- Confirmed that your image was successfully published to Docker Hub. - -With this setup, your Node.js application is now ready for automated testing and deployment across environments — increasing confidence, consistency, and team productivity. +- Generated a Docker Hub access token and added it as a GitHub secret. +- Created a GitHub Actions workflow that: + - Builds your application in a Docker container. + - Run tests in a containerized environment. + - Pushes an image to Docker Hub if tests pass. +- Verified the workflow runs successfully. + +Your Node.js application now has automated testing and deployment. --- diff --git a/content/guides/nodejs/containerize.md b/content/guides/nodejs/containerize.md index 7c2fd2a2370e..8d4178705283 100644 --- a/content/guides/nodejs/containerize.md +++ b/content/guides/nodejs/containerize.md @@ -65,7 +65,7 @@ $ docker init You’ll see output similar to: ```text -Welcome to the Docker Init CLI! +Welcome to the Docker Init CLI This utility will walk you through creating the following files with sensible defaults for your project: - .dockerignore @@ -304,7 +304,7 @@ In the following step, you’ll improve the Dockerfile and configuration files b - Use multi-stage builds to keep the final image clean and small - Improve performance and security by only including what’s needed -These updates help ensure your app is easy to deploy, fast to load, and production-ready. +These updates make your app easier to deploy and faster to load. > [!NOTE] > A `Dockerfile` is a plain text file that contains step-by-step instructions to build a Docker image. It automates packaging your application along with its dependencies and runtime environment. @@ -619,30 +619,14 @@ CMD ["npm", "run", "test:coverage"] {{< /tabs >}} -Key features of this ultra-optimized Dockerfile: - -- **Multi-stage builds**: Separate stages for dependencies, build, development, production, and testing -- **Fast Build System**: Uses esbuild for lightning-fast server builds (12KB output) -- **Ultra-small Production Image**: ~50% size reduction through aggressive optimization -- **Security hardening**: Non-root user, minimal attack surface, no unnecessary packages -- **Performance optimization**: Layer caching, build caching, memory-optimized runtime -- **Aggressive Cleanup**: Removes documentation, tests, TypeScript definitions, and cache files -- **Health checks**: Lightweight health monitoring using Node.js built-in modules -- **Signal handling**: Proper process management with graceful shutdown handlers -- **Single Port Production**: API runs on port 3000 internally, exposed as port 8080 externally -- **Memory Optimized**: Reduced Node.js memory footprint (256MB versus 1GB default) - -### Image Size Optimization - -This Dockerfile implements several strategies to minimize the final image size: - -1. **Aggressive node_modules cleanup**: Removes documentation, tests, and unnecessary files with .dockerignore file. -2. **Multi-stage optimization**: Only copies essential files to production stage -3. **Memory constraints**: Limits Node.js heap size to reduce memory usage -4. **Cache cleanup**: Removes all temporary files and caches -5. **Minimal base image**: Uses Alpine Linux for smallest possible base - -**Expected size reduction**: ~50% smaller than standard Node.js images +Key features of this Dockerfile: +- Multi-stage structure — Separate stages for dependencies, build, development, production, and testing to keep each phase clean and efficient. +- Lean production image — Optimized layering reduces size and keeps only what’s required to run the app. +- Security-minded setup — Uses a dedicated non-root user and excludes unnecessary packages. +- Performance-friendly design — Effective use of caching and well-structured layers for faster builds. +- Clean runtime environment — Removes files not needed in production, such as docs, tests, and build caches. +- Straightforward port usage — The app runs on port 3000 internally, exposed externally as port 8080. +- Memory-optimized runtime — Node.js is configured to run with a smaller memory limit than the default. ### Step 2: Configure the .dockerignore file @@ -737,7 +721,7 @@ What this command does: #### Step 4: View local images -After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](/manuals/desktop/use-desktop/images.md). Since you're already working in the terminal, let's use the Docker CLI. +After building your Docker image, you can check which images are available on your local machine using either the Docker CLI or [Docker Desktop](/manuals/desktop/use-desktop/images.md). Since you're already working in the terminal, use the Docker CLI. To list all locally available Docker images, run the following command: @@ -848,7 +832,7 @@ In this guide, you learned how to containerize, build, and run a Node.js applica What you accomplished: - Initialized your project using `docker init` to scaffold essential Docker configuration files. -- Created a comprehensive `compose.yml` file with development, production, and database services. +- Created a `compose.yml` file with development, production, and database services. - Set up environment configuration with a `.env` file for flexible deployment settings. - Replaced the default `Dockerfile` with a multi-stage build optimized for TypeScript and React. - Replaced the default `.dockerignore` file to exclude unnecessary files and keep the image clean and efficient. diff --git a/content/guides/nodejs/deploy.md b/content/guides/nodejs/deploy.md index 333cd868f014..61234f9f1509 100644 --- a/content/guides/nodejs/deploy.md +++ b/content/guides/nodejs/deploy.md @@ -20,16 +20,14 @@ In this section, you'll learn how to deploy your containerized Node.js applicati You'll deploy a complete stack including: -- Your Node.js Todo application with 3 replicas -- PostgreSQL database with persistent storage -- Auto-scaling based on CPU and memory usage -- SSL/TLS ready ingress configuration -- Production security settings +- Node.js Todo application with 3 replicas. +- PostgreSQL database with persistent storage. +- Auto-scaling based on CPU and memory usage. +- Ingress configuration for external access. +- Security settings. ## Create a Kubernetes deployment file -Create a production-ready Kubernetes deployment file that includes all the components needed for a scalable, secure Node.js application. - Create a new file called `nodejs-sample-kubernetes.yaml` in your project root: ```yaml @@ -396,9 +394,8 @@ Before deploying, you need to customize the deployment file for your environment 3. **Database password** (optional): The default password is already base64 encoded. To change it: - ```bash - # Generate new base64 encoded password - echo -n "your-new-password" | base64 + ```console + $ echo -n "your-new-password" | base64 ``` Then update the Secret: @@ -412,43 +409,51 @@ Before deploying, you need to customize the deployment file for your environment ## Understanding the deployment -This deployment creates a complete, production-ready stack with the following components: +The deployment file creates a complete application stack with multiple components working together. + +### Architecture + +The deployment includes: + +- **Node.js application**: Runs 3 replicas of your containerized Todo app +- **PostgreSQL database**: Single instance with 10Gi of persistent storage +- **Services**: Kubernetes services handle load balancing across application replicas +- **Ingress**: External access through an ingress controller with SSL/TLS support + +### Security -### Application Architecture +The deployment uses several security features: -- **Node.js Application**: 3 replicas running your containerized Todo app -- **PostgreSQL Database**: Single instance with persistent 10Gi storage -- **Load Balancing**: Kubernetes services distribute traffic across app replicas -- **External Access**: Ingress controller handles SSL/TLS and routing +- Containers run as a non-root user (UID 1001) +- Read-only root filesystem prevents unauthorized writes +- Linux capabilities are dropped to minimize attack surface +- Sensitive data like database passwords are stored in Kubernetes secrets -### Production Features +### High availability -**Security Hardening:** +To keep your application running reliably: -- Non-root user execution (UID 1001) -- Read-only root filesystem -- Dropped Linux capabilities -- Kubernetes secrets for sensitive data +- Three application replicas ensure service continues if one pod fails +- Pod disruption budget maintains at least one available pod during updates +- Rolling updates allow zero-downtime deployments +- Health checks on the `/health` endpoint ensure only healthy pods receive traffic -**High Availability:** +### Auto-scaling -- Multiple application replicas (3) -- Pod disruption budget (minimum 1 available) -- Rolling updates with zero downtime -- Health checks on `/health` endpoint +The Horizontal Pod Autoscaler scales your application based on resource usage: -**Auto-Scaling:** +- Scales between 1 and 5 replicas automatically +- Triggers scaling when CPU usage exceeds 70% +- Triggers scaling when memory usage exceeds 80% +- Resource limits: 256Mi-512Mi memory, 250m-500m CPU per pod -- Horizontal Pod Autoscaler (1-5 replicas) -- CPU-based scaling (70% threshold) -- Memory-based scaling (80% threshold) -- Resource limits: 256Mi-512Mi memory, 250m-500m CPU +### Data persistence -**Data Persistence:** +PostgreSQL data is stored persistently: -- PostgreSQL with 10Gi persistent volume -- Automatic database initialization -- Data survives pod restarts and updates +- 10Gi persistent volume stores database files +- Database initializes automatically on first startup +- Data persists across pod restarts and updates ## Deploy your application @@ -567,7 +572,7 @@ $ kubectl delete -f nodejs-sample-kubernetes.yaml ## Summary -In this section, you successfully deployed your containerized Node.js application to Kubernetes with a production-ready configuration. You learned how to: +You've deployed your containerized Node.js application to Kubernetes. You learned how to: - Create a comprehensive Kubernetes deployment file with security hardening - Deploy a multi-tier application (Node.js + PostgreSQL) with persistent storage diff --git a/content/guides/nodejs/develop.md b/content/guides/nodejs/develop.md index b64bb20d2070..5f4d7026e6c1 100644 --- a/content/guides/nodejs/develop.md +++ b/content/guides/nodejs/develop.md @@ -24,7 +24,7 @@ In this section, you'll learn how to set up a development environment for your c ## Add a local database and persist data -The application uses PostgreSQL for data persistence, providing a production-ready database solution. You'll need to add a database service to your Docker Compose configuration. +The application uses PostgreSQL for data persistence. Add a database service to your Docker Compose configuration. ### Add database service to Docker Compose @@ -262,7 +262,7 @@ Next, you'll need to update your Compose file to use the new stage. ### Update your Compose file for development -Now you need to configure your `compose.yml` file to run the development stage with comprehensive bind mounts for hot reloading. Update your development service configuration: +Update your `compose.yml` file to run the development stage with bind mounts for hot reloading: ```yaml {hl_lines=[5,8-10,20-27],collapse=true,title=compose.yml} services: @@ -356,7 +356,7 @@ $ npm run dev # Start both server and client ### Using Task Runner (alternative) -The project includes a comprehensive Taskfile.yml for advanced workflows: +The project includes a Taskfile.yml for advanced workflows: ```console # Development @@ -446,8 +446,7 @@ You can connect a debugger to your application on port 9229. The Node.js inspect 1. Attach the debugger: - Open VS Code - - Go to the Debug panel (Ctrl/Cmd + Shift + D) - - Select "Attach to Docker Container" from the drop-down + - From the Debug panel (Ctrl/Cmd + Shift + D), select **Attach to Docker Container** from the drop-down - Select the green play button or press F5 ### Chrome DevTools (alternative) @@ -460,19 +459,15 @@ You can also use Chrome DevTools for debugging: docker compose up app-dev --build ``` -1. Open Chrome and navigate to: +1. Open Chrome and go to `chrome://inspect`. - ```text - chrome://inspect - ``` - -1. Select **Configure** and add: +1. From the **Configure** option, add: ```text localhost:9229 ``` -1. Select **inspect** under your Node.js target when it appears. +1. When your Node.js target appears, select **inspect**. ### Debugging configuration details @@ -512,15 +507,13 @@ If the debugger doesn't connect: Debugger listening on ws://0.0.0.0:9230/... ``` -Now you can set breakpoints in your TypeScript source files and debug your containerized Node.js application! +Now you can set breakpoints in your TypeScript source files and debug your containerized Node.js application. For more details about Node.js debugging, see the [Node.js documentation](https://nodejs.org/en/docs/guides/debugging-getting-started). ## Summary -In this section, you took a look at setting up your Compose file to add a mock -database and persist data. You also learned how to create a multi-stage -Dockerfile and set up a bind mount for development. +You've set up your Compose file with a PostgreSQL database and data persistence. You also created a multi-stage Dockerfile and configured bind mounts for development. Related information: diff --git a/content/guides/nodejs/run-tests.md b/content/guides/nodejs/run-tests.md index 30fe9036a96d..41f069a83429 100644 --- a/content/guides/nodejs/run-tests.md +++ b/content/guides/nodejs/run-tests.md @@ -15,14 +15,11 @@ Complete all the previous sections of this guide, starting with [Containerize a ## Overview -Testing is an essential part of modern software development. Testing can mean a -lot of things to different development teams. There are unit tests, integration -tests and end-to-end testing. In this guide you take a look at running your unit -tests in Docker when developing and when building. +Testing is a core part of building reliable software. Whether you're writing unit tests, integration tests, or end-to-end tests, running them consistently across environments matters. Docker makes this easy by giving you the same setup locally, in CI/CD, and during image builds. ## Run tests when developing locally -The sample application uses Vitest for testing with comprehensive test coverage across client and server components. The test suite includes 101 passing tests covering React components, custom hooks, API routes, database operations, and utility functions. +The sample application uses Vitest for testing, and it already includes tests for React components, custom hooks, API routes, database operations, and utility functions. ### Run tests locally (without Docker) @@ -116,10 +113,10 @@ You should see output like the following: ✓ src/client/components/__tests__/AddTodoForm.test.tsx (12 tests) 323ms ✓ src/client/hooks/__tests__/useTodos.test.ts (11 tests) 569ms - Test Files 10 passed (10) - Tests 101 passed (101) - Start at 15:32:56 - Duration 1.98s (transform 456ms, setup 1.26s, collect 1.74s, tests 1.87s, environment 5.82s, prepare 916ms) + Test Files 9 passed (9) + Tests 88 passed (88) + Start at 20:57:19 + Duration 4.41s (transform 1.79s, setup 2.66s, collect 5.38s, tests 4.61s, environment 14.07s, prepare 4.34s) ``` ### Test structure @@ -209,10 +206,10 @@ You should see output containing the following: ✓ src/client/components/__tests__/AddTodoForm.test.tsx (12 tests) 323ms ✓ src/client/hooks/__tests__/useTodos.test.ts (11 tests) 569ms - Test Files 10 passed (10) - Tests 101 passed (101) - Start at 07:33:25 - Duration 2.11s (transform 339ms, setup 619ms, collect 1.12s, tests 1.43s, environment 3.52s, prepare 901ms) + Test Files 9 passed (9) + Tests 88 passed (88) + Start at 20:57:19 + Duration 4.41s (transform 1.79s, setup 2.66s, collect 5.38s, tests 4.61s, environment 14.07s, prepare 4.34s) ``` ## Summary