Comparison of standard Node.js and Nginx Units: Error and Troubleshooting
Standard Node.js
Standard Node.js
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
// A port is defined manually
app.listen(3000, () => {
console.log('Server running on port 3000');
});
Characteristics
Manual port management
External process managers such as PM2 or Forever are commonly used
Uses the built-in http module
Usually requires a reverse proxy (Nginx or Apache)
Nginx Unit
const http = require('unit-http');
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello World');
});
// No port definition
http.createServer(app).listen();
Characteristics
Automatic port management
Built-in process management
Requires the unit-http module
Reverse proxy functionality is integrated
The unit-http Module
Why It Is Required
Nginx Unit runs Node.js applications through its own HTTP layer. Instead of Node’s standard http module, unit-http must be used so that the application can communicate correctly with Unit.
Installation
npm install unit-http
Note: In DirectAdmin environments this module is often preinstalled, so manual installation may not be necessary.
Safe Fallback Usage
let http;
try {
http = require('unit-http');
console.log('Running in Nginx Unit mode');
} catch (err) {
http = require('http');
console.log('Running in standard Node.js mode');
}
const express = require('express');
const app = express();
http.createServer(app).listen();
This pattern allows the same codebase to run both locally and under Nginx Unit without modification.
DirectAdmin Configuration
Arguments Field
DirectAdmin automatically adds the following arguments:
--loader unit-http/require_shim.mjs --require unit-http/loader
These arguments:
Load the unit-http module
Provide ES Module compatibility
Create the bridge between Node.js and Nginx Unit
They should not be removed. Without them the application may fail to start.
Template Selection
DirectAdmin typically provides three templates:
express – recommended
basic – simple HTTP server
custom – manual configuration
When the express template is selected:
Arguments are filled automatically
unit-http is preconfigured
Express-compatible structure is generated
Route Configuration
Basic Route
{
"routes": [
{
"match": {
"uri": "/*"
},
"action": {
"pass": "applications/myapp"
}
}
]
}
This configuration sends all incoming requests to a single application.
Multiple Applications
{
"routes": [
{
"match": {
"uri": "/api/*"
},
"action": {
"pass": "applications/api"
}
},
{
"match": {
"uri": "/*"
},
"action": {
"pass": "applications/web"
}
}
]
}
This structure allows separation between API and frontend applications.
Common Mistakes
Defining a Port
Incorrect:
http.createServer(app).listen(3000);
Correct:
http.createServer(app).listen();
Nginx Unit manages ports internally.
Using app.listen()
Incorrect:
app.listen(3000);
Correct:
const http = require('unit-http');
http.createServer(app).listen();
Express’s native listen method bypasses Unit’s control.
Removing Arguments
Leaving the arguments field empty can prevent the server from loading required modules.
Wrong Template Choice
Using basic or custom templates without proper configuration often leads to startup issues. The express template is the safest default.
Process Management
Nginx Unit handles:
Automatic restarts after crashes
Reloading on configuration changes
Memory and resource supervision
External managers such as PM2 or Forever are generally unnecessary.
Logging
Logs usually include:
console.log and console.error outputs
Application startup messages
Module loading information
In DirectAdmin, logs are accessible from the Node.js management panel.
Security Features
Process isolation between applications
Automatic restart on failure
CPU and memory limits
Built-in reverse proxy and SSL/TLS support
Best Practices
Use a Fallback Loader
Allows development locally without Unit while remaining compatible in production.
Environment Detection
const isUnit = !!process.env.UNIT_VERSION;
Useful for conditional configuration.
Graceful Shutdown
process.on('SIGTERM', () => {
server.close(() => {
console.log('HTTP server closed');
});
});
Prevents abrupt termination.
Health Check Endpoint
app.get('/health', (req, res) => {
res.json({
status: 'ok',
uptime: process.uptime(),
timestamp: Date.now()
});
});
Helpful for monitoring and load balancers.
Migration from Standard Node.js
Install unit-http
Replace app.listen(PORT) with http.createServer(app).listen()
Remove hard-coded port references
Test locally and then inside the hosting panel
Performance Tips
Use compression middleware
Cache static files with proper headers
Let Unit manage keep-alive connections automatically
Prefer dynamic process scaling when available
Summary
Key principles for working with Nginx Unit:
Use require('unit-http')
Do not define ports manually
Do not remove automatic loader arguments
Prefer the Express template in hosting panels
Avoid unnecessary native or low-level overrides
Following these guidelines results in stable deployment, cleaner configuration, and fewer runtime surprises.
i tried many trial-and-error techniques and, after a lot of effort, I figured out the logic this way... i hope the above explanation will help those who are struggling like me.