Azure Deployment Guide

Azure Deployment Guide for Huitzo v0.0.0

Deploy Huitzo on Microsoft Azure with production-ready infrastructure, monitoring, and disaster recovery. This guide covers the v0.0.0 release with self-hosted licensing.

Architecture Overview

┌────────────────────────────────────────────────────────────────┐
│                    Azure Subscription                          │
│                                                                │
│  ┌────────────────────────────────────────────────────────┐   │
│  │                 Resource Group                          │   │
│  │           (huitzo-prod-eastus-rg)                       │   │
│  │                                                         │   │
│  │  ┌──────────────────────────────────────────────────┐  │   │
│  │  │              Virtual Network                       │  │   │
│  │  │         (10.0.0.0/16, vnet-huitzo)               │  │   │
│  │  │                                                   │  │   │
│  │  │  ┌─────────────────────────────────────────────┐ │  │   │
│  │  │  │     App Subnet (10.0.1.0/24)               │ │  │   │
│  │  │  │                                             │ │  │   │
│  │  │  │  ┌──────────────────────────────────────┐ │ │  │   │
│  │  │  │  │  Azure Container Instances / VMs     │ │ │  │   │
│  │  │  │  │  (Backend + Worker + Dashboard)      │ │ │  │   │
│  │  │  │  │  Port: 8000 (Backend)                │ │ │  │   │
│  │  │  │  │  Port: 5173 (Dashboard)              │ │ │  │   │
│  │  │  │  └──────────────────────────────────────┘ │ │  │   │
│  │  │  │                                             │ │  │   │
│  │  │  └─────────────────────────────────────────────┘ │  │   │
│  │  │                                                   │  │   │
│  │  │  ┌──────────────────────────────────────────────┐ │  │   │
│  │  │  │    Data Subnet (10.0.2.0/24)               │ │  │   │
│  │  │  │  (PostgreSQL, Redis, File Storage)         │ │  │   │
│  │  │  │                                             │ │  │   │
│  │  │  │  • PostgreSQL 17 (Azure Database)          │ │  │   │
│  │  │  │  • Redis 8.4 (Azure Cache)                 │ │  │   │
│  │  │  │  • Azure Blob Storage (File Storage)       │ │  │   │
│  │  │  └──────────────────────────────────────────────┘ │  │   │
│  │  │                                                   │  │   │
│  │  └─────────────────────────────────────────────────────┘  │   │
│  │                                                         │   │
│  │  ┌──────────────────────────────────────────────────┐  │   │
│  │  │        Application Gateway / Load Balancer       │  │   │
│  │  │        (TLS termination, WAF, routing)          │  │   │
│  │  └──────────────────────────────────────────────────┘  │   │
│  │                                                         │   │
│  └─────────────────────────────────────────────────────────┘  │
│                                                                │
│  ┌────────────────────────────────────────────────────────┐   │
│  │         Monitoring & Observability                     │   │
│  │  • Azure Monitor (Metrics & Logs)                      │   │
│  │  • Application Insights                                │   │
│  │  • Log Analytics Workspace                             │   │
│  └────────────────────────────────────────────────────────┘   │
│                                                                │
│  ┌────────────────────────────────────────────────────────┐   │
│  │         Backup & Disaster Recovery                     │   │
│  │  • PostgreSQL Automated Backups (7-35 days)            │   │
│  │  • Azure Blob Storage Backup (Geo-redundant)           │   │
│  │  • Key Vault (Secrets, License keys)                   │   │
│  └────────────────────────────────────────────────────────┘   │
│                                                                │
└────────────────────────────────────────────────────────────────┘

Infrastructure Components

1. Compute: Application Hosting

Single Standard_D4s_v3 or larger: - SKU: Standard_D4s_v3 (4 vCPU, 16 GB RAM, Premium SSD) - OS: Ubuntu 24.04 LTS - Disk: 200 GB Premium SSD for OS + container data - Image: Azure marketplace Ubuntu LTS - Availability: Single VM initially (upgrade to scale set if needed)

