📑 Table of Contents
- Prerequisites & GCP Account Setup
- Create a GCP Project & Enable APIs
- VPC Network & Firewall Rules
- Create Compute Engine VM
- Install Node.js & Dependencies
- Deploy MinusNow Application (Git Clone from GitHub)
- Database Setup (SQLite or Cloud SQL)
- Process Manager (PM2) & Auto-Start
- GitHub Actions CI/CD — Auto-Deploy on Push
- Nginx Reverse Proxy Setup
- Domain & Cloud DNS Configuration
- SSL/TLS Certificate (Let's Encrypt)
- Cloud Load Balancer (Optional — Production)
- Monitoring & Logging (Cloud Operations)
- Backup & Disaster Recovery
- Security Hardening
- Go-Live Checklist
- Cost Estimation
- Troubleshooting
1) Prerequisites & GCP Account Setup
What You Need
- A Google account (Gmail or Workspace)
- A GCP account with billing enabled — Free trial: $300 credit for 90 days
- A domain name (e.g.
minusnow.yourdomain.com) - Your MinusNow code pushed to a GitHub repository (private or public)
- Basic SSH / Linux terminal skills
Recommended VM Sizing
| Tier | Machine Type | RAM | Disk | Nodes |
|---|---|---|---|---|
| Dev/POC | e2-medium | 4 GB | 30 GB | ≤ 50 |
| Small Prod | e2-standard-2 | 8 GB | 60 GB | ≤ 200 |
| Medium | e2-standard-4 | 16 GB | 120 GB | ≤ 1,000 |
| Large | e2-standard-8 | 32 GB | 250 GB | ≤ 5,000 |
Install Google Cloud CLI (gcloud)
Install the gcloud CLI on your local machine to manage GCP resources from the terminal.
2) Create a GCP Project & Enable APIs
Create a New Project
Every resource in GCP lives inside a Project. Create one dedicated to MinusNow.
Create the project
Link a billing account
Enable required APIs
3) VPC Network & Firewall Rules
Create a Custom VPC
Use a custom VPC for better security and isolation.
Firewall Rules
Allow only the traffic MinusNow needs.
YOUR_IP/32 with your actual public IP for SSH. Never use 0.0.0.0/0 for SSH in production.
Firewall Rules Reference
| Rule Name | Direction | Ports | Source | Purpose |
|---|---|---|---|---|
| allow-ssh | Ingress | TCP 22 | Your IP only | SSH administration |
| allow-http-https | Ingress | TCP 80, 443 | 0.0.0.0/0 | Web traffic |
| allow-internal-app | Ingress | TCP 5000 | 10.10.0.0/24 (VPC) | App server (internal) |
| allow-health-check | Ingress | TCP 5000 | 130.211.0.0/22, 35.191.0.0/16 | GCP health checks (if using LB) |
4) Create Compute Engine VM
Launch the VM
Create the instance via gcloud CLI
Reserve a static external IP
SSH into the VM
5) Install Node.js & Dependencies
All commands below run inside the VM (after SSH).
Update system packages
Install Node.js 20.x LTS (via NodeSource)
Install essential build tools
Install PM2 (process manager)
6) Deploy MinusNow Application (Git Clone from GitHub)
git push to main automatically deploys to this VM.
Create a dedicated deploy user
Run as root or your admin user. A dedicated deploy user isolates the app from system-level access.
Generate SSH key for GitHub access (if private repo)
If your repo is private, you need a deploy key so the VM can pull code.
Add as Deploy Key on GitHub:
- Go to your GitHub repo → Settings → Deploy keys → Add deploy key
- Title:
GCP VM Deploy Key - Paste the public key
- Check "Allow write access" (needed for git pull by Actions)
- Click Add key
Configure SSH to use this key for GitHub:
Clone the repository
YOUR_USERNAME with your actual GitHub username or organization name.
Install dependencies & build
Configure environment variables
.env file is NOT in your git repo (it's in .gitignore). Generate a unique SESSION_SECRET for each environment. Never commit secrets to GitHub.
Create required data directories
Test the application
7) Database Setup
Option A — SQLite (Simple / Dev)
MinusNow uses SQLite by default with file-based JSON storage. This is the easiest option and works well for small-to-medium deployments.
- No separate database server needed
- Data stored in
/opt/minusnow/data/ - Suitable for up to ~500 monitored nodes
- Back up by copying the data directory
Option B — Cloud SQL PostgreSQL (Production)
For high availability and larger deployments, use a managed Cloud SQL PostgreSQL instance.
Update the .env file to use PostgreSQL:
8) Process Manager (PM2) & Auto-Start
PM2 keeps MinusNow running, auto-restarts on crash, and starts on boot.
Start the application with PM2
Enable startup on boot
Useful PM2 commands
8½) GitHub Actions CI/CD — Auto-Deploy on Push
.github/workflows/deploy.yml workflow. When you git push to main, GitHub Actions will: 1) Type-check & build in CI, 2) SSH into your GCP VM, 3) Pull latest code, install, build, and restart PM2 — fully automatic.
Step A — Create SSH Key for GitHub Actions
GitHub Actions needs SSH access to your VM. Create a dedicated key pair.
Generate the key pair on the VM
Step B — Configure GitHub Repository Secrets
Go to your GitHub repo → Settings → Secrets and variables → Actions → New repository secret:
| Secret Name | Value | Description |
|---|---|---|
SERVER_HOST | Your VM's static IP address | The external IP from Step 4 |
SSH_PRIVATE_KEY | Contents of github_actions_key (private key) | Full key including -----BEGIN and -----END lines |
Step by step with screenshots:
- Open github.com/YOUR_USERNAME/MinusNow
- Click Settings (gear icon at top)
- Left sidebar → Secrets and variables → Actions
- Click New repository secret
- Name:
SERVER_HOST→ Value: your VM IP (e.g.34.xxx.xxx.xxx) → Add secret - Click New repository secret again
- Name:
SSH_PRIVATE_KEY→ Paste the entire private key → Add secret
Step C — Create GitHub Environment
The deploy workflow uses a production environment for protection rules.
- Go to your repo → Settings → Environments
- Click New environment
- Name:
production→ Click Configure environment - (Optional) Add protection rules — require reviewers before deploying, or restrict to
mainbranch - Click Save protection rules
Step D — Your Existing Workflow (already in repo)
Your repo includes .github/workflows/deploy.yml which does the following:
🔨 Job 1: Build & Verify
- Checks out code
- Installs Node.js 20 + npm dependencies
- Runs TypeScript type-check (
npm run check) - Builds the application (
npm run build) - Verifies
dist/index.cjsanddist/public/exist
🚀 Job 2: Deploy to Production
- SSHs into your GCP VM as
deployuser - Runs
git pull origin main - Runs
npm install - Runs
npm run build - Runs
npm run db:push(schema migration) - Runs
pm2 restart minusnow
Step E — Test the Full Pipeline
Trigger a deploy from your local machine
Watch the deployment
Go to github.com/YOUR_USERNAME/MinusNow → Actions tab → click the running workflow to watch progress.
Or trigger manually from GitHub
Go to Actions → Deploy MinusNow → Run workflow → select main branch → Run workflow
Verify on the VM
git push to the main branch will automatically: type-check → build → deploy to your GCP VM → restart the app. Zero manual steps needed.
Manual Deploy (if needed)
If you ever need to deploy manually (or GitHub Actions is down):
9) Nginx Reverse Proxy Setup
Nginx sits in front of MinusNow, handles HTTPS termination, compression, and serves static files efficiently.
Install Nginx
Create Nginx configuration
Paste the following configuration:
Enable the site & test
ssl_certificate lines and the HTTPS server block. Use just the HTTP block first with proxy_pass instead of return 301. After setting up Let's Encrypt in the next step, re-enable the full config.
10) Domain & Cloud DNS Configuration
Create a Cloud DNS Zone (optional — if using GCP DNS)
Create DNS A records
If using an external DNS provider (GoDaddy, Cloudflare, Namecheap, etc.)
Simply add an A record pointing to your VM's static IP:
| Type | Name | Value | TTL |
|---|---|---|---|
| A | @ | YOUR_STATIC_IP | 300 |
| CNAME | www | your-domain.com | 300 |
Verify DNS propagation
11) SSL/TLS Certificate (Let's Encrypt)
Free, auto-renewing SSL certificates via Let's Encrypt and Certbot.
Install Certbot
Obtain the SSL certificate
Verify auto-renewal
Test your SSL
https://your-domain.com with a valid SSL certificate.
12) Cloud Load Balancer (Optional — Production)
For production deployments needing high availability, use a GCP HTTPS Load Balancer instead of direct SSL on Nginx. This provides DDoS protection, Google-managed SSL certificates, and CDN caching.
Create an instance group
Create health check
Create backend service + URL map + HTTPS proxy
Add firewall rule for GCP health checks
Update DNS to point to the load balancer IP
13) Monitoring & Logging (Cloud Operations)
Cloud Monitoring
GCP Cloud Monitoring (formerly Stackdriver) provides real-time VM and application metrics.
Set Up Uptime Checks
Go to Cloud Console → Monitoring → Uptime Checks → Create:
- Protocol: HTTPS
- Hostname: your-domain.com
- Path: /
- Check frequency: 1 minute
- Alert: Email + SMS on failure
Cloud Logging
Application logs are automatically collected by the Ops Agent. Configure PM2 to output structured logs:
Create Alert Policies
Go to Monitoring → Alerting → Create Policy:
- CPU utilization > 80% for 5 minutes
- Memory utilization > 85% for 5 minutes
- Disk utilization > 90%
- Uptime check failure (site down)
14) Backup & Disaster Recovery
VM Disk Snapshots (Automated)
Application Data Backup (to Cloud Storage)
Restore from Backup
15) Security Hardening
🔒 OS-Level Hardening
🛡️ Application-Level Security
- Run the app as a non-root user
- Set
NODE_ENV=production(disables debug output) - Use strong
SESSION_SECRET(256-bit random) - Keep Node.js and npm packages updated
- Restrict firewall to only required ports
- Enable HTTPS-only with HSTS header
- Use GCP Secret Manager for sensitive values
Store Secrets in Secret Manager
GCP IAM Best Practices
| Principle | Action |
|---|---|
| Least privilege | Use custom roles with only required permissions |
| Service accounts | Create a dedicated service account for the VM (minusnow-sa@project.iam) |
| No user keys | Use IAP for SSH instead of storing SSH keys on the VM |
| Audit logging | Enable Cloud Audit Logs for all admin activity |
| Two-factor auth | Enforce 2-step verification on all Google accounts |
16) Go-Live Checklist
| ✅ | Item | Details |
|---|---|---|
| ☐ | GCP project created | Billing linked, APIs enabled |
| ☐ | VPC + firewall configured | SSH restricted, 80/443 open, 5000 internal |
| ☐ | VM running | Static IP assigned, correct machine type |
| ☐ | Node.js installed | v20.x LTS with npm |
| ☐ | App deployed | Git cloned, built, .env configured, tested on port 5000 |
| ☐ | PM2 running | Auto-restart, startup on boot, pm2 save done |
| ☐ | GitHub Actions configured | Secrets set (SERVER_HOST, SSH_PRIVATE_KEY), production environment created |
| ☐ | CI/CD pipeline tested | git push to main triggers auto-deploy, workflow passes green |
| ☐ | Nginx configured | Reverse proxy, compression, security headers |
| ☐ | Domain pointing | A record → static IP, DNS propagated |
| ☐ | SSL active | Let's Encrypt or Google-managed, auto-renewal tested |
| ☐ | Monitoring | Ops Agent installed, uptime check, alert policies |
| ☐ | Backups | Daily snapshots, Cloud Storage backup script, tested restore |
| ☐ | Security | Root login disabled, fail2ban, UFW, secrets in Secret Manager |
| ☐ | Test everything | HTTPS works, app loads, login works, agents connect |
17) Cost Estimation
Monthly Cost Breakdown (asia-south1 — Mumbai)
| Resource | Specification | Est. Monthly Cost |
|---|---|---|
| Compute Engine VM | e2-standard-2 (2 vCPU, 8 GB) | ~$49/mo |
| Boot Disk (SSD) | 60 GB pd-ssd | ~$6/mo |
| Static IP | 1 external IP (while VM running) | $0 (free while attached) |
| Network Egress | ~50 GB/mo | ~$6/mo |
| Cloud DNS | 1 managed zone | $0.20/mo |
| Cloud Storage (backups) | ~10 GB | ~$0.20/mo |
| Snapshots | 7 daily snapshots | ~$1.50/mo |
| Cloud SQL (if used) | db-custom-2-8192 | ~$85/mo |
| Total (without Cloud SQL) | ~$63/mo | |
| Total (with Cloud SQL) | ~$148/mo | |
18) Troubleshooting
🔍 Common Issues
App won't start
Can't access from browser
🔧 Debug Commands
SSL certificate issues
DNS not resolving
VM performance issues
gcloud compute instances create minusnow-recovery --source-snapshot=SNAPSHOT_NAME --zone=asia-south1-a