Client Area

Hosting Playbook Node/Express API under Apache + Passenger (DirectAdmin)

7 min readPublished 4 Mar 2026Updated 14 Apr 2026973 views

In this article

  • 1Short Summary
  • 2Table of Contents
  • 31) Getting Access (DirectAdmin, SSH, SFTP)
  • 4DirectAdmin
  • 5SSH (shell)

DirectAdmin Hosting & App Deployment

Short Summary

This endtoend guide showsDirectAdmincustomers andweb/app developershow to deploy, secure, and maintain production websites and APIs on example.com. It covers DNS, SSL, SSH, Node.js/Passenger apps, PHP sites, databases (MySQL/MariaDB), logs, backups, and troubleshooting--optimized for speed, reliability, and clarity.


Table of Contents

  1. Getting Access (DirectAdmin, SSH, SFTP)

  2. Pointing Your Domain (DNS example.com)

  3. Free HTTPS (Let's Encrypt) & HSTS

  4. App Layout on the Server

  5. Deploying aNode.js + ExpressAPI withPassenger(behind Apache/Nginx)

  6. PHP Website or Backend (FastCGI/PHPFPM)

  7. Database Setup (MySQL/MariaDB) & Secure Access

  8. Environment Variables & Secrets

  9. Logs, Monitoring, and Alerts

  10. ZeroDowntime Updates & Rollbacks

  11. Backups & Restores

  12. Performance Tuning

  13. Security Hardening

  14. Common Errors & Fixes

  15. Release Checklist (copy/paste)

  16. Reference Commands (cheatsheet)

Who is this forDirectAdmin endclients and their developers. Use it as your standard operating procedure (SOP) for hosting and app operations.


1) Getting Access (DirectAdmin, SSH, SFTP)

DirectAdmin

  • URL:https://example.com:2222

  • Create users:Admin Account Manager Create User

  • Enable 2FA:User Security Questions / Two-Step Authentication

SSH (shell)

ssh [email protected] -p 22

Keys:Add your public key inUser SSH Keys. Disable password login if possible.

SFTP (files)

  • Host:example.com, Port:22, Protocol: SFTP

  • User: your DirectAdmin username


2) Pointing Your Domain (DNS example.com)

Set these at your domain registrar (or use the server's DNS if provided).

A/AAAA records

A @ 203.0.113.10 ; example.com server IPv4
A api 203.0.113.10 ; api.example.com
AAAA @ 2001:db8::10 ; if IPv6 available

CNAME(optional)

CNAME www example.com.

Propagation check:

dig +short A example.com
dig +short A api.example.com

3) Free HTTPS (Let's Encrypt) & HSTS

InDirectAdmin Account Manager SSL Certificates Let's Encrypt:

  1. Select your domain(s):example.com,www.example.com,api.example.com.

  2. CheckForce SSLandRedirect HTTP to HTTPS(inDomain SetuporHTACCESS).

  3. (Optional) EnableHSTS:

# .htaccess (public_html/ or app root)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

4) App Layout on the Server

/home/USERNAME/
 domains/
 example.com/
 public_html/ # public web root (static/PHP)
 private/ # app code not publicly served
 slkapi/ # example API app
 server.js
 routes/
 db.js
 package.json
 tmp/ # Passenger restart file, boot logs, etc.
 backups/

Keep application codeoutsidepublic_htmlunless it must be public.


5) Deploying a Node.js + Express API with Passenger

Why PassengerDirectAdmin often uses Apache/Nginx + Passenger to run Node.js apps efficiently.

5.1 Install app dependencies (per user)

cd ~/domains/example.com/private/slkapi
node -v && npm -v
npm ci --only=production || npm install --production

5.2 Minimalserver.js(health checks, JSON API, graceful errors)

// server.js
const express = require('express');
const fs = require('fs');
const path = require('path');
const PORT = process.env.PORT || 5000;
const app = express();

app.use(express.json());
app.use(express.urlencoded({ extended: true }));