Cost: ~$200-250/month

# Create VM
az vm create \
  --resource-group huitzo-prod-eastus-rg \
  --name huitzo-app-prod \
  --image UbuntuLTS \
  --size Standard_D4s_v3 \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-address-allocation static \
  --open-port 22 \
  --open-port 80 \
  --open-port 443 \
  --os-disk-size-gb 200 \
  --os-disk-name huitzo-app-osdisk

Option B: Azure Container Instances (Simpler, no VM management)

Deploy Docker Compose directly: - vCPU: 2-4 - Memory: 4-8 GB - Storage: 50 GB ephemeral (bind external Azure Blob)

Cost: ~$100-150/month (pay per second)

Option C: Azure App Service (Future - v0.1+)

Not recommended for v0.0.0 (requires separate container images).

Recommendation: Start with Option A (Single VM + Docker Compose). It's the most flexible and closest to your self-hosted model. Upgrade to scale sets (Option B) when first customer needs HA.

2. Database: PostgreSQL 17

Azure Database for PostgreSQL (Flexible Server)

Service: Azure Database for PostgreSQL
SKU: Standard_B4ms (4 vCore, 16 GB RAM)
Edition: Flexible Server
Version: 17 (latest)
Storage: 256 GB (Premium SSD, auto-scale up to 1 TB)
Backup: Automated (7-day retention, configurable to 35)
PITR: Yes (7 days by default)
Zone Redundancy: No (upgrade to Premium for HA)
HA: Not enabled for v0.0.0 (enable for multi-region)

Cost: ~$300-350/month

PostgreSQL Configuration

# Create PostgreSQL server
az postgres flexible-server create \
  --resource-group huitzo-prod-eastus-rg \
  --name huitzo-db-prod \
  --location eastus \
  --admin-user huitzo_admin \
  --admin-password <STRONG_PASSWORD> \
  --sku-name Standard_B4ms \
  --tier Burstable \
  --storage-size 256 \
  --backup-retention 7 \
  --geo-redundant-backup Disabled \
  --high-availability Disabled \
  --version 17

# Create database
az postgres flexible-server db create \
  --resource-group huitzo-prod-eastus-rg \
  --server-name huitzo-db-prod \
  --database-name huitzo

Firewall Rules:

# Allow app VM
az postgres flexible-server firewall-rule create \
  --name AllowAppVM \
  --resource-group huitzo-prod-eastus-rg \
  --server-name huitzo-db-prod \
  --start-ip-address 10.0.1.0 \
  --end-ip-address 10.0.1.255

# Connection string for application
postgresql://huitzo_admin:[email protected]:5432/huitzo

Scaling Path: - v0.0.0: Standard_B4ms (burstable) - v0.1 (if HA needed): Standard_D4s (general-purpose, zone-redundant) - v1.0 (enterprise): Standard_D8s or higher with read replicas

3. Cache: Redis 8.4

Azure Cache for Redis (Standard or Premium)

Service: Azure Cache for Redis
SKU: Standard or Premium
Tier: Standard (for v0.0.0)
Capacity: 2 GB (handles ~50K concurrent sessions)
Version: 7 (latest stable, close to 8.4)
Replication: No (upgrade to Premium for HA)
Persistence: Enabled (RDB, daily snapshots)
Zone Redundancy: No (Premium only)

Cost: ~$150-200/month (Standard, 2 GB)

# Create Redis cache
az redis create \
  --resource-group huitzo-prod-eastus-rg \
  --name huitzo-cache-prod \
  --location eastus \
  --sku Standard \
  --vm-size c2 \
  --enable-non-ssl-port false \
  --minimum-tls-version 1.2

# Connection string
redis-prod.redis.cache.windows.net:6379

Firewall & Virtual Network Integration:

# Add private endpoint (recommended for production)
az network private-endpoint create \
  --name huitzo-redis-pe \
  --resource-group huitzo-prod-eastus-rg \
  --vnet-name vnet-huitzo \
  --subnet data-subnet \
  --private-connection-resource-id /subscriptions/.../redisCache/... \
  --group-ids redisCache \
  --connection-name huitzo-redis-conn

