bolt Valebyte VPS from $4/mo — NVMe, 60s deploy.

Get a VPS arrow_forward

Deploying Node.js applications on a VPS: Express and NestJS

calendar_month July 01, 2026 schedule 17 min read visibility 32 views
person
Valebyte Team
Deploying Node.js applications on a VPS: Express and NestJS

Deploying a Node.js application on a VPS, whether it's Express or NestJS, involves preparing the code for production, configuring the server environment with PM2 or systemd for process management, setting up Nginx as a reverse proxy, securing it with SSL/TLS, and implementing CI/CD for automation and monitoring systems for stable operation.

Why VPS is the optimal choice for deploying a Node.js application in production?

A Virtual Private Server (VPS) offers a flexible and powerful solution for deploying a Node.js application in a production environment. Unlike shared hosting, which often imposes restrictions on technologies and resources, a VPS provides full root access to the operating system. This allows developers to install any necessary packages, configure the server environment to their needs, and optimize performance. For Node.js applications that require specific Node.js versions, npm packages, and system libraries, such freedom is critical. VPS also offers better resource isolation compared to shared hosting, ensuring that your application's performance won't suffer due to "noisy neighbors."

Advantages of VPS for Node.js

Choosing a VPS for node app hosting provides several key advantages:

  • Full Control: You have root access to the server, allowing you to install and configure any software, including specific Node.js versions, databases (PostgreSQL, MongoDB, Redis), caching tools, and monitoring systems.
  • Resource Isolation: Your server runs in an isolated environment, and its dedicated CPU, RAM, and disk space are guaranteed. This prevents performance degradation due to the activity of other users on the same physical server.
  • Scalability: As your application grows and traffic increases, you can easily scale VPS resources (add CPU, RAM, disk space) without needing to migrate to a new server, unless it's a dedicated server. This significantly simplifies infrastructure management.
  • Cost-Effectiveness: VPS offers an excellent balance between cost and performance. It is significantly cheaper than a dedicated server while providing most of its benefits for the majority of medium and large projects.
  • Configuration Flexibility: The ability to fine-tune network parameters, firewalls, and security, which is especially important for production applications.

For a deeper comparison of different Node.js hosting options, we recommend reading our article: Best Hosting for Node.js: VPS vs PaaS vs Shared.

Difference between Express and NestJS in the context of deployment

While Express.js and NestJS are both Node.js frameworks, their architectural differences can slightly affect the deployment process of a Node.js application, especially in the context of building and preparing for production.

  • Express.js: This is a minimalist and unopinionated framework. Express applications typically have a simple file structure and do not require complex builds. For production, it's enough to ensure all dependencies are installed and run the main application file (e.g., app.js or server.js) using Node.js.
  • NestJS: NestJS is an opinionated, full-stack framework built with TypeScript and inspired by Angular. It uses a modular structure, decorators, and a powerful CLI. NestJS applications require compilation from TypeScript to JavaScript before deployment. This is usually done with the nest build command, which creates a dist folder (by default) with the transpiled code. In production, the JavaScript file from this folder (e.g., dist/main.js) is then run. This build step is a key difference and must be accounted for in the deployment and CI/CD process.

Otherwise, the steps for process management (PM2/systemd), Nginx configuration, SSL, and monitoring will be almost identical for both frameworks, as they both run on top of Node.js.

Preparing a Node.js application for VPS deployment: what to consider?

Before proceeding with deploying a Node.js application on a VPS, you need to ensure that your application is ready for production. This stage includes a number of important steps that ensure security, stability, and performance.

Environment variables and configuration

Using environment variables is a standard and secure practice for managing application configuration across different environments (development, testing, production). Never store sensitive data, such as API keys, database credentials, or JWT secrets, directly in your application code. Instead, use environment variables.

In Node.js, the dotenv library is often used to work with environment variables. In a production environment, these variables are typically set directly in the server's environment or via process manager configuration files (e.g., PM2).

Example of using dotenv for development:


// .env file
PORT=3000
DATABASE_URL=mongodb://localhost:27017/myapp_dev
JWT_SECRET=supersecretdevkey

// app.js or main.ts
require('dotenv').config(); // Should be called as early as possible

const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;
const jwtSecret = process.env.JWT_SECRET;

console.log(`Application launched on port ${port}`);

For production, you will set these variables on the VPS. For example, for systemd:


[Service]
Environment="PORT=8080"
Environment="DATABASE_URL=mongodb://user:password@remote_host:27017/myapp_prod"
Environment="JWT_SECRET=supersecretprodkey_long_random_string"

Or for PM2 via a configuration file:


// ecosystem.config.js
module.exports = {
  apps: [{
    name: "my-nodejs-app",
    script: "./dist/main.js", // For NestJS, or ./app.js for Express
    env_production: {
      NODE_ENV: "production",
      PORT: 8080,
      DATABASE_URL: "mongodb://user:password@remote_host:27017/myapp_prod",
      JWT_SECRET: "supersecretprodkey_long_random_string"
    }
  }]
};

Build and dependency optimization

For Node.js production deployment, it's important that your application is as efficient as possible. This includes:

Looking for a reliable server for your projects?

VPS from $10/month and dedicated servers from $9/month with NVMe, DDoS protection, and 24/7 support.

View offers →
  • Dependency cleanup: Ensure that dev dependencies are not included in the production build. Use npm install --production or yarn install --production on the server or in your CI/CD pipeline to install only necessary dependencies.
  • Minification and bundling: For NestJS, TypeScript compilation to JavaScript (nest build) already performs some optimization. For Express applications, if you use frontend code, consider using Webpack or Rollup for JavaScript/CSS bundling and minification.
  • Removing unnecessary files: Exclude files from the production build that are not needed for the application to run (e.g., tests, documentation, development configuration files). Use a .dockerignore file if deploying via Docker, or .gitignore to avoid committing unnecessary files.
  • Logging: Configure logging to be informative but not excessive in production. Use libraries like Winston or Pino, which allow you to configure logging levels (e.g., info, warn, error).

How to run a Node.js application on a VPS and ensure its stability?

Once your application is ready, the next step is to launch it and ensure its continuous operation on the VPS. This is done using process managers, which not only start the application but also monitor it, restart it in case of failures, and manage logs. The most popular tools for deploying NestJS on a VPS and Express applications are PM2 and systemd.

Using PM2 for process management

PM2 (Process Manager 2) is a production-ready process manager for Node.js applications with a built-in load balancer. It allows applications to stay active 24/7, automatically restart them on crashes, manage logs, and monitor performance.

PM2 Installation:


npm install pm2 -g

Running an application with PM2:


pm2 start app.js # For Express
pm2 start dist/main.js # For NestJS

PM2 will automatically assign a name to the process. For more flexible management and production environment setup, it is recommended to use a configuration file ecosystem.config.js:


// ecosystem.config.js
module.exports = {
  apps : [{
    name: "my-nodejs-app",
    script: "./dist/main.js", // Path to your main application file
    instances: "max", // Launch as many processes as CPU cores
    exec_mode: "cluster", // Use cluster mode for load balancing
    watch: false, // Disable watch in production
    max_memory_restart: "300M", // Restart application if memory consumption exceeds 300MB
    env_production: {
      NODE_ENV: "production",
      PORT: 8080,
      DATABASE_URL: "mongodb://user:password@remote_host:27017/myapp_prod",
      JWT_SECRET: "supersecretprodkey_long_random_string"
    },
    error_file: "logs/err.log",
    out_file: "logs/out.log",
    log_file: "logs/combined.log",
    time: true
  }]
};

Running the application with a configuration file:


pm2 start ecosystem.config.js --env production

Useful PM2 commands:

  • pm2 list: Show a list of running applications.
  • pm2 monit: Open the monitoring dashboard in the terminal.
  • pm2 logs: Show logs for all applications.
  • pm2 restart my-nodejs-app: Restart a specific application.
  • pm2 stop my-nodejs-app: Stop an application.
  • pm2 delete my-nodejs-app: Delete an application from the PM2 list.
  • pm2 startup: Generate a script for PM2 to auto-start on server boot.
  • pm2 save: Save the current list of applications for auto-start.

You can find a more detailed guide on using PM2 in our article: Node.js Hosting on VPS: PM2, Nginx, and SSL in 15 minutes.

Configuring Systemd for automatic startup

Systemd is an init system and service manager for Linux that can manage Node.js applications as system services. This is a more "low-level" approach compared to PM2, but it provides full control over the process and integrates well with the operating system.

Create a Systemd service file (e.g., /etc/systemd/system/my-nodejs-app.service):


[Unit]
Description=My Node.js Application
After=network.target

