📑 Table of Contents
- Why Single Server is NOT Recommended
- Third-Party Integration & Licensing
- Recommended Enterprise Architecture
- Step 1: Load Balancer Configuration
- Step 2: Application Server Configuration
- Step 3: Database Server Configuration
- Step 4: Database Replication
- Step 5: Shared Storage Configuration
- Step 6: Environment Variables
- Step 7: App-Database Integration
- Step 8: Primary/Secondary Configuration
- Monitoring & Health Checks
- Backup & Disaster Recovery
- Pre-Production Checklist
⚠️ CRITICAL: Why Single Server Deployment is NOT Recommended
A single-server deployment (application + database on same server) poses significant risks:
| Risk | Impact | Consequence |
|---|---|---|
| Hardware Failure | Complete system outage | All ITSM operations halt, tickets unreachable |
| Disk Failure | Data loss | Incidents, changes, assets, CMDB data permanently lost |
| Memory Exhaustion | Application crash | Database corruption possible during crash |
| No Failover | Extended downtime | SLA breaches, customer dissatisfaction |
| Maintenance Windows | Service unavailable | Cannot patch/upgrade without downtime |
| Performance Bottleneck | Slow response | App and DB compete for resources |
Real-World Scenario: If a single server hosting both application and database experiences disk failure, you lose:Recovery could take days and data may be unrecoverable.
- All incident history and SLA records
- CMDB and asset relationships
- Change management audit trails
- Knowledge base articles
- Customer data and configurations
📋 Third-Party Integration & Licensing Requirements
Important Licensing Notice
MinusNow integrates with various third-party systems. Customers are responsible for obtaining appropriate licenses from their respective vendors for any third-party software or services used.
Open Source vs Licensed Components
| Component | Open Source (Free) | Licensed Version (Requires Purchase) |
|---|---|---|
| PostgreSQL | PostgreSQL (MIT) | EnterpriseDB, AWS RDS PostgreSQL |
| Redis | Redis OSS | Redis Enterprise (clustering, HA) |
| LDAP/AD | OpenLDAP | Microsoft Active Directory |
| SMTP | Postfix, hMailServer | Exchange, Office 365, SendGrid |
| Monitoring | Prometheus, Grafana OSS | Datadog, New Relic, Grafana Enterprise |
| Backup | pg_dump, rsync | Veeam, Commvault, AWS Backup |
| Load Balancer | HAProxy, Nginx OSS | F5, AWS ALB/NLB, Azure LB |
| SSL Certificates | Let's Encrypt | DigiCert, GlobalSign, Sectigo |
License Procurement Checklist
Before deploying MinusNow in production, ensure you have:
- Database License - PostgreSQL (free) or commercial variant with support
- Operating System License - Windows Server CALs, RHEL subscription, or free alternatives
- SSL/TLS Certificate - Valid certificate from trusted CA
- SMTP Service - Email relay service (SendGrid, AWS SES, or enterprise email)
- Load Balancer - Hardware or software load balancer license if applicable
- Monitoring Tools - Enterprise monitoring licenses if using commercial tools
- Backup Software - Enterprise backup solution license
- Active Directory - Windows Server CALs for AD integration
Vendor Contact Information
| Vendor | Product | Licensing Portal |
|---|---|---|
| Microsoft | Windows Server, SQL Server, AD | microsoft.com/licensing |
| Red Hat | RHEL | redhat.com/en/store |
| EnterpriseDB | PostgreSQL Enterprise | enterprisedb.com/pricing |
| Redis Labs | Redis Enterprise | redis.com/pricing |
| VMware | vSphere | vmware.com/products |
🏗️ Recommended Enterprise Architecture
Minimum Production Architecture (High Availability)
┌─────────────────────────────────────┐
│ INTERNET │
└──────────────┬──────────────────────┘
│
▼
┌──────────────────────────────┐
│ Load Balancer (HA) │
│ (HAProxy / Nginx / ALB) │
│ Port 443 (HTTPS) │
└──────────┬───────────────────┘
│
┌─────────────────┴─────────────────┐
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ APP SERVER 1 │ │ APP SERVER 2 │
│ (Primary Node) │ │ (Secondary Node) │
│ • Node.js 20 LTS │ │ • Node.js 20 LTS │
│ • Port 5000 │ │ • Port 5000 │
│ CPU: 4+ cores │ │ CPU: 4+ cores │
│ RAM: 8-16 GB │ │ RAM: 8-16 GB │
└───────────┬───────────┘ └───────────┬───────────┘
│ │
└─────────────────┬───────────────┘
│
▼
┌──────────────────────────────┐
│ SHARED FILE STORAGE │
│ (NFS / Azure Files / EFS) │
│ • /data • /audit-logs │
└──────────────────────────────┘
│
┌─────────────────┴─────────────────┐
│ │
▼ ▼
┌───────────────────────┐ ┌───────────────────────┐
│ DATABASE SERVER 1 │ │ DATABASE SERVER 2 │
│ (Primary) │◄───────►│ (Replica/Standby) │
│ • PostgreSQL 15+ │ Sync │ • PostgreSQL 15+ │
│ CPU: 4+ cores │ │ CPU: 4+ cores │
│ RAM: 16-32 GB │ │ RAM: 16-32 GB │
│ Storage: 500 GB SSD │ │ Storage: 500 GB SSD │
└───────────────────────┘ └───────────────────────┘
Server Roles and Responsibilities
| Server Role | Primary Function | Minimum Specs | Recommended Specs |
|---|---|---|---|
| Load Balancer | Traffic distribution, SSL termination, health checks | 2 CPU, 4 GB RAM | 4 CPU, 8 GB RAM |
| App Server (Primary) | Request processing, business logic, API handling | 4 CPU, 8 GB RAM | 8 CPU, 16 GB RAM |
| App Server (Secondary) | Failover, load sharing, horizontal scaling | 4 CPU, 8 GB RAM | 8 CPU, 16 GB RAM |
| DB Server (Primary) | Data storage, query processing, transactions | 4 CPU, 16 GB RAM | 8 CPU, 32 GB RAM |
| DB Server (Replica) | Read replicas, failover, backup source | 4 CPU, 16 GB RAM | 8 CPU, 32 GB RAM |
🔧 Step 1: Load Balancer Configuration
Option A: HAProxy (Open Source)
Installation (Ubuntu/Debian):
sudo apt update
sudo apt install haproxy -y
Configuration (/etc/haproxy/haproxy.cfg):
global
log /dev/log local0
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
user haproxy
group haproxy
daemon
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
timeout connect 5000
timeout client 50000
timeout server 50000
frontend https_front
bind *:443 ssl crt /etc/ssl/private/minusnow.pem
bind *:80
redirect scheme https code 301 if !{ ssl_fc }
# Security headers
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
http-response set-header X-Content-Type-Options "nosniff"
http-response set-header X-Frame-Options "SAMEORIGIN"
default_backend minusnow_app_servers
backend minusnow_app_servers
balance roundrobin
option httpchk GET /health
http-check expect status 200
# Sticky sessions
cookie SERVERID insert indirect nocache
# Application servers
server app1 10.0.1.10:5000 check cookie app1 weight 100
server app2 10.0.1.11:5000 check cookie app2 weight 100 backup
listen stats
bind 127.0.0.1:8404
stats enable
stats uri /stats
stats refresh 10s
Enable and start HAProxy:
sudo systemctl enable haproxy
sudo systemctl start haproxy
sudo systemctl status haproxy
Option B: Nginx (Open Source)
upstream minusnow_backend {
least_conn;
server 10.0.1.10:5000 weight=5;
server 10.0.1.11:5000 weight=5 backup;
keepalive 32;
}
server {
listen 80;
server_name minusnow.yourdomain.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name minusnow.yourdomain.com;
ssl_certificate /etc/ssl/certs/minusnow.crt;
ssl_certificate_key /etc/ssl/private/minusnow.key;
ssl_protocols TLSv1.2 TLSv1.3;
# Security Headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
location / {
proxy_pass http://minusnow_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
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;
}
}
🔧 Step 2: Application Server Configuration
Install Node.js 20 LTS
Ubuntu/Debian:
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt install -y nodejs
node --version # Should show v20.x.x
npm --version
RHEL/CentOS:
curl -fsSL https://rpm.nodesource.com/setup_20.x | sudo bash -
sudo yum install -y nodejs
Windows Server (PowerShell):
winget install OpenJS.NodeJS.LTS
Deploy MinusNow Application
# Create application directory
sudo mkdir -p /opt/minusnow
sudo chown -R $USER:$USER /opt/minusnow
cd /opt/minusnow
# Copy application files from deployment package
# Install dependencies
npm install --production
# Build the application
npm run build
Create Systemd Service (Linux)
File: /etc/systemd/system/minusnow.service
[Unit]
Description=MinusNow ITSM Platform
After=network.target
[Service]
Type=simple
User=minusnow
Group=minusnow
WorkingDirectory=/opt/minusnow
ExecStart=/usr/bin/node dist/index.cjs
Restart=always
RestartSec=10
Environment=NODE_ENV=production
Environment=PORT=5000
Environment=DATABASE_URL=postgres://minusnow:password@10.0.2.10:5432/minusnow_db
LimitNOFILE=65535
NoNewPrivileges=true
ProtectSystem=strict
ReadWritePaths=/opt/minusnow/data /opt/minusnow/audit-logs
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl enable minusnow
sudo systemctl start minusnow
🔧 Step 3: Database Server Configuration
PostgreSQL Installation
Ubuntu/Debian:
sudo apt install postgresql-15 postgresql-contrib-15 -y
sudo systemctl enable postgresql
sudo systemctl start postgresql
RHEL/CentOS:
sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
sudo dnf install -y postgresql15-server postgresql15-contrib
sudo /usr/pgsql-15/bin/postgresql-15-setup initdb
sudo systemctl enable postgresql-15
sudo systemctl start postgresql-15
Configure PostgreSQL for Production
Edit /etc/postgresql/15/main/postgresql.conf:
# Connection Settings
listen_addresses = '*'
port = 5432
max_connections = 200
# Memory Settings (adjust based on RAM)
shared_buffers = 4GB # 25% of RAM
effective_cache_size = 12GB # 75% of RAM
maintenance_work_mem = 1GB
work_mem = 64MB
# Write Ahead Log (for replication)
wal_level = replica
max_wal_senders = 3
wal_keep_size = 1GB
# Performance (for SSD)
random_page_cost = 1.1
effective_io_concurrency = 200
Create Database and User
sudo -u postgres psql
-- Create application user
CREATE USER minusnow WITH ENCRYPTED PASSWORD 'YourSecurePassword123!';
-- Create database
CREATE DATABASE minusnow_db OWNER minusnow;
-- Grant privileges
GRANT ALL PRIVILEGES ON DATABASE minusnow_db TO minusnow;
-- Create replication user
CREATE USER replicator WITH REPLICATION ENCRYPTED PASSWORD 'ReplicatorPassword123!';
\q
🔧 Step 4: Database Replication (Replica Server)
Set Up Streaming Replication
On Replica Server:
# 1. Stop PostgreSQL and clear data directory
sudo systemctl stop postgresql
sudo rm -rf /var/lib/postgresql/15/main/*
# 2. Take base backup from primary
sudo -u postgres pg_basebackup -h 10.0.2.10 -D /var/lib/postgresql/15/main -U replicator -P -R -X stream
# 3. Verify standby.signal file exists
ls -la /var/lib/postgresql/15/main/standby.signal
# 4. Start PostgreSQL on replica
sudo systemctl start postgresql
# 5. Verify replication status on primary
psql -U postgres -c "SELECT client_addr, state, sync_state FROM pg_stat_replication;"
🔧 Step 5: Shared Storage Configuration
Option A: NFS (Linux)
NFS Server Setup:
sudo apt install nfs-kernel-server -y
sudo mkdir -p /exports/minusnow/{data,audit-logs,support-tickets,artifacts}
sudo chown -R nobody:nogroup /exports/minusnow
echo "/exports/minusnow 10.0.1.0/24(rw,sync,no_subtree_check,no_root_squash)" | sudo tee -a /etc/exports
sudo exportfs -ra
sudo systemctl restart nfs-kernel-server
NFS Client Setup (App Servers):
sudo apt install nfs-common -y
sudo mkdir -p /opt/minusnow/{data,audit-logs,support-tickets,artifacts}
# Add to /etc/fstab
echo "10.0.3.10:/exports/minusnow/data /opt/minusnow/data nfs defaults 0 0" | sudo tee -a /etc/fstab
sudo mount -a
🔧 Step 6: Environment Variables Configuration
Create Environment File
File: /opt/minusnow/.env
# Application Settings
NODE_ENV=production
PORT=5000
APP_BASE_URL=https://minusnow.yourdomain.com
# Session Security
SESSION_SECRET=your-secure-random-string-minimum-32-characters-long
# Database Configuration
DATABASE_URL=postgres://minusnow:YourSecurePassword@10.0.2.10:5432/minusnow_db
# Email Configuration (SMTP)
SMTP_HOST=smtp.yourdomain.com
SMTP_PORT=587
SMTP_USER=noreply@yourdomain.com
SMTP_PASS=your-smtp-password
# Support Email
SUPPORT_EMAIL=support@yourdomain.com
# Optional: Redis (for session clustering)
REDIS_URL=redis://10.0.4.10:6379
🔗 Step 7: Application and Database Server Integration
Network Architecture
┌─────────────────────────────────────────────────────────────────────────────┐
│ PRIVATE NETWORK (10.0.0.0/16) │
├─────────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────┐ ┌─────────────────────────────────────┐ │
│ │ APP SERVER 1 │ │ DATABASE TIER │ │
│ │ 10.0.1.10:5000 │────────►│ │ │
│ │ │ │ ┌─────────────────┐ │ │
│ │ DATABASE_URL= │ │ │ PRIMARY DB │ │ │
│ │ postgres://minusnow: │ │ │ 10.0.2.10:5432 │ │ │
│ │ @10.0.2.10:5432/db │ │ └────────┬────────┘ │ │
│ └─────────────────────┘ │ │ Streaming │ │
│ │ │ Replication │ │
│ ┌─────────────────────┐ │ ▼ │ │
│ │ APP SERVER 2 │ │ ┌─────────────────┐ │ │
│ │ 10.0.1.11:5000 │────────►│ │ REPLICA DB │ │ │
│ │ │ │ │ 10.0.2.11:5432 │ │ │
│ └─────────────────────┘ │ │ (Read-Only) │ │ │
│ │ └─────────────────┘ │ │
└─────────────────────────────────────────────────────────────────────────────┘
Pre-Integration Checklist
| Step | Task | Command to Verify |
|---|---|---|
| 1 | Database server is running | systemctl status postgresql |
| 2 | Database user created | psql -U postgres -c "\du" |
| 3 | Database created | psql -U postgres -c "\l" |
| 4 | Network connectivity | nc -zv 10.0.2.10 5432 |
| 5 | Firewall allows 5432 | sudo iptables -L -n | grep 5432 |
| 6 | pg_hba.conf allows app IPs | Check /etc/postgresql/15/main/pg_hba.conf |
Configure Database Access
On Database Server - Add to pg_hba.conf:
# Allow connections from Application Servers
host minusnow_db minusnow 10.0.1.10/32 scram-sha-256
host minusnow_db minusnow 10.0.1.11/32 scram-sha-256
# Or allow entire app subnet
host minusnow_db minusnow 10.0.1.0/24 scram-sha-256
Open firewall:
# Ubuntu/Debian (ufw)
sudo ufw allow from 10.0.1.0/24 to any port 5432
# RHEL/CentOS (firewalld)
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="10.0.1.0/24" port protocol="tcp" port="5432" accept'
sudo firewall-cmd --reload
Test Database Connectivity
# Install PostgreSQL client
sudo apt install postgresql-client -y
# Test connection from App Server
psql -h 10.0.2.10 -U minusnow -d minusnow_db -c "SELECT version();"
Initialize Database Schema
cd /opt/minusnow
# Run database migrations
npm run db:push
# Or if using Drizzle
npx drizzle-kit push
# Verify tables created
psql -h 10.0.2.10 -U minusnow -d minusnow_db -c "\dt"
🔄 Step 8: Primary and Secondary Application Server Configuration
Active-Active vs Active-Passive
| Configuration | Description | Use Case |
|---|---|---|
| Active-Active | Both servers handle requests simultaneously | High traffic, load balancing |
| Active-Passive | Secondary only activates if primary fails | Cost-sensitive, disaster recovery |
Active-Active Configuration (Recommended)
┌─────────────────────┐
│ Load Balancer │
│ (HAProxy/Nginx) │
└──────────┬──────────┘
│
┌────────────────┴────────────────┐
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ APP SERVER 1 │ │ APP SERVER 2 │
│ 10.0.1.10:5000 │ │ 10.0.1.11:5000 │
│ Weight: 100 │ │ Weight: 100 │
└─────────┬───────┘ └─────────┬───────┘
│ │
└────────────┬────────────────────┘
│
▼
┌─────────────────────┐
│ Shared Storage │
└─────────────────────┘
HAProxy Configuration:
backend minusnow_app_servers
balance roundrobin
option httpchk GET /health
http-check expect status 200
cookie SERVERID insert indirect nocache
# Both servers active with equal weight
server app1 10.0.1.10:5000 check cookie app1 weight 100
server app2 10.0.1.11:5000 check cookie app2 weight 100
default-server inter 5s fall 3 rise 2
Session Management with Redis
For multi-server deployments, use Redis for centralized session storage:
# Install Redis
sudo apt install redis-server -y
# Configure Redis (/etc/redis/redis.conf)
bind 10.0.4.10
protected-mode no
maxmemory 256mb
maxmemory-policy allkeys-lru
Add to application .env:
REDIS_URL=redis://10.0.4.10:6379
SESSION_STORE=redis
Shared File Storage Requirements
Both application servers must mount the same shared directories:
/opt/minusnow/
├── data/ ← MUST BE SHARED (NFS/EFS)
├── audit-logs/ ← MUST BE SHARED
├── support-tickets/ ← MUST BE SHARED
├── artifacts/ ← MUST BE SHARED
└── uploads/ ← MUST BE SHARED
Rolling Deployment (Zero Downtime)
# Step 1: Remove app2 from load balancer (set weight to 0)
# Step 2: Deploy to app2
ssh app2 "cd /opt/minusnow && git pull && npm run build && sudo systemctl restart minusnow"
# Step 3: Verify app2 is healthy
curl -s http://10.0.1.11:5000/health
# Step 4: Add app2 back, remove app1
# Step 5: Deploy to app1
ssh app1 "cd /opt/minusnow && git pull && npm run build && sudo systemctl restart minusnow"
# Step 6: Add app1 back to load balancer
📊 Monitoring & Health Checks
Application Health Endpoints
| Endpoint | Description | Expected Response |
|---|---|---|
| GET /health | Basic health check | 200 OK |
| GET /api/telemetry/health | Detailed health status | JSON with component status |
| GET /api/telemetry/overview | System metrics | JSON with performance data |
Alerting Thresholds
| Metric | Warning | Critical |
|---|---|---|
| CPU Usage | > 70% | > 90% |
| Memory Usage | > 75% | > 90% |
| Disk Usage | > 70% | > 85% |
| Response Time | > 2s | > 5s |
| Error Rate | > 1% | > 5% |
| DB Replication Lag | > 1 min | > 5 min |
🔄 Backup & Disaster Recovery
Backup Strategy
| Data Type | Frequency | Retention | Method |
|---|---|---|---|
| Database | Every 6 hours | 30 days | pg_dump + WAL archiving |
| File Storage | Daily | 30 days | rsync / cloud snapshots |
| Configuration | On change | 90 days | Git / version control |
| Audit Logs | Weekly | 1 year | Archive to cold storage |
Database Backup Script
#!/bin/bash
# /opt/scripts/backup-database.sh
BACKUP_DIR="/backups/postgresql"
DATE=$(date +%Y%m%d_%H%M%S)
DB_NAME="minusnow_db"
DB_USER="minusnow"
DB_HOST="10.0.2.10"
# Create backup
PGPASSWORD="$DB_PASSWORD" pg_dump -h $DB_HOST -U $DB_USER -Fc $DB_NAME > $BACKUP_DIR/${DB_NAME}_${DATE}.dump
# Compress
gzip $BACKUP_DIR/${DB_NAME}_${DATE}.dump
# Remove backups older than 30 days
find $BACKUP_DIR -name "*.dump.gz" -mtime +30 -delete
# Copy to offsite storage
aws s3 cp $BACKUP_DIR/${DB_NAME}_${DATE}.dump.gz s3://your-backup-bucket/postgresql/
✅ Pre-Production Checklist
Infrastructure
- Load balancer configured and tested
- Primary and secondary app servers deployed
- Primary and replica database configured
- Shared storage mounted and accessible
- Network firewall rules in place
- SSL certificates installed and valid
Security
- All passwords meet complexity requirements
- SSH key-based authentication only
- Database access restricted to app servers
- WAF configured (if applicable)
- Security headers configured
- Rate limiting enabled
Operational
- Monitoring agents installed
- Alerting rules configured
- Backup jobs scheduled
- Disaster recovery plan documented
- Runbooks created for common issues
- On-call rotation established
Licensing
- All third-party licenses procured
- License keys documented and secured
- Renewal dates tracked
- Compliance requirements met
📞 Support
Need Help with Enterprise Deployment?
For additional assistance with enterprise deployment:
- Documentation: docs.minusnow.com
- Support Portal: support.minusnow.com
- Email: enterprise-support@minusnow.com