{"id":121899,"date":"2025-01-28T17:54:15","date_gmt":"2025-01-28T17:54:15","guid":{"rendered":"\/tutorials\/?p=121899"},"modified":"2026-03-09T19:16:55","modified_gmt":"2026-03-09T19:16:55","slug":"how-to-use-node-js-with-docker","status":"publish","type":"post","link":"\/ng\/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\">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=\"\/ng\/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>\u20a613900.00\/month<\/strong>.<\/li>\n\n\n\n<li><strong>Docker running on your system<\/strong>. Hostinger customers can <a href=\"\/ng\/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=\"\/ng\/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            <div class=\"protip__heading\">\n                <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                    <path d=\"M1.49234 23.5024C1.23229 23.5024 0.972242 23.4024 0.782206 23.2123C0.562165 22.9923 0.452144 22.6822 0.502153 22.3722C0.562165 21.9221 1.14227 17.9113 3.00262 16.351C3.63274 15.8209 4.43289 15.5509 5.26305 15.5609C6.09321 15.5909 6.87335 15.9109 7.47347 16.4911C8.6937 17.6913 8.76371 19.6717 7.6435 20.9919C6.0832 22.8523 2.08245 23.4324 1.63237 23.4924C1.59236 23.4924 1.54235 23.4924 1.50234 23.4924L1.49234 23.5024ZM5.16303 17.5613C4.84297 17.5613 4.53291 17.6713 4.29287 17.8813C3.60274 18.4614 3.07264 19.9317 2.75258 21.242C4.06282 20.9219 5.5331 20.3918 6.11321 19.7017C6.55329 19.1716 6.54329 18.3814 6.0832 17.9213C5.85316 17.7013 5.5431 17.5713 5.20304 17.5613C5.19304 17.5613 5.17303 17.5613 5.16303 17.5613ZM11.7243 21.8821C11.4942 21.8821 11.2642 21.8021 11.0841 21.652C10.8541 21.462 10.7241 21.1819 10.7241 20.8819V15.9109L8.08358 13.2705H3.11264C2.81259 13.2705 2.53254 13.1404 2.3425 12.9104C2.15246 12.6803 2.07245 12.3803 2.12246 12.0902C2.19247 11.7102 2.84259 8.36953 4.70294 7.12929C6.33325 6.04909 8.96375 6.49918 10.244 6.80923C11.5442 4.96889 13.2546 3.4286 15.2349 2.33839C17.4553 1.11816 19.9858 0.518051 22.4963 0.498047C23.0464 0.498047 23.4865 0.948132 23.4865 1.49824C23.4865 5.0389 22.3763 9.97983 17.1753 13.7605C17.4853 15.0408 17.9354 17.6613 16.8552 19.2816C15.615 21.1419 12.2744 21.7921 11.8943 21.8621C11.8343 21.8721 11.7743 21.8821 11.7143 21.8821H11.7243ZM12.7245 16.181V19.6016C13.7146 19.2916 14.7948 18.7915 15.2049 18.1814C15.675 17.4812 15.605 16.091 15.385 14.9008C14.5248 15.3808 13.6346 15.8109 12.7245 16.181ZM9.66388 12.0302L11.9643 14.3307C13.1845 13.8306 14.3648 13.2204 15.485 12.5103C19.9358 9.51974 21.2361 5.60901 21.4561 2.53843C19.6157 2.67846 17.8254 3.20856 16.2051 4.09872C14.2847 5.14892 12.6544 6.68921 11.4942 8.54956C10.7841 9.65977 10.174 10.82 9.66388 12.0302ZM4.39289 11.2701H7.81353C8.1936 10.3599 8.63368 9.46974 9.11377 8.60957C7.92355 8.38953 6.51329 8.31952 5.81315 8.78961C5.19304 9.19968 4.70294 10.3099 4.39289 11.2701Z\" fill=\"#673DE6\"\/>\n                <\/svg>\n                <p class=\"protip__title\">\n                    &#128161; Pro tip                <\/p>\n            <\/div>\n            <p class=\"protip__content\">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\n<figure class=\"wp-block-image size-large\"><a class=\"hgr-tutorials-cta hgr-tutorials-cta-vps-hosting\" href=\"\/ng\/vps-hosting\" target=\"_blank\" rel=\"noreferrer noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"300\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2023\/02\/VPS-hosting-banner.png\/w=1024,h=1024,fit=scale-down\" alt=\"\" class=\"wp-image-77934\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2023\/02\/VPS-hosting-banner.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2023\/02\/VPS-hosting-banner.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2023\/02\/VPS-hosting-banner.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2023\/02\/VPS-hosting-banner.png\/w=768,fit=scale-down 768w\" 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\">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\":\"6a5892dae72a3\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892dae72a3\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2152\" height=\"726\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=1024,h=1024,fit=scale-down\" alt=\"The IPv4 information in hPanel's VPS\" class=\"wp-image-133147\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=2152,fit=scale-down 2152w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=1536,fit=scale-down 1536w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/hpanel-vps-overview-ipv4-highlighted.png\/w=2048,fit=scale-down 2048w\" sizes=\"auto, (max-width: 2152px) 100vw, 2152px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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\":\"6a5892dae7d1b\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892dae7d1b\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1856\" height=\"1028\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init.png\/w=1024,h=1024,fit=scale-down\" alt=\"A terminal output after running the mkdir, cd, and npm init commands\" class=\"wp-image-133143\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init.png\/w=1856,fit=scale-down 1856w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-mkdir-cd-npm-init.png\/w=1536,fit=scale-down 1536w\" sizes=\"auto, (max-width: 1856px) 100vw, 1856px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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=\"\/ng\/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\":\"6a5892dae854a\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892dae854a\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2560\" height=\"1493\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=1024,h=1024,fit=scale-down\" alt=\"The app.js file content viewed through the nano text editor\" class=\"wp-image-133144\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=2560,fit=scale-down 2560w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=1536,fit=scale-down 1536w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-app-js-scaled.png\/w=2048,fit=scale-down 2048w\" sizes=\"auto, (max-width: 2560px) 100vw, 2560px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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\":\"6a5892dae8e21\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892dae8e21\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2560\" height=\"1465\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=1024,h=1024,fit=scale-down\" alt=\"The Dockerfile file content viewed through the nano text editor\" class=\"wp-image-133145\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=2560,fit=scale-down 2560w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=1536,fit=scale-down 1536w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-nano-dockerfile-scaled.png\/w=2048,fit=scale-down 2048w\" sizes=\"auto, (max-width: 2560px) 100vw, 2560px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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\":\"6a5892dae94e8\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892dae94e8\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2312\" height=\"1354\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=1024,h=1024,fit=scale-down\" alt=\"A terminal output after running the docker build command\" class=\"wp-image-133146\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=2312,fit=scale-down 2312w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=1536,fit=scale-down 1536w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-build.png\/w=2048,fit=scale-down 2048w\" sizes=\"auto, (max-width: 2312px) 100vw, 2312px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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\":\"6a5892dae9c9c\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892dae9c9c\" 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--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/browser-hello-docker.png\/public\" alt='The \"Hello Docker\" message in the browser' class=\"wp-image-133148\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/browser-hello-docker.png\/w=830,fit=scale-down 830w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/browser-hello-docker.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/browser-hello-docker.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/browser-hello-docker.png\/w=768,fit=scale-down 768w\" 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--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            <div class=\"protip__heading\">\n                <svg width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\">\n                    <path d=\"M1.49234 23.5024C1.23229 23.5024 0.972242 23.4024 0.782206 23.2123C0.562165 22.9923 0.452144 22.6822 0.502153 22.3722C0.562165 21.9221 1.14227 17.9113 3.00262 16.351C3.63274 15.8209 4.43289 15.5509 5.26305 15.5609C6.09321 15.5909 6.87335 15.9109 7.47347 16.4911C8.6937 17.6913 8.76371 19.6717 7.6435 20.9919C6.0832 22.8523 2.08245 23.4324 1.63237 23.4924C1.59236 23.4924 1.54235 23.4924 1.50234 23.4924L1.49234 23.5024ZM5.16303 17.5613C4.84297 17.5613 4.53291 17.6713 4.29287 17.8813C3.60274 18.4614 3.07264 19.9317 2.75258 21.242C4.06282 20.9219 5.5331 20.3918 6.11321 19.7017C6.55329 19.1716 6.54329 18.3814 6.0832 17.9213C5.85316 17.7013 5.5431 17.5713 5.20304 17.5613C5.19304 17.5613 5.17303 17.5613 5.16303 17.5613ZM11.7243 21.8821C11.4942 21.8821 11.2642 21.8021 11.0841 21.652C10.8541 21.462 10.7241 21.1819 10.7241 20.8819V15.9109L8.08358 13.2705H3.11264C2.81259 13.2705 2.53254 13.1404 2.3425 12.9104C2.15246 12.6803 2.07245 12.3803 2.12246 12.0902C2.19247 11.7102 2.84259 8.36953 4.70294 7.12929C6.33325 6.04909 8.96375 6.49918 10.244 6.80923C11.5442 4.96889 13.2546 3.4286 15.2349 2.33839C17.4553 1.11816 19.9858 0.518051 22.4963 0.498047C23.0464 0.498047 23.4865 0.948132 23.4865 1.49824C23.4865 5.0389 22.3763 9.97983 17.1753 13.7605C17.4853 15.0408 17.9354 17.6613 16.8552 19.2816C15.615 21.1419 12.2744 21.7921 11.8943 21.8621C11.8343 21.8721 11.7743 21.8821 11.7143 21.8821H11.7243ZM12.7245 16.181V19.6016C13.7146 19.2916 14.7948 18.7915 15.2049 18.1814C15.675 17.4812 15.605 16.091 15.385 14.9008C14.5248 15.3808 13.6346 15.8109 12.7245 16.181ZM9.66388 12.0302L11.9643 14.3307C13.1845 13.8306 14.3648 13.2204 15.485 12.5103C19.9358 9.51974 21.2361 5.60901 21.4561 2.53843C19.6157 2.67846 17.8254 3.20856 16.2051 4.09872C14.2847 5.14892 12.6544 6.68921 11.4942 8.54956C10.7841 9.65977 10.174 10.82 9.66388 12.0302ZM4.39289 11.2701H7.81353C8.1936 10.3599 8.63368 9.46974 9.11377 8.60957C7.92355 8.38953 6.51329 8.31952 5.81315 8.78961C5.19304 9.19968 4.70294 10.3099 4.39289 11.2701Z\" fill=\"#673DE6\"\/>\n                <\/svg>\n                <p class=\"protip__title\">\n                    &#128161; Pro tip                <\/p>\n            <\/div>\n            <p class=\"protip__content\">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\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\":\"6a5892daeade1\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892daeade1\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"2152\" height=\"1285\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=1024,h=1024,fit=scale-down\" alt=\"The multi-stage Docker build for Node.js diagram\" class=\"wp-image-133186\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=2152,fit=scale-down 2152w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=1536,fit=scale-down 1536w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/multi-stage-docker-build-node-js.png\/w=2048,fit=scale-down 2048w\" sizes=\"auto, (max-width: 2152px) 100vw, 2152px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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=\"\/ng\/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\":\"6a5892daeb5ad\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892daeb5ad\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1970\" height=\"128\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl.png\/w=1024,h=1024,fit=scale-down\" alt=\"A terminal output after running the curl command\" class=\"wp-image-133187\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl.png\/w=1970,fit=scale-down 1970w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-curl.png\/w=1536,fit=scale-down 1536w\" sizes=\"auto, (max-width: 1970px) 100vw, 1970px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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\":\"6a5892daebd5a\"}' data-wp-interactive=\"core\/image\" data-wp-key=\"6a5892daebd5a\" class=\"aligncenter size-large wp-lightbox-container\"><img loading=\"lazy\" decoding=\"async\" width=\"1816\" height=\"126\" data-wp-class--hide=\"state.isContentHidden\" data-wp-class--show=\"state.isContentVisible\" data-wp-init=\"callbacks.setButtonStyles\" data-wp-on--click=\"actions.showLightbox\" data-wp-on--load=\"callbacks.setButtonStyles\" data-wp-on-window--resize=\"callbacks.setButtonStyles\" src=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs.png\/w=1024,h=1024,fit=scale-down\" alt=\"A terminal output after running the docker logs command\" class=\"wp-image-133188\" srcset=\"https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs.png\/w=1816,fit=scale-down 1816w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs.png\/w=300,fit=scale-down 300w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs.png\/w=1024,fit=scale-down 1024w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs.png\/w=150,fit=scale-down 150w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs.png\/w=768,fit=scale-down 768w, https:\/\/imagedelivery.net\/LqiWLm-3MGbYHtFuUbcBtA\/wp-content\/uploads\/sites\/2\/2025\/01\/terminal-docker-logs.png\/w=1536,fit=scale-down 1536w\" sizes=\"auto, (max-width: 1816px) 100vw, 1816px\" \/><button class=\"lightbox-trigger\" type=\"button\" aria-haspopup=\"dialog\" aria-label=\"Enlarge\" data-wp-init=\"callbacks.initTriggerButton\" data-wp-on--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=\"\/ng\/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=\"\/ng\/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=\"\/ng\/tutorials\/how-to-create-docker-swarm\/\">Docker Swarm<\/a> to distribute containers across multiple servers for high availability, or <a href=\"\/ng\/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=\"\/ng\/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=\"\/ng\/tutorials\/how-to-use-node-js-with-docker\/\">Read More&#8230;<\/a><\/p>\n","protected":false},"author":471,"featured_media":143508,"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":[22644],"tags":[],"class_list":["post-121899","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-vps"],"hreflangs":[{"locale":"en-US","link":"https:\/\/www.hostinger.com\/tutorials\/how-to-use-node-js-with-docker","default":0},{"locale":"en-GB","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\/ng\/tutorials\/wp-json\/wp\/v2\/posts\/121899","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/users\/471"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/comments?post=121899"}],"version-history":[{"count":28,"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/posts\/121899\/revisions"}],"predecessor-version":[{"id":143507,"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/posts\/121899\/revisions\/143507"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/media\/143508"}],"wp:attachment":[{"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/media?parent=121899"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/categories?post=121899"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.hostinger.com\/ng\/tutorials\/wp-json\/wp\/v2\/tags?post=121899"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}