OmniRoute — Deployment Guide on VM with Cloudflare
Complete guide to install and configure OmniRoute on a VM (VPS) with domain managed via Cloudflare.
Prerequisites
Section titled “Prerequisites”| Item | Minimum | Recommended |
|---|---|---|
| CPU | 1 vCPU | 2 vCPU |
| RAM | 1 GB | 2 GB |
| Disk | 10 GB SSD | 25 GB SSD |
| OS | Ubuntu 22.04 LTS | Ubuntu 24.04 LTS |
| Domain | Registered on Cloudflare | — |
| Docker | Docker Engine 24+ | Docker 27+ |
Tested providers: Akamai (Linode), DigitalOcean, Vultr, Hetzner, AWS Lightsail.
1. Configure the VM
Section titled “1. Configure the VM”1.1 Create the instance
Section titled “1.1 Create the instance”On your preferred VPS provider:
- Choose Ubuntu 24.04 LTS
- Select the minimum plan (1 vCPU / 1 GB RAM)
- Set a strong root password or configure SSH key
- Note the public IP (e.g.,
203.0.113.10)
1.2 Connect via SSH
Section titled “1.2 Connect via SSH”ssh root@203.0.113.101.3 Update the system
Section titled “1.3 Update the system”apt update && apt upgrade -y1.4 Install Docker
Section titled “1.4 Install Docker”# Install dependenciesapt install -y ca-certificates curl gnupg
# Add official Docker repositoryinstall -m 0755 -d /etc/apt/keyringscurl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpgchmod a+r /etc/apt/keyrings/docker.gpgecho "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $ (. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/nullapt updateapt install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin1.5 Install nginx
Section titled “1.5 Install nginx”apt install -y nginx1.6 Configure Firewall (UFW)
Section titled “1.6 Configure Firewall (UFW)”ufw default deny incomingufw default allow outgoingufw allow 22/tcp # SSHufw allow 80/tcp # HTTP (redirect)ufw allow 443/tcp # HTTPSufw enableTip: For maximum security, restrict ports 80 and 443 to Cloudflare IPs only. See the Advanced Security section.
2. Install OmniRoute
Section titled “2. Install OmniRoute”2.1 Create configuration directory
Section titled “2.1 Create configuration directory”mkdir -p /opt/omniroute2.2 Create environment variables file
Section titled “2.2 Create environment variables file”cat > /opt/omniroute/.env << 'EOF'# === Security ===JWT_SECRET=CHANGE-TO-A-UNIQUE-64-CHAR-SECRET-KEYINITIAL_PASSWORD=YourSecurePassword123!API_KEY_SECRET=REPLACE-WITH-ANOTHER-SECRET-KEYSTORAGE_ENCRYPTION_KEY=REPLACE-WITH-THIRD-SECRET-KEYSTORAGE_ENCRYPTION_KEY_VERSION=v1MACHINE_ID_SALT=CHANGE-TO-A-UNIQUE-SALTOMNIROUTE_WS_BRIDGE_SECRET=REPLACE-WITH-WS-BRIDGE-SECRET # REQUIRED em produção: usado pelo Codex Responses WS bridge
# === App ===PORT=20128NODE_ENV=productionHOSTNAME=0.0.0.0DATA_DIR=/app/dataAPP_LOG_TO_FILE=trueAUTH_COOKIE_SECURE=trueREQUIRE_API_KEY=false
# === URLs (change to your domain) ===# Internal server-to-server base URL for scheduled jobs / self-fetches.BASE_URL=http://127.0.0.1:20128# Browser-facing URL used for OAuth callbacks, dashboard links, and generated public URLs.NEXT_PUBLIC_BASE_URL=https://llms.seudominio.com# Optional explicit public origin override for generated public asset URLs.# OMNIROUTE_PUBLIC_BASE_URL=https://llms.seudominio.com
# === Cloud Sync (optional) ===# CLOUD_URL=https://cloud.omniroute.online# NEXT_PUBLIC_CLOUD_URL=https://cloud.omniroute.onlineEOF⚠️ IMPORTANT: Generate unique secret keys! Use
openssl rand -hex 32for each key.
2.3 Start the container
Section titled “2.3 Start the container”docker pull diegosouzapw/omniroute:latest
docker run -d \ --name omniroute \ --restart unless-stopped \ --env-file /opt/omniroute/.env \ -p 20128:20128 \ -v omniroute-data:/app/data \ diegosouzapw/omniroute:latest2.4 Verify that it is running
Section titled “2.4 Verify that it is running”docker ps | grep omniroutedocker logs omniroute --tail 20It should display: [DB] SQLite database ready and listening on port 20128.
3. Configure nginx (Reverse Proxy)
Section titled “3. Configure nginx (Reverse Proxy)”3.1 Generate SSL certificate (Cloudflare Origin)
Section titled “3.1 Generate SSL certificate (Cloudflare Origin)”In the Cloudflare dashboard:
- Go to SSL/TLS → Origin Server
- Click Create Certificate
- Keep the defaults (15 years, *.yourdomain.com)
- Copy the Origin Certificate and the Private Key
mkdir -p /etc/nginx/ssl
# Paste the certificatenano /etc/nginx/ssl/origin.crt
# Paste the private keynano /etc/nginx/ssl/origin.key
chmod 600 /etc/nginx/ssl/origin.key3.2 Nginx Configuration
Section titled “3.2 Nginx Configuration”cat > /etc/nginx/sites-available/omniroute << 'NGINX'# Default server — blocks direct access via IPserver { listen 80 default_server; listen [::]:80 default_server; listen 443 ssl default_server; listen [::]:443 ssl default_server; ssl_certificate /etc/nginx/ssl/origin.crt; ssl_certificate_key /etc/nginx/ssl/origin.key; server_name _; return 444;}
# OmniRoute — HTTPSserver { listen 443 ssl; listen [::]:443 ssl; server_name llms.yourdomain.com; # Change to your domain
ssl_certificate /etc/nginx/ssl/origin.crt; ssl_certificate_key /etc/nginx/ssl/origin.key; ssl_protocols TLSv1.2 TLSv1.3;
client_max_body_size 100M;
location / { proxy_pass http://127.0.0.1:20128; proxy_set_header Host $host; proxy_set_header X-Forwarded-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;
# WebSocket support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade";
# SSE (Server-Sent Events) — streaming AI responses proxy_buffering off; proxy_cache off; proxy_read_timeout 600s; proxy_send_timeout 600s; }}
# HTTP → HTTPS redirectserver { listen 80; listen [::]:80; server_name llms.yourdomain.com; return 301 https://$server_name$request_uri;}NGINXKeep reverse-proxy stream timeouts aligned with your OmniRoute timeout env vars. If you raise
FETCH_TIMEOUT_MS / STREAM_IDLE_TIMEOUT_MS, raise proxy_read_timeout / proxy_send_timeout
above the same threshold.
OmniRoute uses NEXT_PUBLIC_BASE_URL as the canonical browser-facing origin for OAuth
callbacks and generated public links. Authenticated dashboard writes use same-origin requests
plus session-bound CSRF protection, so they do not require a static public base URL. The
X-Forwarded-* headers above are still useful routing metadata, but they are not a replacement
for setting the explicit public URL when OAuth or generated browser links need one. Only enable
OMNIROUTE_TRUST_PROXY if OmniRoute is not directly reachable by clients and your proxy
strips/rebuilds incoming forwarded headers.
3.3 Enable and Test
Section titled “3.3 Enable and Test”# Remove default configurationrm -f /etc/nginx/sites-enabled/default
# Enable OmniRouteln -sf /etc/nginx/sites-available/omniroute /etc/nginx/sites-enabled/omniroute
# Test and reloadnginx -t && systemctl reload nginx4. Configure Cloudflare DNS
Section titled “4. Configure Cloudflare DNS”4.1 Add DNS record
Section titled “4.1 Add DNS record”In the Cloudflare dashboard → DNS:
| Type | Name | Content | Proxy |
|---|---|---|---|
| A | llms |
203.0.113.10 (VM IP) |
✅ Proxied |
4.2 Configure SSL
Section titled “4.2 Configure SSL”Under SSL/TLS → Overview:
- Mode: Full (Strict)
Under SSL/TLS → Edge Certificates:
- Always Use HTTPS: ✅ On
- Minimum TLS Version: TLS 1.2
- Automatic HTTPS Rewrites: ✅ On
4.3 Testing
Section titled “4.3 Testing”curl -sI https://llms.seudominio.com/health# Should return HTTP/2 2005. Operations and Maintenance
Section titled “5. Operations and Maintenance”Upgrade to a new version
Section titled “Upgrade to a new version”docker pull diegosouzapw/omniroute:latestdocker stop omniroute && docker rm omniroutedocker run -d --name omniroute --restart unless-stopped \ --env-file /opt/omniroute/.env \ -p 20128:20128 \ -v omniroute-data:/app/data \ diegosouzapw/omniroute:latestView logs
Section titled “View logs”docker logs -f omniroute # Real-time streamdocker logs omniroute --tail 50 # Last 50 linesManual database backup
Section titled “Manual database backup”# Copy data from the volume to the hostdocker cp omniroute:/app/data ./backup-$(date +%F)
# Or compress the entire volumedocker run --rm -v omniroute-data:/data -v $(pwd):/backup \ alpine tar czf /backup/omniroute-data-$(date +%F).tar.gz /dataRestore from backup
Section titled “Restore from backup”docker stop omniroutedocker run --rm -v omniroute-data:/data -v $(pwd):/backup \ alpine sh -c "rm -rf /data/* && tar xzf /backup/omniroute-data-YYYY-MM-DD.tar.gz -C /"docker start omniroute6. Advanced Security
Section titled “6. Advanced Security”Restrict nginx to Cloudflare IPs
Section titled “Restrict nginx to Cloudflare IPs”cat > /etc/nginx/cloudflare-ips.conf << 'CF'# Cloudflare IPv4 ranges — update periodically# https://www.cloudflare.com/ips-v4/set_real_ip_from 173.245.48.0/20;set_real_ip_from 103.21.244.0/22;set_real_ip_from 103.22.200.0/22;set_real_ip_from 103.31.4.0/22;set_real_ip_from 141.101.64.0/18;set_real_ip_from 108.162.192.0/18;set_real_ip_from 190.93.240.0/20;set_real_ip_from 188.114.96.0/20;set_real_ip_from 197.234.240.0/22;set_real_ip_from 198.41.128.0/17;set_real_ip_from 162.158.0.0/15;set_real_ip_from 104.16.0.0/13;set_real_ip_from 104.24.0.0/14;set_real_ip_from 172.64.0.0/13;set_real_ip_from 131.0.72.0/22;real_ip_header CF-Connecting-IP;CFAdd the following to nginx.conf inside the http {} block:
include /etc/nginx/cloudflare-ips.conf;Install fail2ban
Section titled “Install fail2ban”apt install -y fail2bansystemctl enable fail2bansystemctl start fail2ban
# Check statusfail2ban-client status sshdBlock direct access to the Docker port
Section titled “Block direct access to the Docker port”# Prevent direct external access to port 20128iptables -I DOCKER-USER -p tcp --dport 20128 -j DROPiptables -I DOCKER-USER -i lo -p tcp --dport 20128 -j ACCEPT
# Persist the rulesapt install -y iptables-persistentnetfilter-persistent save7. Deploy to Cloudflare Workers (Optional)
Section titled “7. Deploy to Cloudflare Workers (Optional)”For remote access via Cloudflare Workers (without exposing the VM directly):
# In the local repositorycd omnirouteCloudnpm installnpx wrangler loginnpx wrangler deploySee also TUNNELS_GUIDE.md for the in-repo Cloudflare Tunnel walkthrough. The standalone omnirouteCloud/ worker lives in a separate companion repo.
Port Summary
Section titled “Port Summary”| Port | Service | Access |
|---|---|---|
| 22 | SSH | Public (with fail2ban) |
| 80 | nginx HTTP | Redirect → HTTPS |
| 443 | nginx HTTPS | Via Cloudflare Proxy |
| 20128 | OmniRoute | Localhost only (via nginx) |
Low-Memory / Small VPS Optimization
Section titled “Low-Memory / Small VPS Optimization”For deployments on small VPS instances (1 GB RAM or less):
- Disable background services — set
OMNIROUTE_DISABLE_BACKGROUND_SERVICES=1to skip scheduler, MCP server, and periodic maintenance tasks. Seedocs/reference/ENVIRONMENT.md. - Use SQLite WAL mode — enabled by default, reduces peak memory during concurrent reads.
- Cap the V8 heap — set
OMNIROUTE_MEMORY_MB(e.g.512) so the runtime does not calibrate a ceiling larger than the VM. Seedocs/reference/ENVIRONMENT.md. - Heavyweight admission auto-scales with the heap cap – once
OMNIROUTE_MEMORY_MBis set above, the ingest byte budget (OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES) derives itself from that same ceiling, so a memory-constrained VM already gets a smaller concurrent-request budget with no extra tuning; excess requests get a retryable503withRetry-Afterinstead of competing for memory. Set the legacyOMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHTrequest-count cap only if you need a hard ceiling on top of that. - Avoid
next buildon the VPS — build locally and deploy the standalone output (.next/standalone/). - Monitor with
top/free -m— OmniRoute typically uses 200-400 MB RSS at idle on a 1 GB VM.
HagiCode
HagiCode is an agentic coding workspace: structured workflows, multi-agent execution, and Hero Dungeon views turn ideas into shipped software.
Turn ideas into polished, usable software with a smarter, faster, and more enjoyable agentic coding workflow.

- SmartStructured workflows turn intent into an executable path from idea to shipped change.
- EfficientMulti-agent workflows keep research, implementation, and review moving in parallel.
- FunHero Dungeon interfaces make long coding sessions visual, collaborative, and rewarding.