How to deploy a Node.js application

How to deploy a Node.js application

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 internet.

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.

Setting up the Node.js production environment correctly from the start helps prevent these issues and keeps the application stable as traffic grows.

The typical Node.js deployment process includes five stages: preparation, hosting selection, environment configuration, deployment, and monitoring.

Here’s what each stage involves:

  1. Prepare the production application. Define dependencies in package.json, configure environment variables, and add a start script.
  2. Commit and push the code to Git. Upload files to the repository using the browser interface or run Git CLI commands from your computer.
  3. Set up Node.js and a process manager. Install Node.js on the server, configure environment variables, and use PM2 to keep the application running after crashes or reboots.
  4. Route traffic through a reverse proxy. Configure NGINX to forward requests to the Node.js process on its internal port and secure connections with an SSL certificate.
  5. Verify and monitor the deployment. Check the application in the browser, review PM2 logs, and monitor performance metrics to detect issues early.

How to deploy a Node.js application

Deploying a Node.js application follows a universal workflow that works regardless of the hosting provider.

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.

1. Prepare the Node.js application for deployment

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.

Three files form the foundation of a deployment-ready Node.js project: app.js (the main application file), package.json (project metadata, dependencies, and scripts), and .gitignore (files excluded from version control).

Every package your application imports must appear under the dependencies field in package.json. The file tells npm install which packages to download on the server.

If a required package is missing, the application will crash in production even if it worked locally, where the package already existed in node_modules. Here’s an example:

{
   "name": "node-deploy-test",
   "version": "1.0.0",
   "description": "Sample Node.js app for deployment tutorial",
   "main": "app.js",
   "scripts": {
      "start": "node app.js"
   },
   "dependencies": {
      "express": "^4.21.0"
   }
}

The scripts section defines how hosting platforms and process managers start the application. The “start”: “node app.js” entry tells the server to run app.js as the entry point.

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.

In the application code, reference these values using process.env:

const PORT = process.env.PORT || 3000;

This line reads the PORT variable if it exists. Otherwise, it defaults to 3000. The same pattern applies to NODE_ENV, which hosting platforms usually set to production automatically.

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.

2. Push the application code to a Git repository

Version control is essential for Node.js deployment because it creates a reliable, trackable record of every change to the codebase.

If a deployment introduces a bug, you can revert to the last working commit in seconds, rather than blindly troubleshooting.

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:

  1. Log in to GitHub, or create an account at github.com if you don’t have one.
  2. Click New on the dashboard, then enter a repository name, for example, node-deploy-test.
  3. Set the visibility to Public if you plan to connect the repository to a hosting platform later.
  4. Leave the other options unchecked (no README, no .gitignore, no license), then click Create repository.

The simplest way to add files is through the browser:

  1. On the repository setup page, click uploading an existing file.
  2. Drag app.js and package.json into the upload area.
  3. Click Commit changes.

Keep in mind that most operating systems hide dotfiles like .gitignore, so drag-and-drop uploads won’t work. Create the file directly on GitHub instead:

  1. On the repository’s main page, click Add fileCreate new file.
  2. Type .gitignore as the filename and add the following line as its content:
node_modules/
  1. Click Commit changes.

Alternatively, if Git is installed on your computer, you can push all your files, including .gitignore, using Git CLI commands:

cd path/to/node-deploy-test
git init
git add .
git commit -m "Initial commit"
git remote add origin https://github.com/your-username/node-deploy-test.git
git push -u origin main

Replace your-username with your actual GitHub username.

The Git CLI method works better for ongoing development. Running git push sends only the changes, while browser uploads require you to upload files again.

3. Set up the server and run the application

Setting up the server starts with installing Node.js. You can install it using Node Version Manager (NVM) or a system package manager.

NVM is the better option because it lets you install and switch between multiple Node.js versions without requiring root permissions.

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.

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.

At this point, the application runs internally on a port such as 3000, but users still can’t access it through standard web ports like 80 or 443. To expose the application to the public internet, you need to set up a reverse proxy.

4. Configure a reverse proxy and enable HTTPS

A reverse proxy server like NGINX routes public traffic to the Node.js application running on an internal port.

NGINX listens on ports 80 (HTTP) and 443 (HTTPS), accepts incoming requests, and forwards them to localhost:3000 or to whichever port your app uses. Users never interact with the Node.js process directly.

This setup also serves as the termination point for SSL/TLS connections, keeping encryption management separate from your application code.

After configuring the reverse proxy, enable HTTPS using Certbot with a Let’s Encrypt SSL certificate.

5. Test and monitor the deployed application

After deployment, test the Node.js application by opening a browser and visiting your application’s root URL, for example, http://domain.tld. The main page should load with the expected content.

If you added a health check route, append /health to your application’s URL and confirm it returns JSON output like:

{"status":"healthy","uptime":...}

Next, check the Node.js logs using your process manager. For PM2, the pm2 logs command shows a real-time stream of your application’s output and error messages.