[Service]
User=your_user # User under which the application will run
WorkingDirectory=/var/www/my-nodejs-app # Path to your application
ExecStart=/usr/bin/node /var/www/my-nodejs-app/dist/main.js # Path to Node.js and the main file
Restart=always
RestartSec=5
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=my-nodejs-app
Environment="NODE_ENV=production"
Environment="PORT=8080"
Environment="DATABASE_URL=mongodb://user:password@remote_host:27017/myapp_prod"
Environment="JWT_SECRET=supersecretprodkey_long_random_string"

[Install]
WantedBy=multi-user.target

After creating the service file, execute the following commands:


sudo systemctl daemon-reload           # Reload systemd configuration
sudo systemctl enable my-nodejs-app    # Enable service auto-start on system boot
sudo systemctl start my-nodejs-app     # Start the service
sudo systemctl status my-nodejs-app    # Check service status
sudo journalctl -u my-nodejs-app -f    # View service logs

The choice between PM2 and systemd depends on your preferences. PM2 is more convenient for managing multiple Node.js applications and has a richer set of monitoring features. Systemd, on the other hand, is more native to Linux and well-suited for integration with other system services.

rocket_launch Quick pick

Need a dedicated server?

Compare prices from top providers. Configure and order in minutes.

Browse dedicated servers arrow_forward

Configuring Nginx as a reverse proxy for Node.js

Directly accessing a Node.js application through its port (e.g., 3000 or 8080) is not a secure or efficient practice for production. Instead, a web server like Nginx is used as a reverse proxy. Nginx accepts incoming HTTP requests on standard ports (80 for HTTP, 443 for HTTPS) and forwards them to your Node.js application, which listens on an internal port. This allows Nginx to handle static files, balance load, cache requests, and, most importantly, manage SSL/TLS encryption.

Nginx configuration for Express and NestJS

Assume your Node.js application is running on port 8080. Create a new Nginx configuration file for your domain (e.g., /etc/nginx/sites-available/yourdomain.com):


server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com; # Replace with your domain

    location / {
        proxy_pass http://localhost:8080; # Port where your Node.js application is running
        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;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # If you have static files that Nginx should serve directly
    # location /static/ {
    #     alias /var/www/my-nodejs-app/public/static/; # Path to your static files
    #     expires 30d;
    #     access_log off;
    #     add_header Cache-Control "public";
    # }
}

Activate the configuration by creating a symbolic link to sites-enabled:


sudo ln -s /etc/nginx/sites-available/yourdomain.com /etc/nginx/sites-enabled/
sudo nginx -t # Test configuration syntax
sudo systemctl restart nginx # Restart Nginx

Now, when a user accesses yourdomain.com, Nginx forwards the request to your Node.js application running on port 8080. This is a critical step for node app hosting in production.

Securing with SSL/TLS (Let's Encrypt)

For a secure connection between the client and the server, HTTPS must be used. Let's Encrypt provides free SSL/TLS certificates, and the process of obtaining and installing them with Certbot is very simple.

Certbot Installation:


sudo snap install core; sudo snap refresh core
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot

Obtaining and installing a certificate for Nginx:


sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com

Certbot will automatically modify your Nginx configuration, adding the necessary directives for SSL/TLS and setting up redirection from HTTP to HTTPS. Your /etc/nginx/sites-available/yourdomain.com configuration will look something like this:


server {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri; # Redirect to HTTPS
}

server {
    listen 443 ssl http2;
    server_name yourdomain.com www.yourdomain.com;

    ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    location / {
        proxy_pass http://localhost:8080;
        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;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Certbot will also configure automatic certificate renewal, saving you from manual work.

Automating Node.js deployment with CI/CD

Automating the process of deploying a Node.js application using CI/CD (Continuous Integration/Continuous Deployment) significantly reduces time, minimizes errors, and ensures more frequent and reliable releases. This is especially important for Node.js production deployment, where any manual operation increases the risk of human error.

Basic Git-push deployment

The simplest way to automate is to use Git Hooks or a simple script that runs after a push to a specific branch (e.g., main or production). This method may be sufficient for small projects.

Example of a simple deployment script (deploy.sh):


#!/bin/bash
APP_DIR="/var/www/my-nodejs-app"
LOG_FILE="/var/log/my-nodejs-app-deploy.log"

echo "Deployment started $(date)" >> $LOG_FILE

cd $APP_DIR || { echo "Failed to navigate to application directory" >> $LOG_FILE; exit 1; }

echo "Performing git pull..." >> $LOG_FILE
git pull origin main >> $LOG_FILE 2>&1

echo "Installing dependencies..." >> $LOG_FILE
npm install --production >> $LOG_FILE 2>&1

echo "Building application (for NestJS)..." >> $LOG_FILE
# For NestJS:
npm run build >> $LOG_FILE 2>&1
# For Express, this step can be skipped or replaced with other commands

echo "Restarting application via PM2..." >> $LOG_FILE
pm2 reload my-nodejs-app --env production >> $LOG_FILE 2>&1 # Or pm2 restart my-nodejs-app

echo "Deployment completed $(date)" >> $LOG_FILE

You can run this script manually after each push or configure SSH access for your CI/CD system so it can invoke it. However, using specialized CI/CD platforms is more reliable and secure.

Integration with GitHub Actions or GitLab CI

For more advanced automation, it is recommended to use platforms such as GitHub Actions, GitLab CI/CD, Jenkins, or Bitbucket Pipelines. These tools allow you to create complex pipelines that can include testing, linting, building, creating Docker images, and, of course, deploying to your VPS.

Example GitHub Actions for deploying to VPS:


# .github/workflows/deploy.yml
name: Deploy Node.js App to VPS

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Setup Node.js
      uses: actions/setup-node@v3
      with:
        node-version: '18'

    - name: Install dependencies
      run: npm install --production

    - name: Build NestJS application (if applicable)
      run: npm run build # Skip for Express

    - name: Deploy to VPS
      uses: appleboy/ssh-action@master
      with:
        host: ${{ secrets.SSH_HOST }}
        username: ${{ secrets.SSH_USERNAME }}
        key: ${{ secrets.SSH_PRIVATE_KEY }}
        script: |
          cd /var/www/my-nodejs-app
          git pull origin main
          npm install --production
          npm run build # Skip for Express
          pm2 reload my-nodejs-app --env production

For this pipeline, you will need to configure secrets in your GitHub repository (SSH_HOST, SSH_USERNAME, SSH_PRIVATE_KEY). Similar pipelines can be created in GitLab CI/CD using the .gitlab-ci.yml file.

Using CI/CD not only automates the deployment of a Node.js application but also ensures that only tested and correctly built code reaches production. This significantly increases the reliability of your application.

Monitoring the performance and health of a Node.js application

After deploying NestJS on a VPS or an Express application, monitoring becomes critically important for maintaining its stability and performance. It allows for prompt identification of problems, tracking metrics, and responding to incidents before they affect users.

Tools for logging and metrics

Effective monitoring begins with proper logging and metric collection:

  • System logs: Systemd and PM2 already direct your application's logs to the system journal. You can use journalctl -u my-nodejs-app -f to view systemd logs or pm2 logs for PM2. For centralized log collection, consider ELK Stack (Elasticsearch, Logstash, Kibana) or Grafana Loki solutions.
  • Application logs: Use logging libraries such as Winston or Pino to structure your application logs. They allow you to configure logging levels (debug, info, warn, error) and output formats (JSON), which simplifies subsequent analysis.
  • Performance metrics:
    • PM2 Monit: The built-in PM2 tool pm2 monit provides basic monitoring of CPU, memory, and RPS (requests per second) for your Node.js processes.
    • Prometheus and Grafana: This is a powerful combination for collecting and visualizing metrics. You can use the prom-client library in Node.js to export custom metrics (e.g., number of requests, response time, errors), and Prometheus will collect them. Grafana is then used to create dashboards for visualizing these metrics.
    • APM tools: Application Performance Monitoring (APM) solutions such as New Relic, Datadog, Sentry, or AppDynamics provide deep performance analysis, request tracing, error monitoring, and more. They often require an agent to be installed in your application.
  • VPS resource monitoring: Use tools such as htop, iotop, netstat for manual resource control. For automated monitoring, install monitoring agents that will collect data on CPU load, RAM usage, disk space, and network traffic and send it to a centralized system (e.g., Prometheus Node Exporter).

Alerts and incident response

Simply collecting metrics is not enough; you need to set up an alerting system that will notify you of critical events. Tools such as Prometheus Alertmanager, Grafana Alerting, or built-in APM systems allow you to configure rules for sending notifications via email, Slack, Telegram, or PagerDuty when certain thresholds are exceeded (e.g., CPU > 90%, RAM > 80%, number of 5xx errors > X per minute).

Examples of alerts:

  • High CPU or memory usage on the VPS.
  • A large number of 5xx errors (Internal Server Error) in the application.
  • API response time exceeds a set threshold (e.g., 500 ms).
  • PM2 or systemd application is inactive.
  • Running out of disk space.

Having a clear incident response plan, including responsible parties and troubleshooting steps, minimizes downtime and business damage. Regular analysis of logs and metrics will help identify bottlenecks and optimize your application and infrastructure.

rocket_launch Quick pick

Need a dedicated server?

Compare prices from top providers. Configure and order in minutes.

Browse dedicated servers arrow_forward

Which VPS to choose for a Node.js application under different loads?

Choosing the right VPS for deploying a Node.js application depends on the expected load, application complexity, and your budget. It's not wise to overpay for excessive resources at the start, but it's also not reasonable to save on critically important components. Valebyte.com offers various plans that can meet any needs.

VPS characteristics recommendations

For Node.js applications, CPU and RAM are particularly important, as well as fast disk I/O (NVMe SSD). The number of CPU cores and the amount of RAM directly affect your application's ability to handle parallel requests and perform resource-intensive operations.

  1. For small projects and MVPs (up to 5000 requests/day):
    • CPU: 1-2 vCPU (virtual cores)
    • RAM: 1-2 GB
    • Disk: 25-50 GB NVMe SSD
    • Example usage: Personal blogs, small APIs, demonstration projects.
    • Cost: from $5-10/month.
  2. For medium projects and startups (5000-50000 requests/day):
    • CPU: 2-4 vCPU
    • RAM: 4-8 GB
    • Disk: 50-100 GB NVMe SSD
    • Example usage: E-commerce sites with moderate traffic, SaaS applications, APIs with active load.
    • Cost: from $20-40/month.
  3. For high-load projects and growing applications (over 50000 requests/day):
    • CPU: 4-8+ vCPU
    • RAM: 8-16+ GB
    • Disk: 100-200+ GB NVMe SSD (or more, depending on data)
    • Example usage: Social networks, large portals, high-load APIs, applications with intensive computations.
    • Cost: from $50-100+/month.
  4. Special requirements:
    • Databases: If the database (especially PostgreSQL or MongoDB) is located on the same VPS, increase RAM by 2-4 GB.
    • WebSockets/Real-time: Applications actively using WebSockets may require more RAM and a stable network connection.
    • Scaling: For very high loads, consider horizontal scaling — multiple VPS instances with a load balancer.

It's important to remember that these recommendations are a starting point. Actual requirements may vary depending on your code optimization, libraries used, and the nature of the load. Always start small and scale as needed.

If you are looking for good deals, check out our article: Where is the cheapest place to host a Node.js application in 2026.

Valebyte.com VPS plan comparison table for Node.js

Below is a table with examples of Valebyte.com plans suitable for node app hosting, indicating recommended usage scenarios and approximate costs. Please note that these are approximate data, and actual prices and configurations may change.

Valebyte.com Plan vCPU RAM (GB) NVMe SSD (GB) Bandwidth Recommended Scenario Approximate Price/month
Entry Node 1 2 50 100 Mbps Small APIs, personal projects, MVPs $7.99
Standard Node 2 4 100 200 Mbps Medium APIs, small SaaS, e-commerce $19.99
Pro Node 4 8 200 500 Mbps High-load APIs, active SaaS, databases $39.99
Ultra Node 8 16 400 1 Gbps Large applications, microservices, intensive computations $79.99

When choosing a plan, always consider the potential growth of your application. Choose a provider that offers easy scaling of VPS resources so you can increase server power as your project grows without the need for complex data migration.

Conclusion

Successful deployment of a Node.js application on a VPS requires a systematic approach, including code preparation, choosing the right tools for process management (PM2/systemd), Nginx and SSL configuration, automation with CI/CD, and continuous monitoring. Selecting an appropriate VPS from a provider like Valebyte.com, with fast NVMe SSDs and sufficient RAM and vCPU, is the foundation for stable and performant operation of your Express or NestJS application in production.

Ready to choose a server?

VPS and dedicated servers in 72+ countries with instant activation and full root access.

Start now →
support_agent
Valebyte Support
Usually replies within minutes
Hi there!
Send us a message and we'll reply as soon as possible.