4. File Storage: Azure Blob Storage

Replace local filesystem with Azure Blob for scalability.

Service: Azure Blob Storage
Tier: Hot (v0.0.0), Cool (v0.1+)
Account Kind: StorageV2
Replication: LRS (Local-redundant, $15-20/month)
Upgrade: GRS (Geo-redundant) for production backup
Redundancy: LRS for v0.0.0

Cost: ~$20-30/month (LRS, 100 GB usage)

# Create storage account
az storage account create \
  --name huitzostorageprod \
  --resource-group huitzo-prod-eastus-rg \
  --location eastus \
  --account-tier Standard \
  --account-replication-type LRS \
  --kind StorageV2 \
  --https-only true \
  --min-tls-version TLS1_2

# Create container
az storage container create \
  --account-name huitzostorageprod \
  --name uploads \
  --auth-mode login

# Connection string
DefaultEndpointsProtocol=https;AccountName=huitzostorageprod;...

Update Backend Configuration

# apps/backend/src/backend/config.py
class Settings:
    # File storage backend selection
    FILE_STORAGE_BACKEND: str = "azure"  # or "local", "s3"

    # Azure Blob Storage
    AZURE_STORAGE_ACCOUNT_NAME: str
    AZURE_STORAGE_ACCOUNT_KEY: str
    AZURE_STORAGE_CONTAINER_NAME: str = "uploads"

    # Alternative: Connection string
    AZURE_STORAGE_CONNECTION_STRING: str

5. Networking: Virtual Network & Load Balancing

Virtual Network

# Create VNet
az network vnet create \
  --name vnet-huitzo \
  --resource-group huitzo-prod-eastus-rg \
  --address-prefix 10.0.0.0/16 \
  --location eastus

# App subnet
az network vnet subnet create \
  --vnet-name vnet-huitzo \
  --name app-subnet \
  --resource-group huitzo-prod-eastus-rg \
  --address-prefixes 10.0.1.0/24

# Data subnet (for databases)
az network vnet subnet create \
  --vnet-name vnet-huitzo \
  --name data-subnet \
  --resource-group huitzo-prod-eastus-rg \
  --address-prefixes 10.0.2.0/24

Load Balancer / Application Gateway

Option A: Azure Application Gateway (Recommended)

  • TLS/SSL termination
  • WAF (Web Application Firewall)
  • Path-based routing (needed for dashboard + API)
  • Auto-scaling backend pools

Cost: ~$150/month + processing fees

# Create public IP
az network public-ip create \
  --name appgw-pip \
  --resource-group huitzo-prod-eastus-rg \
  --sku Standard \
  --allocation-method Static

# Create Application Gateway (simplified)
az network application-gateway create \
  --name huitzo-appgw \
  --resource-group huitzo-prod-eastus-rg \
  --location eastus \
  --vnet-name vnet-huitzo \
  --subnet appgw-subnet \
  --capacity 2 \
  --sku Standard_v2 \
  --http-settings-cookie-based-affinity Disabled \
  --frontend-port 443 \
  --http-settings-port 8000 \
  --cert-file <path-to-cert.pfx> \
  --cert-password <password>

