How to Host a Node.js Application on a Linux VPS
Hosting a Node.js application on a VPS gives you much more control than traditional shared hosting. You can choose the Node.js version, install the packages your application needs, run background processes, configure your own web server, and scale the server as your traffic grows.
But there is an important difference between getting a Node.js application running and running it properly in production.
Starting your application with node app.js may be enough for testing. On a production VPS, you also need to think about what happens when the server reboots, how visitors reach your application, where sensitive credentials are stored, which ports are exposed, and how you will troubleshoot the application when something goes wrong.
This guide walks through a practical production setup using Ubuntu, Node.js, systemd, Nginx, UFW, DNS and HTTPS. The same general approach can also be adapted for applications built with Express, NestJS and other Node.js frameworks.
What you need before starting
Before deploying the application, make sure you have:
- A Linux VPS with SSH access
- A domain name or subdomain
- The source code of your Node.js application
- Access to the application's environment variables
- A supported Node.js version for your project
- Permission to create DNS records for the domain
You do not necessarily need a powerful VPS for a small Node.js application. The right server size depends on traffic, database usage, background jobs, build requirements and memory consumption. It is usually better to start with sensible resources and monitor actual usage than to pay for capacity you do not need.
The basic Node.js VPS deployment process
If you want the short version, a typical production deployment looks like this:
- Prepare and secure the VPS.
- Create a separate Linux user for the application.
- Install Node.js and the required packages.
- Upload or clone the application.
- Install dependencies and build the project.
- Configure environment variables.
- Create a systemd service.
- Put Nginx in front of Node.js.
- Configure the firewall.
- Point your domain to the VPS.
- Install an SSL certificate.
- Test the application, logs and automatic restart.
The important part is that Node.js does not need to be directly exposed to the internet. In a common setup, Nginx receives the visitor's request and forwards it to the Node.js application running privately on the VPS.
Visitor
|
v
Domain
|
v
Nginx :80 / :443
|
v
Node.js :3000
|
v
Application
1. Prepare your Linux VPS
Connect to the VPS through SSH using your administrative account. If the server is new, update the installed packages before starting the deployment.
sudo apt update
sudo apt upgrade -y
Ubuntu and Debian-based systems commonly use apt. Other Linux distributions use different package managers, so the commands will vary if you are using something other than Ubuntu or Debian.
At this stage, it is also worth checking the available resources:
free -h
df -h
nproc
These commands show available memory, disk space and CPU count. A deployment can fail for reasons that have nothing to do with Node.js itself — for example, insufficient RAM during a build or a full disk caused by old logs.
2. Create a separate user for the application
One simple security improvement is to avoid running your application as root.
Create a dedicated user for the Node.js application:
sudo adduser nodeapp
You can choose a different username if you prefer. The important thing is that the application has its own account rather than sharing unnecessary privileges with the rest of the server.
Now create an application directory:
sudo mkdir -p /var/www/myapp
sudo chown -R nodeapp:nodeapp /var/www/myapp
Switch to the application user:
sudo -iu nodeapp
From this point, application files and Node.js commands can be handled under the dedicated account.
3. Install Node.js
Before installing Node.js, check which version your project actually requires. Look at the project's package.json, lockfile and framework documentation rather than simply installing whichever version happens to be available from the operating system repository.
For example, if your project specifies a Node.js engine version, follow that requirement. Using the wrong runtime can result in dependency errors or unexpected behaviour even when the server itself is configured correctly.
After Node.js is installed, verify it:
node -v
npm -v
If your project uses another package manager such as Yarn or pnpm, install and use the version expected by the project.
4. Upload or clone your Node.js application
There are several ways to put your application on the VPS. Git is convenient when the project is maintained in a repository, while SFTP or another secure file-transfer method can be useful for projects that are uploaded manually.
For a Git-based deployment, the process might look like this:
cd /var/www
git clone https://github.com/example/my-node-app.git myapp
cd myapp
Replace the repository address with your actual project repository.
If the directory already exists, make sure the application user owns the files:
sudo chown -R nodeapp:nodeapp /var/www/myapp
Do not copy sensitive files such as production credentials into a public repository just to make deployment easier. Your source code and your production secrets should be treated separately.
5. Install dependencies
Once the application is on the server, install its dependencies.
If your project uses npm and includes a lockfile, a production deployment will often use:
npm ci
For applications that require a build step, run the appropriate build command after installing the dependencies.
npm run build
The exact command depends on your project. A plain Express application may not need a build at all, while a TypeScript, NestJS or framework-based application may produce a compiled output directory.
Check the scripts section of package.json:
{
"scripts": {
"start": "node dist/main.js",
"build": "tsc"
}
}
Do not copy these scripts into your project unless they match your actual application. Your production start command might instead be node server.js, npm start, or another framework-specific command.
6. Test the application before configuring Nginx
This step saves a lot of troubleshooting time later.
Before involving DNS, SSL or Nginx, make sure the Node.js application itself works on the VPS.
For example:
npm start
If the application listens on port 3000, open another SSH session and test it locally:
curl http://127.0.0.1:3000
If you receive a response, the Node.js process is working locally.
If this test fails, there is no point debugging Nginx yet. Check the application logs, environment variables, dependencies and start command first.
7. Configure environment variables
Most real-world Node.js applications need configuration values such as:
- Database credentials
- Application URL
- Session or JWT secrets
- API keys
- Port numbers
- Third-party service credentials
These values should not normally be hard-coded into the source code or committed to Git.
A common approach is to create an environment file:
cd /var/www/myapp
nano .env
A simplified example might look like:
NODE_ENV=production
PORT=3000
APP_URL=https://example.com
DATABASE_URL=your_database_connection_string
SESSION_SECRET=your_long_random_secret
Use the exact variable names required by your application. A variable called DATABASE_URL will not magically work if your application expects DB_CONNECTION.
Protect the file:
chmod 600 .env
Also make sure sensitive environment files are excluded from version control:
echo ".env" >> .gitignore
If credentials have already been committed to a Git repository, simply deleting the file is not enough. The credentials should be considered exposed and replaced.
8. Run Node.js with systemd
Running npm start manually inside an SSH session is fine for testing, but it is not a good production process-management strategy.
Imagine the VPS reboots at 3 AM. You do not want your application to remain offline until somebody logs into the server and starts it again.
Linux's systemd can run the application as a service and start it automatically when the server boots.
Create a service file:
sudo nano /etc/systemd/system/myapp.service
Example:
[Unit]
Description=My Node.js Application
After=network.target
[Service]
Type=simple
User=nodeapp
WorkingDirectory=/var/www/myapp
EnvironmentFile=/var/www/myapp/.env
ExecStart=/usr/bin/node /var/www/myapp/server.js
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Change the username, application directory and Node.js entry point to match your project.
If your application starts through npm instead of directly through Node.js, you can use the appropriate executable path and command for your environment. Confirm the actual Node.js path with:
which node
Reload systemd and start the service:
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
Check its status:
sudo systemctl status myapp
If the service does not start, check the logs:
sudo journalctl -u myapp -n 100 --no-pager
This is usually much more useful than repeatedly restarting the application without checking why it failed.
9. Install and configure Nginx
At this point, your Node.js application may be working on something like 127.0.0.1:3000. Visitors, however, should normally access the application through a domain such as example.com, not by entering a port number.
This is where Nginx comes in.
Install it:
sudo apt install nginx -y
Create a site configuration:
sudo nano /etc/nginx/sites-available/myapp
A basic reverse-proxy configuration can look like this:
server {
listen 80;
server_name example.com www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
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;
}
}
Replace example.com with your actual domain and change port 3000 if your application uses another local port.
Enable the configuration:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
Test the Nginx configuration before reloading it:
sudo nginx -t
If the test is successful:
sudo systemctl reload nginx
Now Nginx receives the web request and forwards it to your Node.js application.
10. Configure the firewall
Your VPS should not expose every service to the public internet.
For a normal web application, the public ports are usually:
- 22 — SSH, if you use the standard SSH port
- 80 — HTTP
- 443 — HTTPS
Before enabling UFW, make sure your SSH connection is allowed. Otherwise, you can accidentally lock yourself out of the server.
Check the current rules:
sudo ufw status
Allow SSH and web traffic:
sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
Then enable the firewall:
sudo ufw enable
Finally, check the result:
sudo ufw status
If Nginx is the public entry point, there is normally no reason to open port 3000 to everyone. Keeping the Node.js service bound to localhost means external users cannot connect directly to it.
11. Point your domain to the VPS
Now connect the domain to your server.
At your DNS provider, create an A record pointing the domain to the VPS's public IPv4 address.
| Type | Name | Value |
|---|---|---|
| A | @ | Your VPS IPv4 address |
| A | www | Your VPS IPv4 address |
The exact DNS records depend on how you want the domain to work. If you are using a subdomain such as app.example.com, create the corresponding record instead.
DNS changes can take time to appear because DNS resolvers cache records according to their TTL.
You can check the DNS response with:
dig example.com +short
The result should show the public IP address of your VPS.
If the domain points somewhere else, fix the DNS before moving on to SSL.
12. Enable HTTPS with an SSL certificate
Once your domain resolves correctly and Nginx is responding, the next step is HTTPS.
On Ubuntu, Certbot is a common option for obtaining and configuring certificates from Let's Encrypt.
sudo apt install certbot python3-certbot-nginx -y
Then request a certificate for your domain:
sudo certbot --nginx -d example.com -d www.example.com
Follow the prompts provided by Certbot. If the DNS records and Nginx configuration are correct, it can configure HTTPS for the domain.
Afterward, test your website using:
https://example.com
Do not stop at checking whether the certificate works today. Automatic certificate renewal is just as important.
You can test the renewal process with:
sudo certbot renew --dry-run
13. Check your Node.js application after HTTPS is enabled
At this point, the expected request path is:
Browser
|
v
HTTPS
|
v
Nginx
|
v
127.0.0.1:3000
|
v
Node.js
Open the website in a browser and test the important parts of the application rather than checking only the homepage.
For example, test:
- Login and logout
- Forms
- API requests
- Database operations
- File uploads
- Authentication cookies
- Background jobs
- Any third-party API integrations
This is particularly important after enabling HTTPS because applications sometimes need additional proxy or secure-cookie configuration.
14. Learn where to look when something breaks
One of the most useful habits when managing a VPS is to troubleshoot one layer at a time.
For the Node.js service:
sudo systemctl status myapp
sudo journalctl -u myapp -n 100 --no-pager
For Nginx:
sudo nginx -t
sudo tail -f /var/log/nginx/error.log
For DNS:
dig example.com
dig example.com +short
For the local Node.js application:
curl http://127.0.0.1:3000
This makes troubleshooting much easier. If the local curl request fails, the problem is probably inside the Node.js application or its service configuration. If the local request works but the domain does not, move up the stack and check Nginx, DNS, firewall and SSL.
Common Node.js VPS deployment mistakes
Running the application as root
There is rarely a good reason for a Node.js application to have unrestricted root privileges. Use a dedicated service user instead.
Leaving port 3000 open to the internet
If Nginx is handling public traffic, keep the Node.js port private. Your application can listen on localhost while Nginx handles requests from the internet.
Keeping passwords inside the source code
Database passwords, API keys and application secrets should not be hard-coded into JavaScript files or committed to Git. Use environment variables or an appropriate secrets-management solution.
Starting Node.js manually
Running node server.js in an SSH session is useful for testing but not for a production service. Use systemd, PM2 or another suitable process manager.
Installing the wrong Node.js version
Not every application works with every Node.js release. Check the requirements of your framework and dependencies before selecting the runtime.
Forgetting the build command
Applications written in TypeScript or frameworks that compile source code often need a production build before they can start.
Changing several things at once
This is a surprisingly common reason VPS troubleshooting becomes difficult. If the website suddenly stops working, check the application, service, Nginx, firewall and DNS separately instead of changing all of them at the same time.
Ignoring memory and disk usage
A VPS can work perfectly for weeks and then fail because the disk becomes full or available RAM becomes too low. Keep an eye on both:
free -h
df -h
Logs, uploaded files, database data and build artifacts can consume more storage than expected on a small server.
Should you use PM2 or systemd for Node.js?
Both are commonly used for running Node.js applications.
systemd is built into modern Linux distributions and integrates naturally with the operating system. It is a good fit when you want your application to behave like a normal Linux service.
PM2 is specifically designed around Node.js process management and provides features such as process monitoring and application management.
Either can work. For a straightforward VPS deployment, the important thing is to choose one process-management approach and configure it properly rather than running multiple process managers for the same application.
Can you host Node.js on shared hosting?
Sometimes, yes.
Some shared hosting providers offer Node.js application support. However, the available Node.js versions, background processes, ports, memory limits and server configuration can be restricted.
A VPS gives you considerably more control, but that control also comes with responsibility. You are responsible for things such as operating-system updates, firewall rules, application security and server-level troubleshooting unless your hosting provider offers management services.
Does every Node.js application need Nginx?
No.
A Node.js application can listen directly on a public port. For many production websites, however, Nginx is useful because it can handle the public HTTP/HTTPS connection and forward requests to the private Node.js process.
It also gives you a convenient place to manage domains, TLS, redirects, static files and other web-server-level rules.
How much VPS RAM does a Node.js application need?
There is no single RAM requirement for every Node.js application.
A small API with little traffic may run comfortably on a modest VPS, while an e-commerce application with a database, image processing, background workers and caching can require considerably more resources.
Your application framework is only one part of the calculation. Traffic, database workload, build processes, uploaded files, workers and caching can all affect resource usage.
Start with a reasonable configuration and monitor the server rather than choosing a VPS solely based on the advertised price.
Why is my domain not opening after deploying Node.js?
Check the deployment in this order:
- Is the Node.js application running?
- Does
curl http://127.0.0.1:3000return a response? - Is Nginx running?
- Does
nginx -tpass? - Does the domain point to the correct VPS IP?
- Are ports 80 and 443 allowed through the firewall?
- Has the SSL certificate been configured correctly?
Testing these layers individually usually identifies the problem much faster than repeatedly restarting the server.
Is a Linux VPS a good choice for Node.js applications?
A Linux VPS can be a practical choice when you need more control over the server environment, custom Node.js versions, background workers, databases, scheduled jobs or application-specific software.
It is also important to remember that a VPS is not completely managed just because the application is online. Someone still needs to maintain the operating system, monitor resources, protect credentials, maintain backups and keep the application dependencies up to date.
If you do not want to manage those responsibilities yourself, managed hosting may be a better fit for your particular project.
Final production checklist
Before considering your Node.js VPS deployment finished, go through this checklist:
- ✓ Node.js version matches the application requirements
- ✓ Application runs under a dedicated Linux user
- ✓ Production dependencies are installed
- ✓ Required build step has been completed
- ✓ Environment variables are protected
- ✓ Node.js is managed by systemd, PM2 or another suitable process manager
- ✓ Nginx is configured as the reverse proxy
- ✓ Node.js is not unnecessarily exposed to the public internet
- ✓ Firewall rules allow only required services
- ✓ DNS points to the correct VPS
- ✓ HTTPS is enabled
- ✓ SSL renewal has been tested
- ✓ Application and Nginx logs can be accessed
- ✓ Server disk and memory usage are monitored
- ✓ Backups are planned and tested
- ✓ The application has been tested after deployment
Final thoughts
Deploying Node.js on a VPS is not particularly difficult once you understand how the different pieces fit together. The Node.js application handles the application logic, systemd keeps the process running, Nginx acts as the public-facing web server, DNS connects the domain to the server, and HTTPS protects the connection.
The biggest mistake is treating production deployment as simply uploading files and starting Node.js. A reliable deployment also needs sensible permissions, protected configuration, automatic restarts, restricted network access, logs and a plan for updates and backups.
Once this basic setup is working, you can build on it with automated deployments, monitoring, database backups, staging environments, load balancing and other infrastructure as your application grows.