MinusNow Documentation

πŸ“‘ 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

FeatureAWSAzureGCPDigitalOcean
ComputeEC2VMCompute EngineDroplets
Managed DBRDS PostgreSQLAzure DatabaseCloud SQLManaged DB
Load BalancerALBAzure LBCloud LBLB
KubernetesEKSAKSGKEDOKS
Starting Cost$20-50/mo$20-50/mo$20-50/mo$15-40/mo

πŸ’» Instance Requirements

Development/Small

Instancem6i.2xlarge / D8s_v5 / n2-standard-8
vCPU8
RAM32 GB
Storage500 GB NVMe SSD

Production (Standard)

Instancem6i.4xlarge / D16s_v5 / n2-standard-16
vCPU16
RAM64 GB
Storage1 TB NVMe SSD

Production (HA)

Instancem6i.8xlarge / D32s_v5 / n2-standard-32
Instances3+ (Auto Scaling)
RAM128 GB per instance
DatabaseManaged 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

ProviderServiceFeatures
AWSRDS PostgreSQLMulti-AZ, Auto backups, Read replicas
AzureAzure Database for PostgreSQLFlexible Server, HA, VNET integration
GCPCloud SQLRegional HA, Auto maintenance
DigitalOceanManaged DatabasesStandby 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