// boot log
const TMP = path.join(__dirname, 'tmp');
try { fs.mkdirSync(TMP, { recursive: true }); } catch {}
const BOOT_LOG = path.join(TMP, 'boot.log');
const log = (...a) => { const s=a.map(x=>typeof x==='string'x:JSON.stringify(x)).join(' ');
 console.log(s); try{fs.appendFileSync(BOOT_LOG, s+'
');}catch{} };

// health
app.get('/', (req,res)=>res.json({status:'OK',message:'API root'}));
app.get('/api', (req,res)=>res.json({status:'OK',message:'API base under /api'}));

// routers
app.use('/api/boardMembers', require('./routes/boardMembers'));
app.use('/api/poojaDetails', require('./routes/poojaDetails'));
app.use('/api/festivalDetails', require('./routes/festivalDetails'));

// 404 + errors (json)
app.use('/api', (req,res)=>res.status(404).json({error:'Not found', path:req.originalUrl}));
app.use('/api', (err,req,res,next)=>{ log('ERR', err.stack||err.message); res.status(500).json({error:'Internal Server Error'}); });

app.listen(PORT, ()=>log('listening on', PORT));

5.3 Hook Passenger to your app

Inpublic_html/create apassenger-nodeentry point (app.js):

// public_html/app.js -- Passenger entry
const path = require('path');
process.chdir(path.join(__dirname, '..', 'private', 'slkapi'));
require('./server');

Passenger autodetects Node apps in the web root. If you already have anapp.jsthere, only update theprocess.chdirpath.

5.4 Restart Passenger

cd ~/domains/example.com/private/slkapi
mkdir -p tmp && touch tmp/restart.txt

Verify:

curl -sS https://example.com/api/ | jq .

5.5 Debug mounted routes

Add a debug route:

app.get('/api/_routes', (req,res)=>{
 const out=[]; const scan=(st,p='')=>st.forEach(l=>{ if(l.route){
 out.push({path:p+l.route.path,methods:Object.keys(l.route.methods)});
 } else if(l.name==='router' && l.handle.stack){ scan(l.handle.stack, p+(l.regexp.fast_slash'':(l.path||''))); }});
 if(app._router.stack) scan(app._router.stack);
 res.json({routes:out});
});

6) PHP Website or Backend (FastCGI/PHPFPM)

  • Upload PHP topublic_html/.

  • Select PHP version:User Select PHP Version.

  • Composer:

cd ~/domains/example.com/public_html
php -v
php -d detect_unicode=0 ~/composer.phar install --no-dev --optimize-autoloader

7) Database Setup (MySQL/MariaDB)

Create DB & user in DirectAdmin

Account Manager MySQL Management Create new Database

  • DB name:example_db

  • User:example_user

  • Strong password

Connect from Node.js (mysql2)

db.js

const cfg = (()=>{ try{ return require('./config'); }catch{return {}; } })();
const mysql = require('mysql2/promise');
module.exports = mysql.createPool({
 host: process.env.DB_HOST || cfg.DB_HOST || '127.0.0.1',
 user: process.env.DB_USER || cfg.DB_USER || 'example_user',
 password: process.env.DB_PASS || cfg.DB_PASS || 'REDACTED',
 database: process.env.DB_NAME || cfg.DB_NAME || 'example_db',
 port: Number(process.env.DB_PORT || cfg.DB_PORT || 3306),
 waitForConnections: true, connectionLimit: 10, queueLimit: 0,
 enableKeepAlive: true, keepAliveInitialDelay: 10000, connectTimeout: 20000,
});

DB connectivity test route

// routes/boardMembers.js
router.get('/_dbping', async (req,res)=>{
 try{ const [rows]=await db.query('SELECT 1 ok, DATABASE() db, NOW() now'); res.json({ok:true, info:rows[0]}); }
 catch(e){ res.status(500).json({ok:false, code:e.code, errno:e.errno, fatal:!!e.fatal, message:e.message}); }
});

Fix common DB errors

  • ER_ACCESS_DENIED_ERROR: wrong user/password or missing privileges reset inMySQL Management.

  • ECONNRESETafter idle: enable keepalive (above), use a pool, or reconnect on error.


8) Environment Variables & Secrets

Option A:.envfile (private/)

DB_HOST=127.0.0.1
DB_USER=example_user
DB_PASS=super-secret
DB_NAME=example_db
NODE_ENV=production

