{"id":121899,"date":"2025-01-28T17:54:15","date_gmt":"2025-01-28T17:54:15","guid":{"rendered":"\/tutorials\/?p=121899"},"modified":"2026-03-10T09:26:58","modified_gmt":"2026-03-10T09:26:58","slug":"how-to-use-node-js-with-docker","status":"publish","type":"post","link":"\/ca\/tutorials\/how-to-use-node-js-with-docker","title":{"rendered":"How to use Node.js with Docker"},"content":{"rendered":"<?xml encoding=\"utf-8\" ?><p>Using Node.js with Docker lets you <strong>build fast, scalable applications and deploy them in secure, lightweight containers<\/strong>.<\/p><p>This combination provides significant benefits, such as portability &ndash; so your app runs the same way everywhere &ndash; and consistency across development and production environments.<\/p><p>Containerizing your app also optimizes resource usage and streamlines your deployment process.<\/p><p>Scroll down, and we&rsquo;ll guide you through setting up a Node.js application in Docker from scratch. You&rsquo;ll also learn how to containerize an existing project, test your containerized app, and follow best practices for a production-ready setup.<\/p><p class=\"has-text-align-center\"><a href=\"https:\/\/assets.hostinger.com\/content\/tutorials\/pdf\/Docker-Cheat-Sheet.pdf\" target=\"_blank\" rel=\"noopener\">Download free docker cheat sheet<\/a><\/p><p>\n\n\n\n\n<\/p><h2 class=\"wp-block-heading\" id=\"h-prerequisites-for-using-node-js-with-docker\">Prerequisites for using Node.js with Docker<\/h2><p>Before you begin, make sure you have the following:<\/p><ul class=\"wp-block-list\">\n<li><strong>Virtual private server (VPS)<\/strong>. While you can follow this guide on your personal machine, you&rsquo;ll need a VPS to host your app in production. <a href=\"\/ca\/vps-hosting\">Hostinger&rsquo;s KVM 2<\/a> plan is a great starting point for small-to-medium projects, offering <strong>2<\/strong> vCPU cores, <strong>8 GB<\/strong> of RAM, and <strong>100 GB<\/strong> of NVMe storage for <strong>CA$ 12.59\/month<\/strong>.<\/li>\n\n\n\n<li><strong>Docker running on your system<\/strong>. Hostinger customers can <a href=\"\/ca\/tutorials\/how-to-install-docker-on-ubuntu\">install Docker<\/a> on their VPS with just a few clicks using our preconfigured template.<\/li>\n\n\n\n<li><strong>Node.js installed and configured<\/strong>. If you haven&rsquo;t done so already, follow our guide to <a href=\"\/ca\/tutorials\/how-to-install-nodejs-ubuntu\">install Node.js<\/a> and NPM on your Ubuntu server.<\/li>\n\n\n\n<li><strong>Basic knowledge of Docker commands<\/strong>. Familiarity with commands like <strong>docker build<\/strong> and <strong>docker run<\/strong> will be helpful, as you&rsquo;ll frequently execute them in the terminal.<\/li>\n<\/ul><p><div class=\"protip\">\n                    <h4 class=\"title\">&#128161; Pro tip<\/h4>\n                    <p>Hostinger offers premade templates for both Docker and Node.js, but you can only install one template at a time. We suggest setting up Docker first using the template, then manually installing Node.js with commands, as the latter has a simpler setup process.<\/p>\n                <\/div>\n\n\n<?xml encoding=\"utf-8\" ?><figure class=\"wp-block-image size-large\"><a class=\"hgr-tutorials-cta hgr-tutorials-cta-vps-hosting\" href=\"\/ca\/vps-hosting\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"300\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2023\/02\/VPS-hosting-banner-1024x300.png\" alt=\"\" class=\"wp-image-77934\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<\/p><h2 class=\"wp-block-heading\" id=\"h-setting-up-a-node-js-app-in-docker\">Setting up a Node.js app in Docker<\/h2><p>To create a simple Node.js Docker container, follow these steps. You&rsquo;ll <strong>start by selecting a base image and finish by running your first containerized app<\/strong>.<\/p><h3 class=\"wp-block-heading\" id=\"h-1-choosing-the-right-node-js-docker-image\">1. Choosing the right Node.js Docker image<\/h3><p>First, select a base image for your container. The <a href=\"https:\/\/hub.docker.com\/_\/node\" target=\"_blank\" rel=\"noopener\">official Node.js Docker image<\/a> provides several prebuilt options optimized for different needs.<\/p><p>The main types include:<\/p><ul class=\"wp-block-list\">\n<li><strong>Full images<\/strong>. Larger images that include many tools and libraries, making them useful for development and debugging. Example: <strong>node:24<\/strong>.<\/li>\n\n\n\n<li><strong>Slim images<\/strong>. Lighter than full images, containing only the essential components needed to run Node.js. Example: <strong>node:24-slim<\/strong>.<\/li>\n\n\n\n<li><strong>Alpine images<\/strong>. Extremely lightweight images based on Alpine Linux. Their small size reduces the attack surface, making them the best Docker image for Node.js in production. Example: <strong>node:24-alpine<\/strong>.<\/li>\n<\/ul><p>For production, <strong>we recommend using Alpine images<\/strong> to keep your containers small, fast, and secure.<\/p><h3 class=\"wp-block-heading\" id=\"h-2-creating-a-basic-node-js-application\">2. Creating a basic Node.js application<\/h3><p>Next, create a basic Node.js app. Open your terminal and connect to your VPS using SSH:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">ssh root@your_vps_ip<\/pre><p>Hostinger VPS users can find their server&rsquo;s IP address in <strong>hPanel &rarr; VPS &rarr; Manage &rarr; Overview &rarr; VPS details<\/strong>.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479a9f95\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"345\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted-1024x345.png\" alt=\"The IPv4 information in hPanel's VPS\" class=\"wp-image-133147\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>After logging in, create a new project directory and navigate into it:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">mkdir node-docker-app &amp;&amp; cd node-docker-app<\/pre><p>Initialize the project with <strong>npm<\/strong>, which generates a <strong>package.json<\/strong> file to manage your app&rsquo;s metadata and dependencies:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">npm init -y<\/pre><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479b8916\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"567\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init-1024x567.png\" alt=\"A terminal output after running the mkdir, cd, and npm init commands\" class=\"wp-image-133143\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>Now, create a file named <strong>app.js<\/strong> with a text editor like <a href=\"\/ca\/tutorials\/how-to-install-and-use-nano-text-editor\">nano<\/a>:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">nano app.js<\/pre><p>Paste the following code inside the file. This script sets up a simple HTTP server that responds with &ldquo;Hello, Docker!&rdquo;:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">const http = require(\"http\");\n\nconst port = 3000;\n\nconst server = http.createServer((req, res) =&gt; {\n  res.writeHead(200, { \"Content-Type\": \"text\/plain\" });\n  res.end(\"Hello, Docker!\");\n});\n\nserver.listen(port, () =&gt; {\n  console.log(`Server running at http:\/\/localhost:${port}`);\n});<\/pre><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479ba040\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"597\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-1024x597.png\" alt=\"The app.js file content viewed through the nano text editor\" class=\"wp-image-133144\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>Save the file and exit <strong>nano<\/strong> by pressing <strong>Ctrl + X &rarr; Y &rarr; Enter<\/strong>.<\/p><h3 class=\"wp-block-heading\" id=\"h-3-writing-a-simple-dockerfile\">3. Writing a simple Dockerfile<\/h3><p>A Dockerfile is a text file with step-by-step instructions for building a Docker image. Create a file named <strong>Dockerfile<\/strong> without an extension in your project&rsquo;s root directory:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">nano Dockerfile<\/pre><p>Add the following content. This is a basic Node.js Dockerfile example:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Use an Alpine-based Node.js image\nFROM node:24-alpine\n\n# Set the working directory inside the container\nWORKDIR \/app\n\n# Copy package.json and package-lock.json\nCOPY package*.json .\/\n\n# Install application dependencies\nRUN npm install\n\n# Copy the rest of the application source code\nCOPY . .\n\n# Expose port 3000 to the outside world\nEXPOSE 3000\n\n# Command to run the application\nCMD [\"node\", \"app.js\"]<\/pre><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479bb7b2\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"586\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-1024x586.png\" alt=\"The Dockerfile file content viewed through the nano text editor\" class=\"wp-image-133145\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>Save and close the file when finished.<\/p><h3 class=\"wp-block-heading\" id=\"h-4-building-the-image\">4. Building the image<\/h3><p>After creating the Dockerfile, build your Docker image by running this command in the project directory:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker build -t node-docker-app .<\/pre><p>The <strong>-t<\/strong> flag tags the image with the name <strong>node-docker-app<\/strong>. When the build completes successfully, Docker displays a confirmation message.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479bcd6e\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"600\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build-1024x600.png\" alt=\"A terminal output after running the docker build command\" class=\"wp-image-133146\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><h3 class=\"wp-block-heading\" id=\"h-5-running-the-container\">5. Running the container<\/h3><p>Once the image is built, run Node.js in a Docker container with this command:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker run -d -p 3000:3000 node-docker-app<\/pre><p>The <strong>-p 3000:3000<\/strong> flag maps port <strong>3000<\/strong> on your server to port <strong>3000<\/strong> inside the container, while the <strong>-d<\/strong> flag runs the container in detached mode (in the background).<\/p><p>Open your browser and go to <strong>http:\/\/your_vps_ip:3000<\/strong> to check if it works. You should see the message &ldquo;Hello, Docker!&rdquo;.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479be941\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-full wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"830\" height=\"320\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/browser-hello-docker.png\" alt='The \"Hello Docker\" message in the browser' class=\"wp-image-133148\"  sizes=\"auto, (max-width: 830px) 100vw, 830px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>Now that you&rsquo;ve built and run a basic application, let&rsquo;s explore how to dockerize a more complex Node.js app.<\/p><h2 class=\"wp-block-heading\" id=\"h-containerizing-an-existing-node-js-application\">Containerizing an existing Node.js application<\/h2><p>To containerize an existing Node.js project, <strong>prepare your app, create an ignore file to keep the image lean, and write an optimized Dockerfile<\/strong>.<\/p><h3 class=\"wp-block-heading\" id=\"h-1-preparing-the-application-for-docker\">1. Preparing the application for Docker<\/h3><p>Before you start, make sure your existing project is ready for containerization.<\/p><ul class=\"wp-block-list\">\n<li><strong>Verify package.json<\/strong>. Confirm it lists all dependencies accurately and includes a <strong>start<\/strong> script to launch the app.<\/li>\n\n\n\n<li><strong>Lock dependencies<\/strong>. Run <strong>npm install<\/strong> to create or update the <strong>package-lock.json<\/strong> file. This guarantees consistent dependency versions across environments.<\/li>\n\n\n\n<li><strong>Test locally<\/strong>. Run <strong>npm start<\/strong> on your machine to confirm everything works as expected before moving forward.<\/li>\n<\/ul><h3 class=\"wp-block-heading\" id=\"h-2-creating-a-dockerignore-file\">2. Creating a .dockerignore file<\/h3><p>A <strong>.dockerignore<\/strong> file prevents certain files and directories from being copied into your Docker image. This keeps the image small and secure.<\/p><p>Create a file named <strong>.dockerignore<\/strong> in your project root with <strong>nano<\/strong> and add the following content:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">node_modules\nnpm-debug.log\n.env\n.DS_Store\nlogs\/\ntmp\/\ndist\/\ncoverage\/<\/pre><p>This file excludes:<\/p><ul class=\"wp-block-list\">\n<li><strong>node_modules<\/strong>. The container installs dependencies, so you don&rsquo;t need the local folder.<\/li>\n\n\n\n<li><strong>npm-debug.log<\/strong>, <strong>logs\/<\/strong>, <strong>tmp\/<\/strong>. Logs and temporary files not required in production.<\/li>\n\n\n\n<li><strong>.env<\/strong>. Contains sensitive keys and should never go into an image.<\/li>\n\n\n\n<li><strong>dist\/<\/strong>, <strong>coverage\/<\/strong>. Build artifacts and test reports not needed to run the app.<\/li>\n<\/ul><p><div class=\"protip\">\n                    <h4 class=\"title\">&#128161; Pro tip<\/h4>\n                    <p>Review your <strong>.dockerignore<\/strong> file regularly to keep it aligned with your project. Always exclude sensitive information like API keys and credentials.<\/p>\n                <\/div>\n\n\n\n<\/p><h3 class=\"wp-block-heading\" id=\"h-3-writing-a-production-ready-dockerfile\">3. Writing a production-ready Dockerfile<\/h3><p>A production-ready Dockerfile optimizes your app for security and performance. It uses a minimal base image, installs only production dependencies, and sets the correct environment variables.<\/p><p>Here&rsquo;s an example:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Use a lightweight Node.js base image\nFROM node:24-alpine\n\n# Set the working directory\nWORKDIR \/usr\/src\/app\n\n# Copy package files and install only production dependencies\nCOPY package*.json .\/\nRUN npm ci --omit=dev\n\n# Copy application files\nCOPY . .\n\n# Set environment variables for production\nENV NODE_ENV=production\n\n# Expose the application port\nEXPOSE 3000\n\n# Command to start the application\nCMD [\"node\", \"app.js\"]<\/pre><p>Each instruction in the Dockerfile has a specific purpose:<\/p><figure tabindex=\"0\" class=\"wp-block-table\"><table><tbody><tr><td><strong>Command<\/strong><\/td><td><strong>Purpose<\/strong><\/td><\/tr><tr><td>FROM node:24-alpine<\/td><td>Uses a minimal Node.js base image.<\/td><\/tr><tr><td>WORKDIR \/usr\/src\/app<\/td><td>Sets the working directory for subsequent commands.<\/td><\/tr><tr><td>COPY package*.json .\/<\/td><td>Copies dependency management files into the container.<\/td><\/tr><tr><td>RUN npm ci &ndash;omit=dev<\/td><td>Installs only production dependencies to reduce image size.<\/td><\/tr><tr><td>COPY . .<\/td><td>Copies the rest of the app source code.<\/td><\/tr><tr><td>ENV NODE_ENV=production<\/td><td>Configures the app to run in production mode for better performance.<\/td><\/tr><tr><td>EXPOSE 3000<\/td><td>Documents that the container listens on port <strong>3000<\/strong>.<\/td><\/tr><tr><td>CMD [&ldquo;node&rdquo;, &ldquo;app.js&rdquo;]<\/td><td>Defines the default command to run when the container starts.<\/td><\/tr><\/tbody><\/table><\/figure><h3 class=\"wp-block-heading\" id=\"h-4-optimizing-with-multi-stage-builds\">4. Optimizing with multi-stage builds<\/h3><p>For apps that require a build step, such as transpiling TypeScript or bundling with Webpack, a multi-stage Dockerfile for Node.js works best.<\/p><p>This technique separates the build environment from the final runtime environment, resulting in a much smaller production image.<\/p><p>A multi-stage build uses multiple <strong>FROM<\/strong> instructions. The first stage builds the app and its assets, and the second stage copies only the necessary built files into a clean image.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479c04e9\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"611\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js-1024x611.png\" alt=\"The multi-stage Docker build for Node.js diagram\" class=\"wp-image-133186\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>Below is an example of a multi-stage Dockerfile:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\"># Stage 1: Build the app\nFROM node:24-alpine AS builder\nWORKDIR \/usr\/src\/app\nCOPY package*.json .\/\nRUN npm install\nCOPY . .\nRUN npm run build\n\n# Stage 2: Create the production image\nFROM node:24-alpine\nWORKDIR \/usr\/src\/app\n\n# Copy built files and production dependencies\nCOPY --from=builder \/usr\/src\/app\/dist .\/dist\nCOPY package*.json .\/\nRUN npm ci --omit=dev\n\n# Set environment variables\nENV NODE_ENV=production\n\n# Expose the app port\nEXPOSE 3000\n\n# Start the app\nCMD [\"node\", \"dist\/app.js\"]<\/pre><p>This approach produces a final image that contains only what&rsquo;s needed to run the app, making it smaller, faster, and more secure.<\/p><p>After creating your optimized Dockerfile, build and run the image with the same <strong>docker build<\/strong> and <strong>docker run<\/strong> commands as before. Now you&rsquo;re ready to test your container.<\/p><h2 class=\"wp-block-heading\" id=\"h-testing-your-node-js-docker-container\">Testing your Node.js Docker container<\/h2><p>Once your Node.js app runs inside a container, you need to test it to confirm it behaves as expected in a production-like environment.<\/p><p>Start by making a request with <a href=\"\/ca\/tutorials\/curl-command\">curl<\/a> or a web browser. In your terminal, run:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">curl http:\/\/your_vps_ip:3000<\/pre><p>This command should return the message &ldquo;Hello, Docker!&rdquo;.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479c1bb0\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"67\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl-1024x67.png\" alt=\"A terminal output after running the curl command\" class=\"wp-image-133187\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>Next, check the container logs to monitor its behavior and diagnose issues:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker logs container_id_or_name<\/pre><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69e1f479c34c5\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"71\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-on-async--load=\"callbacks.setButtonStyles\" data-wp-on-async-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/www.hostinger.com\/tutorials\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs-1024x71.png\" alt=\"A terminal output after running the docker logs command\" class=\"wp-image-133188\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on-async--click=\"actions.showLightbox\" data-wp-style--right=\"state.imageButtonRight\" data-wp-style--top=\"state.imageButtonTop\">\n\t\t\t<svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"12\" height=\"12\" fill=\"none\" viewbox=\"0 0 12 12\">\n\t\t\t\t<path fill=\"#fff\" d=\"M2 0a2 2 0 0 0-2 2v2h1.5V2a.5.5 0 0 1 .5-.5h2V0H2Zm2 10.5H2a.5.5 0 0 1-.5-.5V8H0v2a2 2 0 0 0 2 2h2v-1.5ZM8 12v-1.5h2a.5.5 0 0 0 .5-.5V8H12v2a2 2 0 0 1-2 2H8Zm2-12a2 2 0 0 1 2 2v2h-1.5V2a.5.5 0 0 0-.5-.5H8V0h2Z\"><\/path>\n\t\t\t<\/svg>\n\t\t<\/button><\/figure><\/div><p>If you don&rsquo;t know the container ID, list active containers with:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker ps<\/pre><p>For interactive debugging, open a shell inside the running container with <a href=\"\/ca\/tutorials\/docker-exec-command\">docker exec<\/a>:<\/p><pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">docker exec -it container_id_or_name sh<\/pre><p>It lets you explore the container&rsquo;s file system and troubleshoot problems directly.<\/p><p>If you run into permission issues while executing Docker commands, follow our guide on <a href=\"\/ca\/tutorials\/how-to-fix-docker-permission-denied-error\">how to fix the Docker permission denied error<\/a>.<\/p><p>Finally, add a health check. It&rsquo;s a lightweight endpoint (for example, <strong>\/health<\/strong>) that reports your app&rsquo;s status. Configure Docker to ping this endpoint periodically to confirm the container is running correctly and restart it automatically if it fails.<\/p><p>With your container fully tested, you&rsquo;re ready to apply best practices to keep it efficient and production-ready.<\/p><h2 class=\"wp-block-heading\" id=\"h-what-are-some-best-practices-for-using-node-js-with-docker\">What are some best practices for using Node.js with Docker?<\/h2><p>Following best practices helps you create secure, efficient, and maintainable Docker images for your Node.js apps. Here are some key tips to apply:<\/p><ul class=\"wp-block-list\">\n<li><strong>Use lightweight base images<\/strong>. Use Alpine or Slim images instead of full ones in production. Smaller images build faster and expose a smaller attack surface.<\/li>\n\n\n\n<li><strong>Install only production dependencies<\/strong>. Use <strong>npm ci &ndash;omit=dev<\/strong> in your Dockerfile. This avoids installing development dependencies such as testing libraries, which aren&rsquo;t needed in the final image and help keep it smaller.<\/li>\n\n\n\n<li><strong>Set NODE_ENV=production<\/strong>. This environment variable signals libraries like Express to enable optimizations such as caching view templates and reducing debug overhead.<\/li>\n\n\n\n<li><strong>Keep images small<\/strong>. Use multi-stage builds to separate build tools from the runtime environment. Add a <strong>.dockerignore<\/strong> file to exclude logs, local modules, and other unnecessary files from the build context.<\/li>\n\n\n\n<li><strong>Update base images regularly<\/strong>. Periodically pull the latest base image and rebuild your app image to include security patches and performance improvements.<\/li>\n\n\n\n<li><strong>Run as a non-root user<\/strong>. For better security, avoid running your app as root inside the container. Instead, create a dedicated user in your Dockerfile and switch to it before running the app.<\/li>\n\n\n\n<li><strong>Scale your app<\/strong>. Use orchestration tools like <a href=\"\/ca\/tutorials\/how-to-create-docker-swarm\">Docker Swarm<\/a> to distribute containers across multiple servers for high availability, or <a href=\"\/ca\/tutorials\/how-to-deploy-a-docker-stack\">Docker stack<\/a> to deploy multi-container apps in production.<\/li>\n<\/ul><h2 class=\"wp-block-heading\" id=\"h-key-takeaways-for-using-node-js-with-docker\">Key takeaways for using Node.js with Docker<\/h2><p>By combining Node.js with Docker, you create a powerful workflow for building and deploying applications that are consistent, portable, and efficient.<\/p><p>You&rsquo;ve set up a project, written an optimized Dockerfile with multi-stage builds, and applied best practices to create a production-ready container.<\/p><p>The next step is to experiment further. Integrate Docker Compose to manage multi-container apps or set up a continuous integration and deployment (CI\/CD) pipeline to automate your builds.<\/p><p>You can also use environment variables and secret files to manage sensitive data, add a <a href=\"\/ca\/tutorials\/how-to-set-up-nginx-reverse-proxy\">reverse proxy like NGINX<\/a> to enable HTTPS, or push your image to Docker Hub to share and deploy it anywhere.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using Node.js with Docker lets you build fast, scalable applications and deploy them in secure, lightweight containers. This combination provides significant benefits, such as portability &ndash; so your app runs the same way everywhere &ndash; and consistency across development and production environments. Containerizing your app also optimizes resource usage and streamlines your deployment process. Scroll [&#8230;]<\/p>\n<p><a class=\"btn btn-secondary understrap-read-more-link\" href=\"\/ca\/tutorials\/how-to-use-node-js-with-docker\">Read More&#8230;<\/a><\/p>\n","protected":false},"author":471,"featured_media":139216,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"How to use Node.js with Docker","rank_math_description":"Learn how to use Node.js with Docker to build, test, and deploy applications. This guide covers setup, Dockerfile creation, and best practices.","rank_math_focus_keyword":"node.js docker","footnotes":""},"categories":[22701,22699],"tags":[],"class_list":["post-121899","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-pre-installed-applications","category-vps"],"hreflangs":[{"locale":"en-US","link":"https:\/\/www.hostinger.com\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-UK","link":"https:\/\/www.hostinger.com\/uk\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-MY","link":"https:\/\/www.hostinger.com\/my\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-PH","link":"https:\/\/www.hostinger.com\/ph\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-IN","link":"https:\/\/www.hostinger.com\/in\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-CA","link":"https:\/\/www.hostinger.com\/ca\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-AU","link":"https:\/\/www.hostinger.com\/au\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-NG","link":"https:\/\/www.hostinger.com\/ng\/tutorials\/how-to-use-node-js-with-docker","default":0}],"_links":{"self":[{"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/posts\/121899","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/users\/471"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/comments?post=121899"}],"version-history":[{"count":28,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/posts\/121899\/revisions"}],"predecessor-version":[{"id":137290,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/posts\/121899\/revisions\/137290"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/media\/139216"}],"wp:attachment":[{"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/media?parent=121899"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/categories?post=121899"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/tags?post=121899"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}