{"id":143360,"date":"2026-04-08T01:47:33","date_gmt":"2026-04-08T01:47:33","guid":{"rendered":"https:\/\/www.hostinger.com\/ca\/tutorials\/deploy-node-js-application\/"},"modified":"2026-04-08T01:47:33","modified_gmt":"2026-04-08T01:47:33","slug":"deploy-node-js-application","status":"publish","type":"post","link":"\/ca\/tutorials\/deploy-node-js-application","title":{"rendered":"How to deploy a Node.js application"},"content":{"rendered":"<p>To deploy a Node.js application, you can either follow a general deployment workflow that works across most hosting environments or use hosting-specific options like web app hosting or a virtual private server (VPS).<\/p><p>Node.js deployment means moving your application from a local development machine to a production server where users can access it over the internet.<\/p><p>Proper deployment directly affects how your Node.js application performs under real traffic. A misconfigured server can cause slow response times, unexpected downtime, or security vulnerabilities that expose sensitive data.<\/p><p>Setting up the Node.js production environment correctly from the start helps prevent these issues and keeps the application stable as traffic grows.<\/p><p>The typical Node.js deployment process includes five stages: preparation, hosting selection, environment configuration, deployment, and monitoring.<\/p><p>Here&rsquo;s what each stage involves:<\/p><ol class=\"wp-block-list\">\n<li><strong>Prepare the production application.<\/strong> Define dependencies in <strong>package.json<\/strong>, configure environment variables, and add a start script.<\/li>\n\n\n\n<li><strong>Commit and push the code to Git.<\/strong> Upload files to the repository using the browser interface or run Git CLI commands from your computer.<\/li>\n\n\n\n<li><strong>Set up Node.js and a process manager.<\/strong> Install Node.js on the server, configure environment variables, and use PM2 to keep the application running after crashes or reboots.<\/li>\n\n\n\n<li><strong>Route traffic through a reverse proxy.<\/strong> Configure NGINX to forward requests to the Node.js process on its internal port and secure connections with an SSL certificate.<\/li>\n\n\n\n<li><strong>Verify and monitor the deployment.<\/strong> Check the application in the browser, review PM2 logs, and monitor performance metrics to detect issues early.<\/li>\n<\/ol><p><\/p><h2 class=\"wp-block-heading\" id=\"h-how-to-deploy-a-node-js-application\"><strong>How to deploy a Node.js application<\/strong><\/h2><p>Deploying a <a href=\"\/ca\/tutorials\/what-is-node-js\">Node.js<\/a> application follows a universal workflow that works regardless of the hosting provider.<\/p><p>The core process includes preparing the app for production, pushing the code to Git, setting up the server with a process manager, configuring a reverse proxy with HTTPS, and testing the live deployment.<\/p><h3 class=\"wp-block-heading\"><strong>1. <\/strong><strong>Prepare the Node.js application for deployment<\/strong><\/h3><p>Preparing a Node.js application for deployment means making sure the project includes everything a production server needs to install dependencies and start the app automatically.<\/p><p>Three files form the foundation of a deployment-ready Node.js project: <strong>app.js<\/strong> (the main application file), <strong>package.json<\/strong> (project metadata, dependencies, and scripts), and <strong>.gitignore<\/strong> (files excluded from version control).<\/p><p>Every package your application imports must appear under the <strong>dependencies<\/strong> field in <strong>package.json<\/strong>. The file tells <strong>npm<\/strong> install which packages to download on the server.<\/p><p>If a required package is missing, the application will crash in production even if it worked locally, where the package already existed in <strong>node_modules<\/strong>. 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=\"\">{\n   \"name\": \"node-deploy-test\",\n   \"version\": \"1.0.0\",\n   \"description\": \"Sample Node.js app for deployment tutorial\",\n   \"main\": \"app.js\",\n   \"scripts\": {\n      \"start\": \"node app.js\"\n   },\n   \"dependencies\": {\n      \"express\": \"^4.21.0\"\n   }\n}<\/pre><p>The scripts section defines how hosting platforms and process managers start the application. The <strong>&ldquo;start&rdquo;: &ldquo;node app.js&rdquo;<\/strong> entry tells the server to run <strong>app.js<\/strong> as the entry point.<\/p><p>Node.js environment variables store configuration values that change between environments, such as development and production. Use them for values such as port numbers, database credentials, and API keys, rather than hardcoding them in source files.<\/p><p>In the application code, reference these values using <strong>process.env<\/strong>:<\/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 PORT = process.env.PORT || 3000;<\/pre><p>This line reads the <strong>PORT<\/strong> variable if it exists. Otherwise, it defaults to <strong>3000<\/strong>. The same pattern applies to <strong>NODE_ENV<\/strong>, which hosting platforms usually set to production automatically.<\/p><p>Keeping configuration in environment variables prevents sensitive data from leaking into your repository. It also lets you change settings for each environment without modifying the code.<\/p><h3 class=\"wp-block-heading\"><strong>2. Push the application code to a Git repository<\/strong><\/h3><p>Version control is essential for Node.js deployment because it creates a reliable, trackable record of every change to the codebase.<\/p><p>If a deployment introduces a bug, you can revert to the last working commit in seconds, rather than blindly troubleshooting.<\/p><p>Git is the standard version control system, and GitHub is the most common platform for hosting Git repositories. Start by creating a repository on GitHub:<\/p><ol class=\"wp-block-list\">\n<li>Log in to GitHub, or create an account at <strong>github.com<\/strong> if you don&rsquo;t have one.<\/li>\n\n\n\n<li>Click <strong>New<\/strong> on the dashboard, then enter a repository name, for example, <strong>node-deploy-test<\/strong>.<\/li>\n\n\n\n<li>Set the visibility to <strong>Public<\/strong> if you plan to connect the repository to a hosting platform later.<\/li>\n\n\n\n<li>Leave the other options unchecked (no <strong>README<\/strong>, no <strong>.gitignore<\/strong>, no license), then click <strong>Create repository<\/strong>.<\/li>\n<\/ol><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f5674f\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/github-create-a-new-repository-1010x1024.png\" alt=\"The Create repository menu in GitHub\" class=\"wp-image-145249\" title=\"github-create-a-new-repository\"><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>The simplest way to add files is through the browser:<\/p><ol class=\"wp-block-list\">\n<li>On the repository setup page, click <strong>uploading an existing file<\/strong>.<\/li>\n\n\n\n<li>Drag <strong>app.js<\/strong> and <strong>package.json<\/strong> into the upload area.<\/li>\n\n\n\n<li>Click <strong>Commit changes<\/strong>.<\/li>\n<\/ol><p>Keep in mind that most operating systems hide dotfiles like <strong>.gitignore<\/strong>, so drag-and-drop uploads won&rsquo;t work. Create the file directly on GitHub instead:<\/p><ol class=\"wp-block-list\">\n<li>On the repository&rsquo;s main page, click <strong>Add file<\/strong> &rarr; <strong>Create new file<\/strong>.<\/li>\n\n\n\n<li>Type <strong>.gitignore<\/strong> as the filename and add the following line as its content:<\/li>\n<\/ol><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\/<\/pre><ol start=\"3\" class=\"wp-block-list\">\n<li>Click <strong>Commit changes<\/strong>.<\/li>\n<\/ol><p>Alternatively, if Git is installed on your computer, you can push all your files, including <strong>.gitignore<\/strong>, using <a href=\"\/ca\/tutorials\/basic-git-commands\">Git CLI commands<\/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=\"\">cd path\/to\/node-deploy-test\ngit init\ngit add .\ngit commit -m \"Initial commit\"\ngit remote add origin https:\/\/github.com\/your-username\/node-deploy-test.git\ngit push -u origin main<\/pre><p>Replace <strong>your-username<\/strong> with your actual GitHub username.<\/p><p>The Git CLI method works better for ongoing development. Running <strong>git push<\/strong> sends only the changes, while browser uploads require you to upload files again.<\/p><h3 class=\"wp-block-heading\"><strong>3. Set up the server and run the application<\/strong><\/h3><p>Setting up the server starts with installing Node.js. You can install it using Node Version Manager (NVM) or a system package manager.<\/p><p>NVM is the better option because it lets you install and switch between multiple Node.js versions without requiring root permissions.<\/p><p>Node.js processes stop when the terminal session closes or when the server reboots. This means a crash can take the application offline until someone manually restarts it.<\/p><p>PM2, a production-grade Node.js process manager, solves this problem. It runs the application as a background daemon and automatically restarts it after failures or server reboots.<\/p><p>At this point, the application runs internally on a port such as <strong>3000<\/strong>, but users still can&rsquo;t access it through standard web ports like <strong>80<\/strong> or <strong>443<\/strong>. To expose the application to the public internet, you need to set up a reverse proxy.<\/p><h3 class=\"wp-block-heading\"><strong>4. Configure a reverse proxy and enable HTTPS<\/strong><\/h3><p>A reverse proxy server like NGINX routes public traffic to the Node.js application running on an internal port.<\/p><p>NGINX listens on ports <strong>80<\/strong> (HTTP) and <strong>443<\/strong> (HTTPS), accepts incoming requests, and forwards them to <strong>localhost:3000<\/strong> or to whichever port your app uses. Users never interact with the Node.js process directly.<\/p><p>This setup also serves as the termination point for SSL\/TLS connections, keeping encryption management separate from your application code.<\/p><p>After configuring the reverse proxy, enable HTTPS using Certbot with a Let&rsquo;s Encrypt SSL certificate.<\/p><h3 class=\"wp-block-heading\"><strong>5. Test and monitor the deployed application<\/strong><\/h3><p>After deployment, test the Node.js application by opening a browser and visiting your application&rsquo;s root URL, for example, <strong>http:\/\/domain.tld<\/strong>. The main page should load with the expected content.<\/p><p>If you added a health check route, append <strong>\/health<\/strong> to your application&rsquo;s URL and confirm it returns JSON output like:<\/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=\"\">{\"status\":\"healthy\",\"uptime\":...}<\/pre><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f580ec\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/node-js-app-health-check-route-1024x181.png\" alt=\"The health check route for a Node.js application\" class=\"wp-image-145259\" title=\"node-js-app-health-check-route\"><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 Node.js logs using your process manager. For PM2, the<strong> pm2 logs<\/strong> command shows a real-time stream of your application&rsquo;s output and error messages.<\/p><p>For a broader view, run <strong>pm2 monit<\/strong> to open an interactive dashboard. It shows CPU usage, memory consumption, event loop latency, and logs in one place.<\/p><p>As a baseline, a healthy Node.js application typically stays under 50 ms event loop latency, uses around 300&ndash;500 MB of memory for a standard Express app, and keeps CPU usage below 70% under normal traffic.<\/p><h2 class=\"wp-block-heading\" id=\"h-how-to-deploy-a-node-js-application-using-web-app-hosting\"><strong>How to deploy a Node.js application using web app hosting<\/strong><\/h2><p>Deploying a Node.js application with web app hosting involves connecting a Git repository, defining build commands, and triggering deployment from a dashboard.<\/p><p>Hostinger Node.js web app hosting provides a managed hosting environment that lets you deploy applications without setting up a server from scratch.<\/p><p>It handles the underlying infrastructure, including server configuration, Node.js installation, process management, and SSL certificates.<\/p><p>It also supports automatic redeployment, so when you push changes to GitHub, the system rebuilds and redeploys your application automatically.<\/p><h3 class=\"wp-block-heading\"><strong>1. Create a Node.js web app hosting environment<\/strong><\/h3><p>After purchasing a <a href=\"\/ca\/web-apps-hosting\">Node.js hosting plan<\/a>, log in to hPanel using your Hostinger account to create a new application.<\/p><p>Go to <strong>Websites<\/strong> &rarr; <strong>Add website<\/strong> &rarr; <strong>Node.js Web App<\/strong>.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f5970d\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-websites-node-js-web-app-highlighted-1024x614.png\" alt=\"The Node.js Web App option in hPanel's Websites menu\" class=\"wp-image-145258\" title=\"hpanel-websites-node-js-web-app-highlighted\"><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 have a <strong>Business<\/strong> plan or higher on regular web hosting, you can follow the same steps to create a Node.js application.<\/p><?xml encoding=\"utf-8\" ?><figure class=\"wp-block-image size-full\"><a class=\"hgr-tutorials-cta hgr-tutorials-cta-web-hosting\" href=\"\/ca\/web-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\/11\/Web-hosting_in-text-banner.png\" alt=\"Hostinger web hosting banner\" class=\"wp-image-98604\"  sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure><h3 class=\"wp-block-heading\"><strong>2. Connect the Git repository<\/strong><\/h3><p>Connecting a Git repository links your GitHub project to the Node.js web app hosting environment.<\/p><p>Once connected, every time you update your code on GitHub, the platform pulls the latest version, installs dependencies, and restarts the application automatically.<\/p><p>To connect the repository:<\/p><ol class=\"wp-block-list\">\n<li>Select <strong>Import Git Repository<\/strong> on the setup screen.<\/li>\n<\/ol><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f5c5fe\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-deploy-your-node-js-web-app-connect-with-github-1024x576.png\" alt=\"The Connect with GitHub option in hPanel's Node.js web app deployment menu\" class=\"wp-image-145250\" title=\"hpanel-deploy-your-node-js-web-app-connect-with-github\"><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><ol start=\"2\" class=\"wp-block-list\">\n<li>GitHub redirects you to an authorization page. Click <strong>Authorize<\/strong> to grant Hostinger access.<\/li>\n\n\n\n<li>Select the repository from the list.<\/li>\n<\/ol><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f5e3ba\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-select-git-repository-to-import-1024x609.png\" alt=\"The Select Git repository to import dialog in hPanel's Node.js web app deployment menu\" class=\"wp-image-145254\" title=\"hpanel-select-git-repository-to-import\"><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>A single hosting plan connects to one GitHub account at a time. All Node.js websites on that plan share the same connection.<\/p><p>If the project files aren&rsquo;t in a Git repository, select <strong>Upload your website files<\/strong> instead and upload a compressed ZIP file. This method doesn&rsquo;t support automatic redeployment when you update your code.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f60f9d\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-upload-your-app-files-1024x417.png\" alt=\"The Upload your app files option in hPanel's Node.js web app deployment menu\" class=\"wp-image-145255\" title=\"hpanel-upload-your-app-files\"><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\"><strong>3. Configure build and start commands<\/strong><\/h3><p>After you select the repository, the system automatically detects the framework and suggests build settings.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f62f26\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-review-build-settings-1024x686.png\" alt=\"The Review build settings dialog in hPanel's Node.js web app deployment menu\" class=\"wp-image-145253\" title=\"hpanel-review-build-settings\"><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>You can adjust these settings if your project needs different values:<\/p><ul class=\"wp-block-list\">\n<li><strong>Framework preset.<\/strong> The detected framework for your project, for example, Express.js. Change it if auto-detection selects the wrong one.<\/li>\n\n\n\n<li><strong>Node.js version.<\/strong> The runtime version for your application. The system detects this from the engines field in <strong>package.json<\/strong>, but you can override it manually.<\/li>\n\n\n\n<li><strong>Entry file.<\/strong> The file the server runs to start the application. It defaults to <strong>app.js<\/strong>, based on the Node.js start command in <strong>package.json<\/strong>.<\/li>\n\n\n\n<li><strong>Package manager.<\/strong> The tool used to install dependencies. By default, it&rsquo;s <strong>npm<\/strong>, but you can switch to <strong>Yarn<\/strong> or <strong>pnpm<\/strong> if your project uses a different lock file.<\/li>\n\n\n\n<li><strong>Environment variables.<\/strong> Production values like API keys or database credentials. Add them here instead of committing them to the repository.<\/li>\n<\/ul><h3 class=\"wp-block-heading\"><strong>4. Launch and verify the deployed application<\/strong><\/h3><p>Click <strong>Deploy<\/strong> to start the build. The system installs dependencies, runs the build command, and starts the Node.js application.<\/p><p>Once deployment finishes, you should see a &ldquo;Deployment completed&rdquo; message.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f6490f\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-deployment-completed-1024x789.png\" alt=\"The Deployment completed dialog in hPanel's Node.js web app deployment\" class=\"wp-image-145251\"><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>Click <strong>Go to Dashboard<\/strong> and open the temporary domain in a new browser tab to confirm the application loads correctly.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f66567\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-node-js-websites-temporary-domain-highlighted-1024x468.png\" alt=\"The temporary domain in the Node.js dashboard of hPanel\" class=\"wp-image-145252\" title=\"hpanel-node-js-websites-temporary-domain-highlighted\"><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>The Node.js dashboard in hPanel provides a quick overview of the deployed application, so you can monitor performance and manage settings.<\/p><ul class=\"wp-block-list\">\n<li><strong>GitHub repository link.<\/strong> Opens the connected repository in a new tab so you can review code, branches, or recent commits.<\/li>\n\n\n\n<li><strong>Last deployment details.<\/strong> Shows the deployment status (<strong>success<\/strong> or <strong>failure<\/strong>), the timestamp, and a link to the full deployment log.<\/li>\n\n\n\n<li><strong>Quick links.<\/strong> Jump directly to <strong>Deployments<\/strong>, <strong>Environment Variables<\/strong>, <strong>Settings &amp; Redeploy<\/strong>, or <strong>File Manager<\/strong>.<\/li>\n\n\n\n<li><strong>Resource usage graphs.<\/strong> Show average CPU, RAM, and I\/O usage. If any metric approaches the red dotted line, which marks your plan&rsquo;s limit, consider upgrading.<\/li>\n<\/ul><h2 class=\"wp-block-heading\" id=\"h-how-to-deploy-a-node-js-application-using-vps-hosting\"><strong>How to deploy a Node.js application using VPS hosting<\/strong><\/h2><p>Deploying a Node.js application on a VPS involves connecting to a remote server via SSH, installing Node.js, uploading your application code, setting up a process manager and reverse proxy, and enabling HTTPS.<\/p><p><a href=\"\/ca\/vps-hosting\">VPS hosting<\/a> gives you full control over the server environment so that you can configure everything from the operating system to firewall rules.<\/p><p>This approach requires more setup than managed web app hosting. Still, it gives you direct access to the server for custom configurations, OS-level debugging, and running other services alongside your Node.js application.<\/p><?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><h3 class=\"wp-block-heading\"><strong>1. Create a VPS instance<\/strong><\/h3><p>Start by creating a VPS instance and connecting to it via SSH. Choose the most recent Ubuntu long-term support (LTS) version, such as <strong>24.04<\/strong>, as the operating system.<\/p><p>Then connect using an <a href=\"\/ca\/tutorials\/how-to-use-putty-ssh\">SSH client like PuTTY<\/a> or hPanel&rsquo;s browser terminal.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f6ab06\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-vps-overview-terminal-highlighted-1024x391.png\" alt=\"The browser terminal button in hPanel's VPS dashboard\" class=\"wp-image-145257\" title=\"hpanel-vps-overview-terminal-highlighted\"><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 that, complete the initial <a href=\"\/ca\/tutorials\/how-to-set-up-vps\">VPS server setup<\/a> by updating system packages, configuring the firewall using <strong>ufw<\/strong>, and creating a non-root user with sudo privileges for daily use.<\/p><h3 class=\"wp-block-heading\"><strong>2. <\/strong><strong>Install Node.js on the server<\/strong><\/h3><p>To <a href=\"\/ca\/tutorials\/how-to-install-nodejs-ubuntu\">install Node.js on Ubuntu<\/a>, you can use NVM or a system package manager. Follow these steps if you choose the former:<\/p><ol class=\"wp-block-list\">\n<li>Run the NVM install script:<\/li>\n<\/ol><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 -o- https:\/\/raw.githubusercontent.com\/nvm-sh\/nvm\/v0.40.2\/install.sh | bash<\/pre><ol start=\"2\" class=\"wp-block-list\">\n<li>Close and reopen the terminal to load NVM into the current session.<\/li>\n\n\n\n<li>Install the latest LTS version of Node.js:<\/li>\n<\/ol><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=\"\">nvm install --lts<\/pre><ol start=\"4\" class=\"wp-block-list\">\n<li>Verify that Node.js and <strong>npm<\/strong> are installed correctly:<\/li>\n<\/ol><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 -v<\/pre><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 -v<\/pre><p>Both commands should return version numbers like this:<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f6d073\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-full wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-node-v-npm-v.png\" alt=\"The node -v and npm -v commands' output in the terminal\" class=\"wp-image-145262\" title=\"terminal-node-v-npm-v\"><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\"><strong>3. Upload the Node.js application to the server<\/strong><\/h3><p>If you&rsquo;ve already pushed your application to a GitHub repository, you can transfer the code to the server using Git:<\/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=\"\">cd \/var\/www\ngit clone https:\/\/github.com\/your-username\/your-repository.git\ncd your-repository\nnpm install<\/pre><p>Replace <strong>your-username<\/strong> and <strong>your-repository<\/strong> with your actual GitHub username and repository name.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f6ed22\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-git-clone-npm-install-1024x581.png\" alt=\"The git clone and npm install commands' output in the terminal\" class=\"wp-image-145260\" title=\"terminal-git-clone-npm-install\"><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>The <strong>npm install<\/strong> command reads <strong>package.json<\/strong> and installs all dependencies into the <strong>node_modules<\/strong> folder on the server.<\/p><p>Alternatively, you can transfer files directly using the <a href=\"\/ca\/tutorials\/linux-scp-command\">scp command<\/a>. Open a new terminal window on your computer (not the one connected to the server) and 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=\"\">scp -r \/local\/path\/node-deploy-test root@192.0.2.1:\/var\/www\/<\/pre><p>Replace <strong>\/local\/path\/node-deploy-test<\/strong> with the path to your project folder, <strong>root<\/strong> with your server username, and <strong>192.0.2.1<\/strong> with your server&rsquo;s IP address.<\/p><p>Hostinger VPS customers can find their server&rsquo;s IP address in hPanel by going to <strong>VPS &rarr; Manage &rarr; Overview &rarr; VPS details<\/strong>.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f70313\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/hpanel-vps-overview-ipv4-highlighted-1024x338.png\" alt=\"The IPv4 information in the VPS dashboard of hPanel\" class=\"wp-image-145256\" title=\"hpanel-vps-overview-ipv4-highlighted\"><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 the transfer finishes, switch back to the server terminal and run npm install inside the project directory.<\/p><p>Then set the required environment variables before starting the application:<\/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=\"\">export NODE_ENV=production\nexport PORT=3000<\/pre><p><strong>NODE_ENV=production<\/strong> tells frameworks to optimize for performance and disable development features. <strong>PORT<\/strong> should match the port your application listens on, as configured in <strong>app.js<\/strong>.<\/p><h3 class=\"wp-block-heading\"><strong>4. Configure PM2 and reverse proxy<\/strong><\/h3><p>Start your Node.js application with PM2 to keep it running in the background.<\/p><ol class=\"wp-block-list\">\n<li>Install PM2 and start the application:<\/li>\n<\/ol><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 install -g pm2\npm2 start app.js --name \"node-app\"<\/pre><ol start=\"2\" class=\"wp-block-list\">\n<li>Configure PM2 to restart the application after a server reboot:<\/li>\n<\/ol><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=\"\">pm2 startup\npm2 save<\/pre><p><strong>pm2 startup<\/strong> generates a system service that launches PM2 on boot. <strong>pm2 save<\/strong> persists the current process list so PM2 knows which applications to restart.<\/p><ol start=\"3\" class=\"wp-block-list\">\n<li>Open firewall ports for web traffic. Otherwise, NGINX won&rsquo;t receive incoming connections:<\/li>\n<\/ol><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=\"\">ufw allow http\nufw allow https<\/pre><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f71c5b\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-ufw-allow-http-ufw-allow-https-1024x247.png\" alt=\"The ufw allow http and ufw allow https commands' output in the terminal\" class=\"wp-image-145267\" title=\"terminal-ufw-allow-http-ufw-allow-https\"><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><ol start=\"4\" class=\"wp-block-list\">\n<li>Install NGINX on your server:<\/li>\n<\/ol><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=\"\">apt update\napt install nginx -y<\/pre><ol start=\"5\" class=\"wp-block-list\">\n<li><a href=\"\/ca\/tutorials\/how-to-set-up-nginx-reverse-proxy\">Create an NGINX reverse proxy<\/a> configuration for your Node.js deployment:<\/li>\n<\/ol><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 \/etc\/nginx\/sites-available\/node-app<\/pre><ol start=\"6\" class=\"wp-block-list\">\n<li>Add the following configuration, replacing <strong>your-hostname<\/strong> with your own domain name or server hostname:<\/li>\n<\/ol><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=\"\">server {\n   listen 80;\n   server_name your-hostname;\n\n   location \/ {\n      proxy_pass http:\/\/localhost:3000;\n      proxy_http_version 1.1;\n      proxy_set_header Upgrade $http_upgrade;\n      proxy_set_header Connection 'upgrade';\n      proxy_set_header Host $host;\n      proxy_cache_bypass $http_upgrade;\n   }\n}<\/pre><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f7310e\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-nano-node-app-1024x598.png\" alt=\"The node-app file's content in the nano text editor\" class=\"wp-image-145261\" title=\"terminal-nano-node-app\"><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>This configuration forwards all incoming HTTP requests to the Node.js process on port <strong>3000<\/strong>. The <strong>proxy_set_header<\/strong> directives preserve client information and enable WebSocket connections.<\/p><ol start=\"7\" class=\"wp-block-list\">\n<li>Save the file and exit the editor with <strong>Ctrl + O<\/strong> &rarr; <strong>Enter<\/strong> &rarr; <strong>Ctrl + X<\/strong>.<\/li>\n\n\n\n<li>Enable the configuration and remove the default site:<\/li>\n<\/ol><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=\"\">ln -s \/etc\/nginx\/sites-available\/node-app \/etc\/nginx\/sites-enabled\/\nrm \/etc\/nginx\/sites-enabled\/default<\/pre><ol start=\"9\" class=\"wp-block-list\">\n<li>Test the configuration and reload NGINX. The command should return &ldquo;syntax is ok&rdquo; and &ldquo;test is successful&rdquo;:<\/li>\n<\/ol><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=\"\">nginx -t\nsystemctl reload nginx<\/pre><ol start=\"10\" class=\"wp-block-list\">\n<li>Visit your application&rsquo;s URL in your browser, without a port number. It should load correctly.<\/li>\n<\/ol><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f74761\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/chrome-node-js-application-1024x679.png\" alt=\"The Node.js application in the browser\" class=\"wp-image-145248\" title=\"chrome-node-js-application\"><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><ol start=\"11\" class=\"wp-block-list\">\n<li>Enable HTTPS with Certbot. Make sure your domain or hostname points to the server&rsquo;s IP address, since Certbot can&rsquo;t issue certificates for IP addresses.<\/li>\n<\/ol><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=\"\">apt install certbot python3-certbot-nginx -y\ncertbot --nginx -d your-hostname<\/pre><ol start=\"12\" class=\"wp-block-list\">\n<li>Follow the prompts to enter an email address and accept the terms. Certbot verifies domain ownership, issues the certificate, and automatically updates the NGINX configuration to redirect HTTP traffic to HTTPS.<\/li>\n<\/ol><p>Once done, visit your application&rsquo;s URL again and confirm that the &ldquo;Connection is secure&rdquo; message appears in your browser.<\/p><h3 class=\"wp-block-heading\"><strong>5. Run and verify the production application<\/strong><\/h3><p>Visit your Node.js application&rsquo;s URL and confirm the page loads with the expected content. If it doesn&rsquo;t load, check that NGINX is running with <strong>systemctl status nginx<\/strong> and that PM2 shows the app as &ldquo;online&rdquo; with <strong>pm2 list<\/strong>.<\/p><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f75c05\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-pm2-list-1024x241.png\" alt=\"The pm2 list command's output in the terminal\" class=\"wp-image-145263\" title=\"terminal-pm2-list\"><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>Once the application is live, use PM2 to monitor its runtime behavior:<\/p><ul class=\"wp-block-list\">\n<li><strong>pm2 logs<\/strong>. Streams real-time application output and errors. Open the app in your browser while this command runs to confirm new log entries appear.<\/li>\n<\/ul><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f77362\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-pm2-logs-1024x379.png\" alt=\"The pm2 logs command's output in the terminal\" class=\"wp-image-145264\" title=\"terminal-pm2-logs\"><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><ul class=\"wp-block-list\">\n<li><strong>pm2 monit<\/strong>. Opens an interactive dashboard that shows CPU usage, memory consumption, event loop latency, and logs in one view.<\/li>\n<\/ul><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f787d0\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-pm2-monit-1024x554.png\" alt=\"The pm2 monit command's log in the terminal\" class=\"wp-image-145265\" title=\"terminal-pm2-monit\"><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><ul class=\"wp-block-list\">\n<li><strong>pm2 show node-app<\/strong>. Displays detailed information, including uptime, restart count, memory usage, script path, and log file locations.<\/li>\n<\/ul><div class=\"wp-block-image\"><figure data-wp-context='{\"imageId\":\"69d6822f7a4af\"}' data-wp-interactive=\"core\/image\" class=\"aligncenter size-large wp-lightbox-container\"><img decoding=\"async\" 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\/2026\/04\/terminal-pm2-show-node-app-899x1024.png\" alt=\"The pm2 show node-app command's log in the terminal\" class=\"wp-image-145266\" title=\"terminal-pm2-show-node-app\"><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>Press <strong>Ctrl + C<\/strong> to exit any of these views.<\/p><h2 class=\"wp-block-heading\" id=\"h-what-are-the-best-practices-for-node-js-deployment\"><strong>What are the best practices for Node.js deployment?<\/strong><\/h2><p>Best practices for Node.js deployment focus on automation, environment separation, and proactive monitoring to keep production applications stable and secure.<\/p><ul class=\"wp-block-list\">\n<li><strong>Set up a <\/strong><strong>CI\/CD<\/strong><strong> pipeline.<\/strong> Use tools like GitHub Actions, GitLab CI\/CD, or Jenkins to run your test suite on every commit and automatically deploy when all tests pass. This removes manual deployment steps and helps catch bugs before they reach production.<\/li>\n\n\n\n<li><strong>Separate configuration from code.<\/strong> Store environment-specific values in environment variables or external config files that you don&rsquo;t commit to the repository. Use a <strong>.env<\/strong> file locally, loaded with a library like <strong>dotenv<\/strong>, and set variables directly on the server or through your hosting platform&rsquo;s dashboard in production.<\/li>\n\n\n\n<li><strong>Use structured logging.<\/strong> Log in JSON format so tools like the ELK Stack (Elasticsearch, Logstash, Kibana) or Datadog can parse and search logs efficiently. Include a timestamp, log level, and request ID in each entry to trace issues across requests.<\/li>\n\n\n\n<li><strong>Set up automated health checks.<\/strong> Use a monitoring service like UptimeRobot or Pingdom to ping your application&rsquo;s health endpoint every 30&ndash;60 seconds. If the endpoint fails or returns an error, the service sends alerts through email, Slack, or SMS so you can respond before users notice. As a baseline, aim to keep p95 response time under 200 ms.<\/li>\n\n\n\n<li><strong>Enable automated rollbacks.<\/strong> Configure your CI\/CD pipeline to redeploy the last stable version if a new release fails health checks or crashes within a set window, for example, three restarts within five minutes. PM2 supports this with the <strong>&ndash;max-restarts<\/strong> flag, and tools like GitHub Actions can trigger a rollback when the deployment script fails.<\/li>\n<\/ul><h2 class=\"wp-block-heading\" id=\"h-how-to-scale-a-node-js-application-in-production\"><strong>How to scale a Node.js application in production<\/strong><\/h2><p>To scale a Node.js application in production, combine server-level strategies with architectural changes that distribute traffic and workload across multiple processes or machines.<\/p><p><strong>Horizontal scaling<\/strong> adds more server instances to handle increased traffic, with a load balancer distributing requests between them.<\/p><p><strong>Vertical scaling<\/strong> increases resources like CPU, RAM, and storage on a single server. It&rsquo;s simpler to set up, but it has limits because a single machine can only scale so far.<\/p><p><strong>Load balancing<\/strong> distributes requests evenly, preventing any single process from becoming a bottleneck. NGINX can act as a load balancer by defining an upstream block with multiple backend servers and routing traffic between them.<\/p><p>Node.js runs on a single thread by default, so it uses only one CPU core even if the server has more available.<\/p><p>The built-in <strong>cluster<\/strong> module solves this by forking the main process into multiple worker processes, usually one per CPU core, all sharing the same server port.<\/p><p>PM2 simplifies clustering with cluster mode. Running <strong>pm2 start app.js -i max<\/strong> spawns one worker per available CPU core automatically.<\/p><p>For larger setups, containerization with Docker packages your Node.js application and its dependencies into a portable image that runs consistently across environments.<\/p><p>Tools like <a href=\"\/ca\/tutorials\/kubernetes-tutorial\">Kubernetes<\/a> manage containers across multiple machines. They handle scaling, rolling updates, health checks, and load balancing automatically.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>To deploy a Node.js application, you can either follow a general deployment workflow that works across most hosting environments or use hosting-specific options like web app hosting or a virtual private server (VPS). Node.js deployment means moving your application from a local development machine to a production server where users can access it over the [&#8230;]<\/p>\n<p><a class=\"btn btn-secondary understrap-read-more-link\" href=\"\/ca\/tutorials\/deploy-node-js-application\">Read More&#8230;<\/a><\/p>\n","protected":false},"author":411,"featured_media":143361,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_title":"How to deploy a Node.js application","rank_math_description":"Learn how to deploy a Node.js application step by step, including general deployment workflows and deployment using Hostinger web app hosting or VPS.","rank_math_focus_keyword":"deploy node js application","footnotes":""},"categories":[22699],"tags":[],"class_list":["post-143360","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-vps"],"hreflangs":[{"locale":"en-US","link":"https:\/\/www.hostinger.com\/tutorials\/deploy-node-js-application\/","default":1},{"locale":"en-PH","link":"https:\/\/www.hostinger.com\/ph\/tutorials\/deploy-node-js-application\/","default":0},{"locale":"en-MY","link":"https:\/\/www.hostinger.com\/my\/tutorials\/deploy-node-js-application\/","default":0},{"locale":"en-UK","link":"https:\/\/www.hostinger.com\/uk\/tutorials\/deploy-node-js-application\/","default":0},{"locale":"en-IN","link":"https:\/\/www.hostinger.com\/in\/tutorials\/deploy-node-js-application\/","default":0},{"locale":"en-CA","link":"https:\/\/www.hostinger.com\/ca\/tutorials\/deploy-node-js-application\/","default":0},{"locale":"en-AU","link":"https:\/\/www.hostinger.com\/au\/tutorials\/deploy-node-js-application\/","default":0},{"locale":"en-NG","link":"https:\/\/www.hostinger.com\/ng\/tutorials\/deploy-node-js-application\/","default":0}],"_links":{"self":[{"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/posts\/143360","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\/411"}],"replies":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/comments?post=143360"}],"version-history":[{"count":0,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/posts\/143360\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/media\/143361"}],"wp:attachment":[{"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/media?parent=143360"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/categories?post=143360"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.hostinger.com\/ca\/tutorials\/wp-json\/wp\/v2\/tags?post=143360"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}