Routing Rules: - /api/* → Backend (port 8000) - /terminal/* → Backend WebSocket (port 8000) - / → Dashboard (port 5173)

DNS & SSL Certificates

Option A: Azure DNS + Azure Key Vault

# Create DNS zone
az network dns zone create \
  --name huitzo.company.com \
  --resource-group huitzo-prod-eastus-rg

# Store SSL certificate in Key Vault
az keyvault create \
  --name huitzo-keyvault \
  --resource-group huitzo-prod-eastus-rg \
  --location eastus

# Upload certificate
az keyvault certificate import \
  --vault-name huitzo-keyvault \
  --name huitzo-prod-cert \
  --file <path-to-cert.pfx> \
  --password <password>

Option B: Let's Encrypt (Free)

Deploy certbot on VM and auto-renew with Azure Functions.

Deployment Strategy

Phase 1: Single-Instance (v0.0.0)

┌─────────────────────────┐
│   Azure VM (1x)         │
│  ┌───────────────────┐  │
│  │ Docker Compose    │  │
│  │ ├── Backend       │  │
│  │ ├── Worker        │  │
│  │ └── Dashboard     │  │
│  └───────────────────┘  │
└─────────────────────────┘
        ↓ (via network)
  ┌──────────┬──────────┐
  ↓          ↓          ↓
PostgreSQL Redis    Blob Storage
(Azure DB)(Azure)(Azure)

Deployment Script:

#!/bin/bash
# deploy-azure-v0.0.0.sh

set -e

# Variables
RESOURCE_GROUP="huitzo-prod-eastus-rg"
VM_NAME="huitzo-app-prod"
LICENSE_KEY="your-license-key"
DEPLOYMENT_MODE="self_hosted"

# SSH into VM and deploy
ssh azureuser@$(az vm show -d --resource-group $RESOURCE_GROUP \
  --name $VM_NAME --query publicIps -o tsv) << 'EOF'

  # Install Docker
  curl -fsSL https://get.docker.com -o get-docker.sh
  sudo sh get-docker.sh
  sudo usermod -aG docker $USER

  # Clone Huitzo
  git clone https://github.com/Huitzo-Inc/huitzo-inc.git
  cd huitzo-inc/deploy/compose

  # Configure .env
  cat > .env << ETX
HUITZO_DEPLOYMENT_MODE=$DEPLOYMENT_MODE
HUITZO_LICENSE_KEY=$LICENSE_KEY
DATABASE_URL="postgresql://..."
REDIS_URL="redis://..."
AZURE_STORAGE_CONNECTION_STRING="..."
DOMAIN="huitzo.company.com"
ETX

  # Start services
  docker compose -f docker-compose.yml \
    -f docker-compose.external-db.yml up -d

  # Verify health
  sleep 10
  curl -I http://localhost:8000/health

EOF

Phase 2: Multi-Instance with Load Balancer (v0.1+)

Add Application Gateway + VM Scale Set:

┌────────────────────────────────────────┐
│    Application Gateway (LB + WAF)      │
└────────────────────────────────────────┘
          ↓
┌────────────────────────────────────────┐
│       VM Scale Set (3x instances)      │
│  ┌──────────┐ ┌──────────┐ ┌───────┐ │
│  │ Backend  │ │ Backend  │ │Backe..│ │
│  │ + Worker │ │ + Worker │ │+Worker│ │
│  └──────────┘ └──────────┘ └───────┘ │
└────────────────────────────────────────┘
          ↓
     Shared DB / Cache / Storage

Network Security

Network Security Groups (NSGs)

# Create NSG for app subnet
az network nsg create \
  --name app-nsg \
  --resource-group huitzo-prod-eastus-rg

# Allow HTTPS inbound from internet
az network nsg rule create \
  --nsg-name app-nsg \
  --resource-group huitzo-prod-eastus-rg \
  --name AllowHTTPS \
  --priority 100 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --source-address-prefixes '*' \
  --source-port-ranges '*' \
  --destination-address-prefixes '10.0.1.0/24' \
  --destination-port-ranges 443

# Allow SSH (restrict to your IP)
az network nsg rule create \
  --nsg-name app-nsg \
  --resource-group huitzo-prod-eastus-rg \
  --name AllowSSH \
  --priority 101 \
  --direction Inbound \
  --access Allow \
  --protocol Tcp \
  --source-address-prefixes '<YOUR_IP>/32' \
  --destination-port-ranges 22

# Deny all other inbound
az network nsg rule create \
  --nsg-name app-nsg \
  --resource-group huitzo-prod-eastus-rg \
  --name DenyAllInbound \
  --priority 4090 \
  --direction Inbound \
  --access Deny \
  --protocol '*' \
  --source-address-prefixes '*'

Private Endpoints (Database & Cache)

Keep database and Redis on private network (no public IPs):

# PostgreSQL private endpoint
az network private-endpoint create \
  --resource-group huitzo-prod-eastus-rg \
  --name pg-private-endpoint \
  --vnet-name vnet-huitzo \
  --subnet data-subnet \
  --private-connection-resource-id $(az postgres flexible-server show \
    --resource-group $RESOURCE_GROUP \
    --name huitzo-db-prod --query id -o tsv) \
  --group-ids postgresqlServer \
  --connection-name pg-connection

Monitoring & Observability

Azure Monitor Integration

# apps/backend/src/backend/observability/azure_monitor.py
"""
Module: Azure Monitor Integration
Implements:
    - docs/guides/self-hosting/observability.md#azure-monitor
"""

from azure.monitor.opentelemetry import configure_azure_monitor

def setup_azure_monitor():
    configure_azure_monitor(
        connection_string=f"InstrumentationKey={INSTRUMENTATION_KEY}"
    )

    # Automatic collection:
    # - Request duration and error rates
    # - Dependency calls (DB, Redis, HTTP)
    # - Exceptions and traces
    # - Performance counters

Metrics to Monitor

Critical Metrics:
  Application:
    - Request latency (p50, p95, p99)
    - Error rate (% of failed requests)
    - Active users / sessions
    - Command execution success rate
    - Command queue depth
    - Worker CPU/Memory utilization

  Database:
    - Connection count
    - Query latency (p95)
    - Replication lag (if multi-region)
    - Disk usage
    - Connection pool utilization

  Cache:
    - Hit rate (should be >80%)
    - Eviction rate
    - Memory usage
    - Connection count

  File Storage:
    - Upload success rate
    - Average file size
    - Total storage used
    - Bandwidth usage

Alert Rules

# Example: High error rate
az monitor metrics alert create \
  --name huitzo-high-error-rate \
  --resource-group huitzo-prod-eastus-rg \
  --scopes /subscriptions/.../huitzo-app-prod \
  --condition "avg http_requests_failed > 50" \
  --window-size 5m \
  --evaluation-frequency 1m \
  --action /subscriptions/.../actionGroups/alerts

Backup & Disaster Recovery

PostgreSQL Backups

Automated Backups (Built-in): - Daily snapshots (7-day retention by default) - PITR enabled (7-day window) - Geo-backup available (on Premium tier)

# Configure backup retention
az postgres flexible-server parameter set \
  --resource-group huitzo-prod-eastus-rg \
  --server-name huitzo-db-prod \
  --name "backup_retention_days" \
  --value 35

Manual Backups:

# Export database
az postgres flexible-server export \
  --admin-user huitzo_admin \
  --admin-password <PASSWORD> \
  --database-name huitzo \
  --server-name huitzo-db-prod \
  --resource-group huitzo-prod-eastus-rg \
  --output-name huitzo-backup-$(date +%Y%m%d) \
  --storage-account-name huitzostorageprod \
  --storage-account-container-name backups

File Storage Backups

Geo-Redundant Storage (GRS):

# Upgrade to GRS (requires new account or storage sync)
az storage account create \
  --name huitzostorageprod-backup \
  --resource-group huitzo-prod-eastus-rg \
  --location eastus \
  --account-tier Standard \
  --account-replication-type GRS

Azure Backup:

# Set up container backup
az backup container register \
  --vault-name huitzo-backup-vault \
  --resource-group huitzo-prod-eastus-rg \
  --backup-management-type AzureStorage \
  --workload-type AzureFileShare

Redis Persistence

Azure Cache for Redis includes RDB (Snapshot) persistence:

# Enable persistence
az redis patch \
  --name huitzo-cache-prod \
  --resource-group huitzo-prod-eastus-rg \
  --rdb-storage-primary-connection-string \
    "DefaultEndpointsProtocol=https;..."

Disaster Recovery Plan

Component Backup Method RTO RPO
PostgreSQL Azure automated + geo-backup 30 min 5 min
Redis RDB snapshots to Blob Storage 15 min 1 hour
File Storage Geo-redundant storage (GRS) 1 hour 1 hour
Application Code Git repository 5 min continuous

Recovery Steps: 1. Restore PostgreSQL from backup (Azure Portal, 5 min) 2. Restore Redis data from Blob Storage snapshot (10 min) 3. Redeploy application container (5 min) 4. Verify health endpoints (2 min)

Cost Estimation (v0.0.0)

Monthly Infrastructure Costs

Component SKU Quantity Cost
Compute VM (D4s_v3) 1 $200
Database PostgreSQL (B4ms) 1 $300
Cache Redis (Standard, 2GB) 1 $150
Storage Blob Storage (100GB LRS) 1 $25
Networking Application Gateway 1 $150
Monitoring Azure Monitor + AppInsights 1 $50
Backup Automated (included) 1 $0
DNS Azure DNS 1 $0
TOTAL ~$875/month

Cost Optimization Tips

  1. Start Smaller: Use Standard_D2s_v3 ($100/month) → upgrade as needed
  2. Reserved Instances: Save 30-40% with 1-year commitments (if plan extends)
  3. Scaling: Auto-shutdown non-prod resources during off-hours
  4. Storage Tiering: Use Cool tier for archived files (50% cheaper)
  5. Monitor Costs: Use Azure Cost Management + Billing dashboard

Security Checklist

  • Network security groups restricting inbound to HTTPS only
  • Private endpoints for PostgreSQL and Redis
  • SSL/TLS certificates renewed automatically
  • SSH key-based authentication (no passwords)
  • License key stored in Azure Key Vault
  • Database credentials in Key Vault (not in .env)
  • Application Insights monitoring enabled
  • Log Analytics Workspace configured
  • Automated backups with retention policy
  • Firewall rules limiting database access to app VM
  • Storage account encryption at rest
  • Managed identity for VM → Key Vault access

Deployment Checklist

Pre-Deployment

  • Azure subscription active with sufficient quota
  • License key obtained ([email protected])
  • Domain purchased (e.g., huitzo.company.com)
  • SSL certificate (self-signed or Let's Encrypt)
  • Admin access to Azure Portal

During Deployment

  • Create resource group
  • Create virtual network and subnets
  • Create PostgreSQL database server
  • Create Redis cache
  • Create storage account
  • Create VM and configure networking
  • Deploy application via SSH + Docker Compose
  • Test all endpoints (/health, /api/v1/commands, WebSocket)
  • Configure monitoring and alerts
  • Set up backup policies

Post-Deployment

  • Verify application health checks passing
  • Test command execution with example pack
  • Confirm database backups running
  • Confirm monitoring dashboards showing data
  • Document access credentials in secure location
  • Set up on-call rotation for alerts
  • Schedule monthly cost review

Support & Troubleshooting

Common Issues

Database Connection Timeout - Verify NSG rules allowing app VM → PostgreSQL - Check Private Endpoint DNS resolution - Verify firewall rules in PostgreSQL console

Redis Connection Issues - Check Azure Cache firewall / NSG rules - Verify SSL/TLS setting matches application config - Test connection from VM: redis-cli -h <endpoint> ping

File Upload Failures - Verify storage account connection string - Check container permissions - Verify SDK file storage backend configured to "azure"

High Costs - Review Azure Cost Management dashboard - Check for unused resources (old backups, snapshots) - Consider reserved instances for long-term deployments

Next Steps

  1. Prepare Infrastructure: Create resource group and VNet (15 min)
  2. Deploy Databases: PostgreSQL + Redis (20 min)
  3. Deploy VM: Configure networking and security (15 min)
  4. Deploy Application: Docker Compose on VM (10 min)
  5. Configure Monitoring: Azure Monitor + alerts (10 min)
  6. Test & Validate: Full integration test (20 min)
  7. Onboard Customer: Provide access, credentials, documentation (30 min)

Total Time: ~2 hours for complete v0.0.0 Azure deployment

Version History

Date Version Changes
2026-02-11 1.0.0 Initial Azure deployment guide for v0.0.0