Load withdotenvinserver.js(optional).

Option B: DirectAdmin custom environment

  • If your template supports it:User Custom HTTPD Configor vendor plugin to set env in Apache/nginx/Passenger context.

Never commit secrets to Git.


9) Logs, Monitoring, and Alerts

  • Web logs:~/domains/example.com/logs/

  • Passenger/Node logs:your app'stmp/boot.log+ DirectAdmin's application logs

  • Tail live:

tail -f ~/domains/example.com/private/slkapi/tmp/boot.log
  • Uptime probe:add an external monitor hittinghttps://example.com/api/every 1-5 minutes.


10) ZeroDowntime Updates & Rollbacks

  1. git pullor upload new build intoprivate/slkapi/

  2. npm ci --only=production

  3. Run database migrations (if any)

  4. Smoke test(curl health,_routes)

  5. touch tmp/restart.txt(Passenger restarts quickly)

  6. If bad release:git reset --hard <last-good>touch tmp/restart.txt


11) Backups & Restores

Files

  • DirectAdminCreate/Restore Backups(automate daily + 7/30 day retention)

  • Or manual:

tar -czf ~/backups/site-$(date +%F).tgz ~/domains/example.com

Database

mysqldump -u example_user -p example_db > ~/backups/db-$(date +%F).sql
mysql -u example_user -p example_db < ~/backups/db-YYYY-MM-DD.sql

Store offserver (object storage) for disaster recovery.


12) Performance Tuning

  • Keep Node hot(Passenger manages worker lifecycle)

  • Gzip/Brotlivia server; cache static assets:

# .htaccess in public_html
<IfModule mod_expires.c>
 ExpiresActive On
 ExpiresByType text/css "access plus 7 days"
 ExpiresByType application/javascript "access plus 7 days"
 ExpiresByType image/png "access plus 30 days"
 ExpiresByType image/jpeg "access plus 30 days"
</IfModule>
  • Use a CDN for images/assets if global audience.


13) Security Hardening

  • Force HTTPS+HSTS

  • Firewall: allow 80/443/22 only; restrict MySQL to localhost

  • SSH keysonly; disable root SSH if you manage the server

  • Secretsin env, not code

  • Principle of least privilegefor DB users

  • ModSecurity/WAF(if provided) and fail2ban (providerside)


14) Common Errors & Fixes

  • 404 Not Found on /api/ the app isn't mounted; checkserver.js, confirmtmp/restart.txttouched, view/api/_routes.

  • Passenger "Error starting web application" open app'stmp/boot.log; runnode server.jslocally to catch syntax/runtime errors.

  • ER_ACCESS_DENIED_ERROR fix DB creds/privileges in DirectAdmin; retest/api/boardMembers/_dbping.

  • ECONNRESET use pooled connections and keepalive (seedb.js).

  • CORSissues set explicit CORS headers in Express if calling from browsers.


15) Release Checklist

  • DNS records correct IPs (A/AAAA)

  • Valid HTTPS (Let's Encrypt) on all hostnames

  • App code inprivate/, public files inpublic_html/

  • npm ci --only=productioncompleted

  • server.jsmounts routers at/api/...

  • DB credentials verified;_dbpingpasses

  • Logs clean aftertouch tmp/restart.txt

  • Smoke tests:GET /api/, critical endpoints return 200

  • Backups scheduled and tested restore


16) Reference Commands (Cheatsheet)

# SSH
ssh [email protected]

# Passenger restart
cd ~/domains/example.com/private/slkapi && mkdir -p tmp && touch tmp/restart.txt

# Health checks
curl -sS https://example.com/api/ | jq .
curl -sS https://example.com/api/_routes | jq .

# Logs
tail -n 200 ~/domains/example.com/private/slkapi/tmp/boot.log

# DB ping (custom route)
curl -sS https://example.com/api/boardMembers/_dbping | jq .

Conclusion / Next Steps

You now have acomplete, productionready workflowfor hosting onDirectAdmin: DNS, SSL, app deployment with Passenger, DB connectivity, logging, backups, performance, and security. Use the checklist on every release. For teams, bake this guide into your onboarding and CI/CD runbooks for consistent, repeatable, andguaranteedresults.


Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket