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

  • 0

🚀 DirectAdmin Hosting & App Deployment

✍️ Short Summary

This end‑to‑end guide shows DirectAdmin customers and web/app developers how 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 a Node.js + Express API with Passenger (behind Apache/Nginx)

  6. PHP Website or Backend (FastCGI/PHP‑FPM)

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

  8. Environment Variables & Secrets

  9. Logs, Monitoring, and Alerts

  10. Zero‑Downtime 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 for? DirectAdmin end‑clients 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 username@example.com -p 22

Keys: Add your public key in User → 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

In DirectAdmin → Account Manager → SSL Certificates → Let’s Encrypt:

  1. Select your domain(s): example.comwww.example.comapi.example.com.

  2. Check Force SSL and Redirect HTTP to HTTPS (in Domain Setup or HTACCESS).

  3. (Optional) Enable HSTS:

# .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 code outside public_html unless it must be public.


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

Why Passenger? DirectAdmin 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 Minimal server.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

In public_html/ create a passenger-node entry point (app.js):

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

Passenger auto‑detects Node apps in the web root. If you already have an app.js there, only update the process.chdir path.

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/PHP‑FPM)

  • Upload PHP to public_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 in MySQL Management.

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


8) 🔑 Environment Variables & Secrets

Option A: .env file (private/)

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

Load with dotenv in server.js (optional).

Option B: DirectAdmin custom environment

  • If your template supports it: User → Custom HTTPD Config or 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’s tmp/boot.log + DirectAdmin’s application logs

  • Tail live:

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


10) 🔁 Zero‑Downtime Updates & Rollbacks

  1. git pull or upload new build into private/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

  • DirectAdmin Create/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 off‑server (object storage) for disaster recovery.


12) ⚡ Performance Tuning

  • Keep Node hot (Passenger manages worker lifecycle)

  • Gzip/Brotli via 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 keys only; disable root SSH if you manage the server

  • Secrets in env, not code

  • Principle of least privilege for DB users

  • ModSecurity/WAF (if provided) and fail2ban (provider‑side)


14) 🧰 Common Errors & Fixes

  • 404 Not Found on /api/ → the app isn’t mounted; check server.js, confirm tmp/restart.txt touched, view /api/_routes.

  • Passenger “Error starting web application” → open app’s tmp/boot.log; run node server.js locally 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 (see db.js).

  • CORS issues → 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 in private/, public files in public_html/

  • npm ci --only=production completed

  • server.js mounts routers at /api/...

  • DB credentials verified; _dbping passes

  • Logs clean after touch tmp/restart.txt

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

  • Backups scheduled and tested restore


16) 🧾 Reference Commands (Cheatsheet)

# SSH
ssh username@example.com

# 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 a complete, production‑ready workflow for hosting on DirectAdmin: 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, and guaranteed results.



Was this answer helpful?

« Back