How to Deploy a Node.js Application on Windows Server 2025
Node.js has become the runtime of choice for modern JavaScript-based backends, APIs, and real-time applications. Deploying a Node.js application to Windows Server 2025 for production use requires more than just running node app.js in a terminal — you need a process manager to keep the application alive after crashes and reboots, a way to manage environment variables securely, and ideally a robust web server like IIS acting as a reverse proxy to handle SSL termination and static file serving. This guide covers the complete deployment stack: installing Node.js, configuring PM2 as a process manager, setting up PM2 as a Windows service, and routing traffic from IIS to your Node application using Application Request Routing (ARR) or the httpPlatformHandler module.
Prerequisites
- Windows Server 2025 with administrative privileges
- IIS installed (via Server Manager → Add Roles and Features) if using IIS as a reverse proxy
- A Node.js application with a defined entry point (e.g.,
app.jsorserver.js) - Internet access to download Node.js installer or Chocolatey packages
- PowerShell 5.1 or later (included in Windows Server 2025)
Step 1: Install Node.js on Windows Server 2025
There are two recommended methods for installing Node.js on Windows Server 2025: the official MSI installer from nodejs.org, or the Chocolatey package manager for scripted/repeatable installations.
Option A: MSI Installer
Download the LTS release from https://nodejs.org/en/download/. Run the installer with administrator privileges and ensure that the option to add Node.js to the system PATH is checked. Accept the option to install the necessary tools (Python and Visual Studio Build Tools) if you plan to build native modules.
Option B: Chocolatey (Recommended for Automation)
# Install Chocolatey (if not already installed)
Set-ExecutionPolicy Bypass -Scope Process -Force
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072
iex ((New-Object System.Net.WebClient).DownloadString('https://community.chocolatey.org/install.ps1'))
# Install Node.js LTS
choco install nodejs-lts -y
# Verify installation
node --version
npm --version
After installation, open a new PowerShell window to ensure the updated PATH is loaded before continuing.
Step 2: Deploy Your Application Files
Copy your application to a suitable directory. Avoid placing application files under C:inetpub unless IIS is serving them directly — a dedicated directory is cleaner:
# Create application directory
New-Item -ItemType Directory -Path "C:Appsmyapp" -Force
# Copy application files (adjust source path as needed)
Copy-Item -Path "\fileserverreleasesmyapp*" -Destination "C:Appsmyapp" -Recurse
# Install production dependencies
cd C:Appsmyapp
npm install --omit=dev
Step 3: Install and Configure PM2
PM2 is a production-grade process manager for Node.js. It provides automatic restarts on crash, log management, cluster mode for multi-core utilization, and startup scripts.
# Install PM2 globally
npm install -g pm2
# Start your application with PM2
pm2 start C:Appsmyappapp.js --name myapp
# Start with specific Node options or environment
pm2 start C:Appsmyappapp.js --name myapp --env production
# View running processes
pm2 list
# View logs
pm2 logs myapp
# Monitor CPU and memory in real time
pm2 monit
Using an Ecosystem Configuration File
For more complex deployments, create a pm2.ecosystem.config.js file in your application root:
// C:Appsmyappecosystem.config.js
module.exports = {
apps: [
{
name: "myapp",
script: "./app.js",
instances: "max", // use all CPU cores
exec_mode: "cluster",
watch: false,
max_memory_restart: "512M",
env: {
NODE_ENV: "development",
PORT: 3000
},
env_production: {
NODE_ENV: "production",
PORT: 3000
},
error_file: "C:/Apps/logs/myapp-error.log",
out_file: "C:/Apps/logs/myapp-out.log",
log_date_format: "YYYY-MM-DD HH:mm:ss"
}
]
};
# Start using ecosystem config
pm2 start ecosystem.config.js --env production
# Save the process list so PM2 can restore it on restart
pm2 save
Step 4: Configure PM2 as a Windows Service
PM2’s built-in startup support for Windows requires the pm2-startup approach using the pm2 startup windows command combined with a service wrapper. The most reliable method on Windows Server 2025 is using @pm2/io or the pm2-windows-startup package:
# Install pm2-windows-startup
npm install -g pm2-windows-startup
# Register PM2 to start with Windows
pm2-startup install
# Save current process list
pm2 save
Alternatively, use NSSM (Non-Sucking Service Manager) for a more robust Windows service wrapper:
# Install NSSM via Chocolatey
choco install nssm -y
# Create a Windows service that starts PM2 and restores saved processes
nssm install PM2 "C:Program Filesnodejsnode.exe"
nssm set PM2 AppParameters "C:UsersAdministratorAppDataRoamingnpmnode_modulespm2binpm2 resurrect"
nssm set PM2 AppDirectory "C:Appsmyapp"
nssm set PM2 Start SERVICE_AUTO_START
nssm start PM2
Step 5: Set Environment Variables on Windows
Never hard-code secrets in application code. On Windows Server, set environment variables at the system level:
# Set a persistent system-level environment variable
[System.Environment]::SetEnvironmentVariable("DATABASE_URL", "Server=dbserver;Database=mydb;User=app;Password=secret;", "Machine")
[System.Environment]::SetEnvironmentVariable("JWT_SECRET", "your-secret-here", "Machine")
[System.Environment]::SetEnvironmentVariable("NODE_ENV", "production", "Machine")
# Or via setx for the current user scope
setx DATABASE_URL "Server=dbserver;Database=mydb;" /M
After setting machine-level variables, restart the PM2 service or reboot the server so the process picks up the new environment.
Step 6: Configure IIS as a Reverse Proxy
Install the required IIS modules: Application Request Routing (ARR) and URL Rewrite. Both are available as free downloads from the Microsoft Web Platform Installer or directly from IIS.net.
Create a new IIS site pointing to a placeholder directory, then add a web.config file to proxy all traffic to your Node.js application running on port 3000:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="ReverseProxyToNode" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https?)://" />
</conditions>
<action type="Rewrite" url="http://localhost:3000/{R:1}" />
</rule>
</rules>
</rewrite>
<httpProtocol>
<customHeaders>
<add name="X-Forwarded-Proto" value="https" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>
Enable proxy mode in ARR by navigating to the IIS manager root node → Application Request Routing Cache → Server Proxy Settings → check Enable proxy.
Step 7: Verify the Deployment
# Check PM2 process status
pm2 list
# Tail application logs
pm2 logs myapp --lines 50
# Test the Node.js app directly (bypass IIS)
curl http://localhost:3000/
# Test through IIS (if configured on port 80)
curl http://localhost/
Conclusion
Your Node.js application is now running on Windows Server 2025 in a production-ready configuration: managed by PM2 with automatic restarts and cluster mode, persisted as a Windows service so it survives reboots, and fronted by IIS which handles SSL termination and acts as a reverse proxy. This architecture allows you to host multiple Node.js applications on the same server on different ports, all routed through a single IIS instance on port 443. For future deployments, the ecosystem.config.js file can be version-controlled alongside your application to ensure consistent startup configuration across environments.