π Table of Contents
π Overview
This guide covers deploying MinusNow ITSM on major cloud platforms using Linux instances. Cloud deployment offers scalability, high availability, and managed services.
High Availability Architecture
ββββββββββββββββββββββββββββββββββββ
β DNS (Route 53 / β
β Cloud DNS / Azure DNS) β
ββββββββββββββββββββββββββββββββββββ
β
βΌ
ββββββββββββββββββββββββββββββββββββ
β Cloud Load Balancer (ALB / β
β Azure LB / Cloud LB) β
β SSL Termination β
ββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββΌββββββββββββββββ
βΌ βΌ βΌ
ββββββββββββββββββββββββββββββββββββββββββββββββ
β App VM 1 ββ App VM 2 ββ App VM 3 β
β (Node.js) ββ (Node.js) ββ (Node.js) β
ββββββββββββββββββββββββββββββββββββββββββββββββ
β β β
βββββββββββββββββΌββββββββββββββββ
βΌ
ββββββββββββββββββββββββββββββββββββ
β Managed Database (RDS / β
β Azure PostgreSQL / Cloud SQL)β
β Multi-AZ Replica β
ββββββββββββββββββββββββββββββββββββ
Cloud Provider Comparison
| Feature | AWS | Azure | GCP | DigitalOcean |
|---|---|---|---|---|
| Compute | EC2 | VM | Compute Engine | Droplets |
| Managed DB | RDS PostgreSQL | Azure Database | Cloud SQL | Managed DB |
| Load Balancer | ALB | Azure LB | Cloud LB | LB |
| Kubernetes | EKS | AKS | GKE | DOKS |
| Starting Cost | $20-50/mo | $20-50/mo | $20-50/mo | $15-40/mo |
π» Instance Requirements
Development/Small
| Instance | m6i.2xlarge / D8s_v5 / n2-standard-8 |
| vCPU | 8 |
| RAM | 32 GB |
| Storage | 500 GB NVMe SSD |
Production (Standard)
| Instance | m6i.4xlarge / D16s_v5 / n2-standard-16 |
| vCPU | 16 |
| RAM | 64 GB |
| Storage | 1 TB NVMe SSD |
Production (HA)
| Instance | m6i.8xlarge / D32s_v5 / n2-standard-32 |
| Instances | 3+ (Auto Scaling) |
| RAM | 128 GB per instance |
| Database | Managed PostgreSQL Multi-AZ (db.r6g.2xlarge+) |
AWS AWS Deployment
1Create VPC and Networking
# Create VPC
aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=minusnow-vpc}]'
# Create subnets
aws ec2 create-subnet --vpc-id vpc-xxx --cidr-block 10.0.1.0/24 --availability-zone us-east-1a
aws ec2 create-subnet --vpc-id vpc-xxx --cidr-block 10.0.2.0/24 --availability-zone us-east-1b
# Create Internet Gateway
aws ec2 create-internet-gateway --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=minusnow-igw}]'
aws ec2 attach-internet-gateway --internet-gateway-id igw-xxx --vpc-id vpc-xxx
2Create Security Groups
# Application Security Group
aws ec2 create-security-group --group-name minusnow-app-sg --description "MinusNow ITSM Application" --vpc-id vpc-xxx
# Allow SSH, HTTP, HTTPS
aws ec2 authorize-security-group-ingress --group-id sg-xxx --protocol tcp --port 22 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id sg-xxx --protocol tcp --port 80 --cidr 0.0.0.0/0
aws ec2 authorize-security-group-ingress --group-id sg-xxx --protocol tcp --port 443 --cidr 0.0.0.0/0
# Database Security Group - Allow PostgreSQL from app SG only
aws ec2 authorize-security-group-ingress --group-id sg-db-xxx --protocol tcp --port 5432 --source-group sg-app-xxx
3Create RDS PostgreSQL
# Create DB subnet group
aws rds create-db-subnet-group --db-subnet-group-name minusnow-db-subnet --db-subnet-group-description "MinusNow DB Subnets" --subnet-ids subnet-xxx subnet-yyy
# Create RDS instance
aws rds create-db-instance \
--db-instance-identifier minusnow-itsm-db \
--db-instance-class db.t3.medium \
--engine postgres \
--engine-version 15.4 \
--master-username minusnow \
--master-user-password YourSecurePassword123! \
--allocated-storage 100 \
--storage-type gp3 \
--vpc-security-group-ids sg-db-xxx \
--db-subnet-group-name minusnow-db-subnet \
--multi-az \
--backup-retention-period 7 \
--storage-encrypted \
--db-name minusnow_itsm
4Launch EC2 Instance
# Launch instance
aws ec2 run-instances \
--image-id ami-0c55b159cbfafe1f0 \
--instance-type t3.large \
--key-name minusnow-key \
--security-group-ids sg-app-xxx \
--subnet-id subnet-xxx \
--associate-public-ip-address \
--block-device-mappings '[{"DeviceName":"/dev/sda1","Ebs":{"VolumeSize":100,"VolumeType":"gp3","Encrypted":true}}]' \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=minusnow-itsm-app}]' \
--user-data file://user-data.sh
5Create Application Load Balancer
# Create ALB
aws elbv2 create-load-balancer --name minusnow-alb --subnets subnet-xxx subnet-yyy --security-groups sg-alb-xxx --scheme internet-facing --type application
# Create target group
aws elbv2 create-target-group --name minusnow-tg --protocol HTTP --port 5000 --vpc-id vpc-xxx --health-check-path /health --target-type instance
# Create HTTPS listener (requires ACM certificate)
aws elbv2 create-listener --load-balancer-arn arn:aws:elasticloadbalancing:xxx --protocol HTTPS --port 443 --certificates CertificateArn=arn:aws:acm:xxx --default-actions Type=forward,TargetGroupArn=arn:aws:elasticloadbalancing:xxx
Azure Azure Deployment
1Create Resource Group and VNet
# Login to Azure
az login
# Create resource group
az group create --name minusnow-rg --location eastus
# Create VNet
az network vnet create --resource-group minusnow-rg --name minusnow-vnet --address-prefix 10.0.0.0/16 --subnet-name app-subnet --subnet-prefix 10.0.1.0/24
2Create Azure Database for PostgreSQL
# Create PostgreSQL Flexible Server
az postgres flexible-server create \
--resource-group minusnow-rg \
--name minusnow-itsm-db \
--location eastus \
--admin-user minusnow \
--admin-password YourSecurePassword123! \
--sku-name Standard_B2s \
--tier Burstable \
--storage-size 128 \
--version 15 \
--high-availability ZoneRedundant \
--vnet minusnow-vnet \
--subnet db-subnet
# Create database
az postgres flexible-server db create --resource-group minusnow-rg --server-name minusnow-itsm-db --database-name minusnow_itsm
3Create Virtual Machine
# Create VM
az vm create \
--resource-group minusnow-rg \
--name minusnow-vm \
--image Ubuntu2204 \
--size Standard_B4ms \
--admin-username azureuser \
--generate-ssh-keys \
--vnet-name minusnow-vnet \
--subnet app-subnet \
--public-ip-address minusnow-pip \
--custom-data cloud-init.txt
4Create Application Gateway
# Create Application Gateway
az network application-gateway create \
--resource-group minusnow-rg \
--name minusnow-agw \
--location eastus \
--sku Standard_v2 \
--capacity 2 \
--vnet-name minusnow-vnet \
--subnet agw-subnet \
--public-ip-address minusnow-agw-pip \
--http-settings-port 5000 \
--http-settings-protocol Http \
--frontend-port 443 \
--servers 10.0.1.4
GCP Google Cloud Deployment
1Create VPC and Firewall
# Set project
gcloud config set project your-project-id
# Create VPC
gcloud compute networks create minusnow-vpc --subnet-mode=custom
# Create subnet
gcloud compute networks subnets create minusnow-subnet --network=minusnow-vpc --region=us-central1 --range=10.0.1.0/24
# Create firewall rules
gcloud compute firewall-rules create minusnow-allow-ssh --network=minusnow-vpc --allow=tcp:22 --source-ranges=0.0.0.0/0
gcloud compute firewall-rules create minusnow-allow-https --network=minusnow-vpc --allow=tcp:443 --source-ranges=0.0.0.0/0
2Create Cloud SQL PostgreSQL
# Create Cloud SQL instance
gcloud sql instances create minusnow-itsm-db \
--database-version=POSTGRES_15 \
--tier=db-custom-2-4096 \
--region=us-central1 \
--storage-type=SSD \
--storage-size=100GB \
--availability-type=REGIONAL
# Create database and user
gcloud sql databases create minusnow_itsm --instance=minusnow-itsm-db
gcloud sql users create minusnow --instance=minusnow-itsm-db --password=YourAppPassword123!
3Create Compute Engine Instance
# Create instance
gcloud compute instances create minusnow-vm \
--zone=us-central1-a \
--machine-type=e2-standard-2 \
--image-family=ubuntu-2204-lts \
--image-project=ubuntu-os-cloud \
--boot-disk-size=100GB \
--boot-disk-type=pd-ssd \
--network=minusnow-vpc \
--subnet=minusnow-subnet \
--metadata-from-file=startup-script=startup.sh
π³ Docker Deployment
Dockerfile
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine
WORKDIR /app
# Install production dependencies only
COPY package*.json ./
RUN npm ci --only=production
# Copy built files
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/drizzle ./drizzle
# Create non-root user
RUN addgroup -g 1001 -S minusnow && adduser -S minusnow -u 1001
USER minusnow
ENV NODE_ENV=production
ENV PORT=5000
EXPOSE 5000
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget --no-verbose --tries=1 --spider http://localhost:5000/health || exit 1
CMD ["node", "dist/index.js"]
Docker Compose
version: '3.8'
services:
app:
build: .
image: minusnow/itsm:latest
container_name: minusnow-itsm
restart: unless-stopped
ports:
- "5000:5000"
environment:
- NODE_ENV=production
- DATABASE_URL=postgresql://minusnow:password@db:5432/minusnow_itsm
- SESSION_SECRET=${SESSION_SECRET}
volumes:
- ./data:/app/data
- ./logs:/app/logs
depends_on:
db:
condition: service_healthy
networks:
- minusnow-network
db:
image: postgres:15-alpine
container_name: minusnow-db
restart: unless-stopped
environment:
- POSTGRES_USER=minusnow
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=minusnow_itsm
volumes:
- postgres_data:/var/lib/postgresql/data
networks:
- minusnow-network
healthcheck:
test: ["CMD-SHELL", "pg_isready -U minusnow -d minusnow_itsm"]
interval: 10s
timeout: 5s
retries: 5
nginx:
image: nginx:alpine
container_name: minusnow-nginx
restart: unless-stopped
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
depends_on:
- app
networks:
- minusnow-network
volumes:
postgres_data:
networks:
minusnow-network:
driver: bridge
Deploy with Docker Compose
# Build and start
docker-compose up -d --build
# View logs
docker-compose logs -f app
# Stop
docker-compose down
# Stop and remove volumes
docker-compose down -v
βΈοΈ Kubernetes Deployment
Deployment Manifest
apiVersion: apps/v1
kind: Deployment
metadata:
name: minusnow-itsm
namespace: minusnow-itsm
spec:
replicas: 3
selector:
matchLabels:
app: minusnow-itsm
template:
metadata:
labels:
app: minusnow-itsm
spec:
containers:
- name: minusnow-itsm
image: minusnow/itsm:latest
ports:
- containerPort: 5000
envFrom:
- configMapRef:
name: minusnow-config
- secretRef:
name: minusnow-secrets
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 5000
initialDelaySeconds: 5
periodSeconds: 5
Service and Ingress
---
apiVersion: v1
kind: Service
metadata:
name: minusnow-itsm-service
namespace: minusnow-itsm
spec:
selector:
app: minusnow-itsm
ports:
- port: 80
targetPort: 5000
type: ClusterIP
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minusnow-ingress
namespace: minusnow-itsm
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
tls:
- hosts:
- itsm.yourdomain.com
secretName: minusnow-tls
rules:
- host: itsm.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: minusnow-itsm-service
port:
number: 80
Horizontal Pod Autoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: minusnow-hpa
namespace: minusnow-itsm
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: minusnow-itsm
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
Deploy to Kubernetes
# Apply all manifests
kubectl apply -f namespace.yaml
kubectl apply -f configmap.yaml
kubectl apply -f secrets.yaml
kubectl apply -f deployment.yaml
kubectl apply -f hpa.yaml
# Check status
kubectl get all -n minusnow-itsm
# View logs
kubectl logs -f deployment/minusnow-itsm -n minusnow-itsm
# Scale manually
kubectl scale deployment minusnow-itsm --replicas=5 -n minusnow-itsm
ποΈ Managed Database Options
| Provider | Service | Features |
|---|---|---|
| AWS | RDS PostgreSQL | Multi-AZ, Auto backups, Read replicas |
| Azure | Azure Database for PostgreSQL | Flexible Server, HA, VNET integration |
| GCP | Cloud SQL | Regional HA, Auto maintenance |
| DigitalOcean | Managed Databases | Standby nodes, Auto failover |
Connection String Examples
# AWS RDS
DATABASE_URL=postgresql://minusnow:password@mydb.xxx.us-east-1.rds.amazonaws.com:5432/minusnow_itsm?sslmode=require
# Azure
DATABASE_URL=postgresql://minusnow:password@mydb.postgres.database.azure.com:5432/minusnow_itsm?sslmode=require
# GCP Cloud SQL (via Cloud SQL Proxy)
DATABASE_URL=postgresql://minusnow:password@localhost:5432/minusnow_itsm
# DigitalOcean
DATABASE_URL=postgresql://minusnow:password@mydb-do-user-xxx.db.ondigitalocean.com:25060/minusnow_itsm?sslmode=require
π Monitoring & Logging
AWS CloudWatch
# Install CloudWatch agent
wget https://s3.amazonaws.com/amazoncloudwatch-agent/ubuntu/amd64/latest/amazon-cloudwatch-agent.deb
sudo dpkg -i amazon-cloudwatch-agent.deb
# Configure
sudo /opt/aws/amazon-cloudwatch-agent/bin/amazon-cloudwatch-agent-config-wizard
Azure Monitor
# Install Log Analytics agent
wget https://raw.githubusercontent.com/Microsoft/OMS-Agent-for-Linux/master/installer/scripts/onboard_agent.sh
sh onboard_agent.sh -w <workspace-id> -s <workspace-key>
GCP Operations
# Install Ops Agent
curl -sSO https://dl.google.com/cloudagents/add-google-cloud-ops-agent-repo.sh
sudo bash add-google-cloud-ops-agent-repo.sh --also-install
π X-Forwarded Headers Configuration
When running behind cloud load balancers, configure X-Forwarded headers to pass original client information to your application.
Nginx Configuration (for Docker/VM)
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;
# X-Forwarded Headers
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 300s;
}
Cloud Load Balancer Headers
AWS ALB
ALB automatically adds:
- X-Forwarded-For
- X-Forwarded-Proto
- X-Forwarded-Port
Azure App Gateway
Configurable via:
az network application-gateway http-settings update \
--gateway-name minusnow-agw \
--name appGatewayBackendHttpSettings \
--resource-group minusnow-rg \
--host-name-from-backend-pool true
GCP HTTP(S) LB
Automatically adds:
- X-Forwarded-For
- X-Cloud-Trace-Context
Kubernetes Ingress Annotations
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minusnow-ingress
annotations:
nginx.ingress.kubernetes.io/use-forwarded-headers: "true"
nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "0.0.0.0/0"
nginx.ingress.kubernetes.io/configuration-snippet: |
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;
Why X-Forwarded Headers?
- X-Forwarded-For: Original client IP (for logging, rate limiting, geolocation)
- X-Forwarded-Proto: Original protocol (http/https) for secure redirects
- X-Forwarded-Host: Original Host header for virtual hosting
- X-Real-IP: Single client IP (simpler alternative to X-Forwarded-For)
π Security Best Practices
Network Security
- Use private subnets for databases
- Enable VPC Flow Logs
- Use NAT Gateway for outbound
- Implement least-privilege security groups
Secrets Management
# AWS Secrets Manager
aws secretsmanager create-secret \
--name minusnow/production/db-credentials \
--secret-string '{"username":"minusnow","password":"xxx"}'
# Azure Key Vault
az keyvault secret set --vault-name minusnow-vault --name db-password --value "xxx"
π Disaster Recovery
Backup Strategy:
- Database: Automated daily backups (managed service)
- Application: Store artifacts in S3/Blob/GCS
- Configuration: Version control (Git)
- Data: Regular exports to object storage
Multi-Region Setup (AWS Example)
# DNS failover with Route 53
aws route53 create-health-check \
--caller-reference $(date +%s) \
--health-check-config "Type=HTTPS,FullyQualifiedDomainName=itsm.yourdomain.com,Port=443,RequestInterval=30"
# Database read replica in secondary region
aws rds create-db-instance-read-replica \
--db-instance-identifier minusnow-itsm-replica \
--source-db-instance-identifier minusnow-itsm-db \
--availability-zone us-west-2a