For a broader view, run pm2 monit to open an interactive dashboard. It shows CPU usage, memory consumption, event loop latency, and logs in one place.

As a baseline, a healthy Node.js application typically stays under 50 ms event loop latency, uses around 300–500 MB of memory for a standard Express app, and keeps CPU usage below 70% under normal traffic.

How to deploy a Node.js application using web app hosting

Deploying a Node.js application with web app hosting involves connecting a Git repository, defining build commands, and triggering deployment from a dashboard.

Hostinger Node.js web app hosting provides a managed hosting environment that lets you deploy applications without setting up a server from scratch.

It handles the underlying infrastructure, including server configuration, Node.js installation, process management, and SSL certificates.

It also supports automatic redeployment, so when you push changes to GitHub, the system rebuilds and redeploys your application automatically.

1. Create a Node.js web app hosting environment

After purchasing a Node.js hosting plan, log in to hPanel using your Hostinger account to create a new application.

Go to WebsitesAdd websiteNode.js Web App.

If you have a Business plan or higher on regular web hosting, you can follow the same steps to create a Node.js application.

Hostinger web hosting banner

2. Connect the Git repository

Connecting a Git repository links your GitHub project to the Node.js web app hosting environment.

Once connected, every time you update your code on GitHub, the platform pulls the latest version, installs dependencies, and restarts the application automatically.

To connect the repository:

  1. Select Import Git Repository on the setup screen.
  1. GitHub redirects you to an authorization page. Click Authorize to grant Hostinger access.
  2. Select the repository from the list.

A single hosting plan connects to one GitHub account at a time. All Node.js websites on that plan share the same connection.

If the project files aren’t in a Git repository, select Upload your website files instead and upload a compressed ZIP file. This method doesn’t support automatic redeployment when you update your code.

3. Configure build and start commands

After you select the repository, the system automatically detects the framework and suggests build settings.

You can adjust these settings if your project needs different values:

  • Framework preset. The detected framework for your project, for example, Express.js. Change it if auto-detection selects the wrong one.
  • Node.js version. The runtime version for your application. The system detects this from the engines field in package.json, but you can override it manually.
  • Entry file. The file the server runs to start the application. It defaults to app.js, based on the Node.js start command in package.json.
  • Package manager. The tool used to install dependencies. By default, it’s npm, but you can switch to Yarn or pnpm if your project uses a different lock file.
  • Environment variables. Production values like API keys or database credentials. Add them here instead of committing them to the repository.

4. Launch and verify the deployed application

Click Deploy to start the build. The system installs dependencies, runs the build command, and starts the Node.js application.

Once deployment finishes, you should see a “Deployment completed” message.

Click Go to Dashboard and open the temporary domain in a new browser tab to confirm the application loads correctly.

The Node.js dashboard in hPanel provides a quick overview of the deployed application, so you can monitor performance and manage settings.

  • GitHub repository link. Opens the connected repository in a new tab so you can review code, branches, or recent commits.
  • Last deployment details. Shows the deployment status (success or failure), the timestamp, and a link to the full deployment log.
  • Quick links. Jump directly to Deployments, Environment Variables, Settings & Redeploy, or File Manager.
  • Resource usage graphs. Show average CPU, RAM, and I/O usage. If any metric approaches the red dotted line, which marks your plan’s limit, consider upgrading.

How to deploy a Node.js application using VPS hosting

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.

VPS hosting gives you full control over the server environment so that you can configure everything from the operating system to firewall rules.

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.

1. Create a VPS instance

Start by creating a VPS instance and connecting to it via SSH. Choose the most recent Ubuntu long-term support (LTS) version, such as 24.04, as the operating system.

Then connect using an SSH client like PuTTY or hPanel’s browser terminal.

After that, complete the initial VPS server setup by updating system packages, configuring the firewall using ufw, and creating a non-root user with sudo privileges for daily use.

2. Install Node.js on the server

To install Node.js on Ubuntu, you can use NVM or a system package manager. Follow these steps if you choose the former:

  1. Run the NVM install script:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.2/install.sh | bash
  1. Close and reopen the terminal to load NVM into the current session.
  2. Install the latest LTS version of Node.js:
nvm install --lts
  1. Verify that Node.js and npm are installed correctly:
node -v
npm -v

Both commands should return version numbers like this:

3. Upload the Node.js application to the server

If you’ve already pushed your application to a GitHub repository, you can transfer the code to the server using Git:

cd /var/www
git clone https://github.com/your-username/your-repository.git
cd your-repository
npm install

Replace your-username and your-repository with your actual GitHub username and repository name.

The npm install command reads package.json and installs all dependencies into the node_modules folder on the server.

Alternatively, you can transfer files directly using the scp command. Open a new terminal window on your computer (not the one connected to the server) and run:

scp -r /local/path/node-deploy-test root@192.0.2.1:/var/www/

Replace /local/path/node-deploy-test with the path to your project folder, root with your server username, and 192.0.2.1 with your server’s IP address.

Hostinger VPS customers can find their server’s IP address in hPanel by going to VPS → Manage → Overview → VPS details.

