Azure deployment — step by step
deploy/azure/main.bicep provisions stateless tier on Azure Container Apps plus Postgres Flexible Server + Log Analytics.
1. Prerequisites
- Azure CLI (
az logindone), subscription, permission to create resource groups. - Three images pushed to registry Azure can pull (e.g. GHCR public, or ACR).
2. Create a resource group
az group create -n cmind-rg -l westeurope
3. Deploy the Bicep
az deployment group create -g cmind-rg -f deploy/azure/main.bicep \
-p imageRegistry=ghcr.io/your-org/cmind imageTag=1.0.0 \
ownerEmail=you@example.com \
ownerPassword='Change-Me-Str0ng!' \
pgPassword="$(openssl rand -hex 16)" \
discoveryJoinToken="$(openssl rand -hex 24)"
Creates: Container Apps environment, Web (external ingress), MCP (external ingress), Postgres Flexible Server + appdb, Log Analytics, workspace-based Application Insights component. Discovery on for Web. Its connection string injected into Web + MCP as APPLICATIONINSIGHTS_CONNECTION_STRING, so traces + metrics export natively to App Insights while logs land in same Log Analytics workspace — no collector needed. Pass -p otlpEndpoint=... to also forward to OTLP collector.
4. Get the URLs
az deployment group show -g cmind-rg -n main --query properties.outputs
# webUrl, mcpUrl
Open webUrl, sign in with owner (forced password change on first login).
5. Add node agents (separate)
Container Apps can't run privileged/DinD, so run agents elsewhere, point at webUrl:
- AKS — deploy Helm chart (kubernetes.md) with
nodeAgent.privileged=true, scale Web/MCP to 0 if want only agent tier there. - VM / VMSS — run
cmind-node-agentimage--privilegedwithNodeAgent:MainUrl=<webUrl>,NodeAgent:AdvertiseUrl=<vm reachable url>,NodeAgent:JwtSecret=<discoveryJoinToken>.
Agents self-register within one heartbeat interval — see ../operations/node-discovery.md.
6. Verify
az containerapp logs show -g cmind-rg -n cmind-web --tail 50 # compact JSON logs
curl -s <webUrl>/version
Production notes
- Front Web with Azure Front Door / App Gateway for TLS + WAF.
- Store secrets in Key Vault; pass stable Data Protection cert (
App__DataProtectionCertBase64/...Password) so key ring survives replica restarts. - App Insights (traces+metrics) + Log Analytics (logs) wired automatically; correlate on
trace_id. See ../operations/logging.md. - Set
otlpEndpointparam (orOTEL_EXPORTER_OTLP_ENDPOINTon apps) to also forward to collector. - Container Apps
scalerules (min/max) wired in Bicep.
Copy-trading agent + Key Vault (S5)
deploy/azure/main.bicep also provisions copy-agent Container App hosting CopyEngineSupervisor (App:Copy:Enabled=true, App:Features:CopyTrading=true) with no ingress — worker holding long-lived cTrader sockets. Reads DB connection string from Azure Key Vault secret via user-assigned managed identity (Key Vault Secrets User role) rather than inline plaintext secret. Each replica's NodeName defaults to its container hostname (unique), so DB lease attributes running profiles per replica and two replicas never double-host one. Scale minReplicas/maxReplicas to add copy capacity; DataProtection key ring shared through Postgres, so any replica can decrypt stored Open API tokens. Outputs: copyAgentName, keyVaultName.