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¶
Option A: Azure Virtual Machines (Recommended for v0.0.0)¶
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¶
- Start Smaller: Use Standard_D2s_v3 ($100/month) → upgrade as needed
- Reserved Instances: Save 30-40% with 1-year commitments (if plan extends)
- Scaling: Auto-shutdown non-prod resources during off-hours
- Storage Tiering: Use Cool tier for archived files (50% cheaper)
- 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
Related Documentation¶
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¶
- Prepare Infrastructure: Create resource group and VNet (15 min)
- Deploy Databases: PostgreSQL + Redis (20 min)
- Deploy VM: Configure networking and security (15 min)
- Deploy Application: Docker Compose on VM (10 min)
- Configure Monitoring: Azure Monitor + alerts (10 min)
- Test & Validate: Full integration test (20 min)
- 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 |