After the transfer finishes, switch back to the server terminal and run npm install inside the project directory.

Then set the required environment variables before starting the application:

export NODE_ENV=production
export PORT=3000

NODE_ENV=production tells frameworks to optimize for performance and disable development features. PORT should match the port your application listens on, as configured in app.js.

4. Configure PM2 and reverse proxy

Start your Node.js application with PM2 to keep it running in the background.

  1. Install PM2 and start the application:
npm install -g pm2
pm2 start app.js --name "node-app"
  1. Configure PM2 to restart the application after a server reboot:
pm2 startup
pm2 save

pm2 startup generates a system service that launches PM2 on boot. pm2 save persists the current process list so PM2 knows which applications to restart.

  1. Open firewall ports for web traffic. Otherwise, NGINX won’t receive incoming connections:
ufw allow http
ufw allow https
  1. Install NGINX on your server:
apt update
apt install nginx -y
  1. Create an NGINX reverse proxy configuration for your Node.js deployment:
nano /etc/nginx/sites-available/node-app
  1. Add the following configuration, replacing your-hostname with your own domain name or server hostname:
server {
   listen 80;
   server_name your-hostname;

   location / {
      proxy_pass http://localhost:3000;
      proxy_http_version 1.1;
      proxy_set_header Upgrade $http_upgrade;
      proxy_set_header Connection 'upgrade';
      proxy_set_header Host $host;
      proxy_cache_bypass $http_upgrade;
   }
}

This configuration forwards all incoming HTTP requests to the Node.js process on port 3000. The proxy_set_header directives preserve client information and enable WebSocket connections.

  1. Save the file and exit the editor with Ctrl + OEnterCtrl + X.
  2. Enable the configuration and remove the default site:
ln -s /etc/nginx/sites-available/node-app /etc/nginx/sites-enabled/
rm /etc/nginx/sites-enabled/default
  1. Test the configuration and reload NGINX. The command should return “syntax is ok” and “test is successful”:
nginx -t
systemctl reload nginx
  1. Visit your application’s URL in your browser, without a port number. It should load correctly.
  1. Enable HTTPS with Certbot. Make sure your domain or hostname points to the server’s IP address, since Certbot can’t issue certificates for IP addresses.
apt install certbot python3-certbot-nginx -y
certbot --nginx -d your-hostname
  1. 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.

Once done, visit your application’s URL again and confirm that the “Connection is secure” message appears in your browser.

5. Run and verify the production application

Visit your Node.js application’s URL and confirm the page loads with the expected content. If it doesn’t load, check that NGINX is running with systemctl status nginx and that PM2 shows the app as “online” with pm2 list.

Once the application is live, use PM2 to monitor its runtime behavior:

  • pm2 logs. Streams real-time application output and errors. Open the app in your browser while this command runs to confirm new log entries appear.
  • pm2 monit. Opens an interactive dashboard that shows CPU usage, memory consumption, event loop latency, and logs in one view.
  • pm2 show node-app. Displays detailed information, including uptime, restart count, memory usage, script path, and log file locations.

Press Ctrl + C to exit any of these views.

What are the best practices for Node.js deployment?

Best practices for Node.js deployment focus on automation, environment separation, and proactive monitoring to keep production applications stable and secure.

  • Set up a CI/CD pipeline. 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.
  • Separate configuration from code. Store environment-specific values in environment variables or external config files that you don’t commit to the repository. Use a .env file locally, loaded with a library like dotenv, and set variables directly on the server or through your hosting platform’s dashboard in production.
  • Use structured logging. 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.
  • Set up automated health checks. Use a monitoring service like UptimeRobot or Pingdom to ping your application’s health endpoint every 30–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.
  • Enable automated rollbacks. 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 –max-restarts flag, and tools like GitHub Actions can trigger a rollback when the deployment script fails.

How to scale a Node.js application in production

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.

Horizontal scaling adds more server instances to handle increased traffic, with a load balancer distributing requests between them.

Vertical scaling increases resources like CPU, RAM, and storage on a single server. It’s simpler to set up, but it has limits because a single machine can only scale so far.

Load balancing 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.

Node.js runs on a single thread by default, so it uses only one CPU core even if the server has more available.

The built-in cluster module solves this by forking the main process into multiple worker processes, usually one per CPU core, all sharing the same server port.

PM2 simplifies clustering with cluster mode. Running pm2 start app.js -i max spawns one worker per available CPU core automatically.

For larger setups, containerization with Docker packages your Node.js application and its dependencies into a portable image that runs consistently across environments.

Tools like Kubernetes manage containers across multiple machines. They handle scaling, rolling updates, health checks, and load balancing automatically.

All of the tutorial content on this website is subject to Hostinger's rigorous editorial standards and values.

Author
The author

Ariffud Muhammad

Ariffud is a Technical Content Writer with an educational background in Informatics. He has extensive expertise in Linux and VPS, authoring over 200 articles on server management and web development. Follow him on LinkedIn.

What our customers say