Skip to content

BYOC on Azure

This brief lists the Azure resources, foundation-model deployments, network, and identity prerequisites your IT and cloud platform team should arrange so a Nomain BYOC stamp can be deployed into your own Azure subscription. It is the conversation-starter when scoping a proof-of-concept or production rollout.

For the AWS equivalent, see BYOC on AWS. The deployment pipeline itself is covered in Azure CI/CD, and the platform-wide security model in Security.

What is Nomain BYOC?

Nomain is an AI-native platform for understanding and modernizing mainframe estates (COBOL, PL/I, JCL, DB2). The BYOC (Bring Your Own Cloud) model runs the entire application stack inside your own Azure subscription: no Nomain-operated data plane, and no customer code or AST leaves your perimeter.

There are four outbound calls to Nomain infrastructure, all against the Nomain Customers API and all authenticated with an M2M ClientId / Secret issued at customer onboarding:

#CallWhat it does
1License validationAt App Service startup and at a configurable refresh interval. Returns a signed JWT carrying the contract tier and expiry.
2HeartbeatThe webapp periodically posts a small liveness signal so Nomain can detect deployments that have stopped reporting and surface tenant state in the admin portal. It also carries an aggregate, content-free chat-health summary (counts, percentiles, and each tier's effective model/region; no chat content or PII).
3FeedbackWhen an end user rates an AI response (thumbs up or down, free-text comment) or reports an issue from the webapp, the rating plus the redacted prompt category is posted back so the product team can detect regressions and improve the models. Opt-out at the tenant level.
4TelemetryAggregated usage signals (job counts, query latencies, error-rate buckets by category) posted on a fixed cadence for product analytics and service-quality monitoring. Aggregates only: no per-user content, no source code, no AST.

Data boundary

All four calls carry license, tenant, or aggregated-metric metadata only. No customer code, AST, embeddings, or chat content ever crosses this boundary.

Architecture at a glance

  • One Azure subscription, one region (e.g. swedencentral, westeurope, northeurope).
  • A single resource group holds the entire stamp.
  • All resources land inside a dedicated VNet (default 10.10.0.0/16). No public PaaS endpoints: every PaaS service uses Private Endpoints routed through private DNS zones.
  • Your existing spoke VNet (DevOps subnet, user-access subnet) peers into the deployment VNet so internal users and Azure DevOps can reach the webapp and Key Vault privately.
  • Six application services run as containers on Premium V3 App Service Plans: webapp, graphs-api, chats-api, recipes-api, recipes-worker, and llm-proxy, the in-stamp relay that serves the VS Code coding agent, so workspace code reaches the model without leaving your subscription.
  • End-user authentication is delegated to your existing OIDC IdP via WorkOS AuthKit, or WorkOS Connect bridging Okta, Entra ID, Auth0, or Keycloak, with no app-level integration changes. See Single sign-on.

Architecture diagrams (solution, components, data flow, HA, network, integrations) are delivered alongside this brief as a separate diagram pack. Each diagram covers the same three-flavour shape (Azure-native, Azure Arc-enabled, vanilla on-prem K8s) so the comparison is direct.

Solution map

A logical view of the Azure resources Nomain deploys into your subscription and the outbound calls it makes. Every resource lives inside the customer-owned resource group and is reached over Private Endpoints; only the webapp is exposed externally, via the public ingress tier the customer chooses at deploy time.

The stamp groups into these logical tiers: compute (App Service), AI (Azure OpenAI), database (PostgreSQL), storage and registry, identity and Key Vault, messaging, and observability, plus the four outbound calls to Nomain.

Network topology

Every PaaS resource is fronted by a Private Endpoint, so no Nomain service has a public endpoint. The stamp VNet peers with your existing spoke VNet for internal user access and Azure DevOps deployment. The design combines VNet peering, App Service integration, private endpoints, private DNS zones, and controlled NAT-gateway egress. See AWS networking for the equivalent shape on AWS.

Azure resources provisioned

The Bicep stamp creates the following resources inside your subscription. Sizes below are a typical production starting point; smaller PoC sizings are available.

CategoryResourceTypical SKU / size
ComputeApp Service Plan, graphs-apiP2V3 PremiumV3, capacity 1
ComputeApp Service Plan, chats-apiP2V3 PremiumV3, capacity 1
ComputeApp Service Plan, recipes (api + worker)P3V3 PremiumV3, capacity 1
ComputeApp Service Plan, webappP2V3 PremiumV3, capacity 1
ComputeApp Service Plan, llm-proxyP2V3 PremiumV3, capacity 1
ComputeApp Service Plan, generic (status, ancillary)S1 Standard, capacity 1
ComputeApp Services × 6 (Linux containers)publicNetworkAccess=Disabled
RegistryAzure Container Registry (dedicated to the stamp)Standard SKU, private endpoint
DatabaseAzure Database for PostgreSQL Flexible ServerStandard_D2ds_v4 GP, 128 GB, PG 16
AIAzure OpenAI account + AI Hub + AI ProjectEntraId auth from each App Service
StorageStorage Account (Blob + Queue)Standard_GRS geo-redundant
MessagingEvent Grid TopicPrivate endpoint
SecretsKey Vault (RBAC mode)Private endpoint
IdentityUser-assigned managed identities (6, one per App Service)Workload Identity scopes
NetworkVirtual Network 10.10.0.0/16 + NAT GatewaySubnets per tier
NetworkPrivate DNS zones (8), privatelink.*Auto-linked to VNet
NetworkPrivate Endpoints (≈ 10), one per PaaS resourceOne per PaaS resource
ObservabilityLog Analytics Workspace + diagnostic settingsAudit-table retention

Backups: 14-day point-in-time PG, geo-redundant backups enabled. High availability (zone-redundant) is an opt-in switch with a per-environment cost delta.

Azure OpenAI models and quota

Nomain needs access to a small set of Azure OpenAI models. The exact deployment names in your Azure OpenAI resource (and the precise wiring of which deployment serves which application code path) are configured per environment at deploy time and ship with the operational runbook: you do not need to pre-create deployments yourself. What you do need to arrange with Microsoft is model approval and TPM quota in your target region.

Required model families

Capacities below are in thousand-tokens-per-minute (TPM) units as Azure surfaces them in the quota console. Each row shows a recommended range: pick a point in the range based on your expected estate size, except the coding-agent row, which is driven by how many developers use it at once rather than by the size of the estate.

ModelRole in the platformRecommended capacity
gpt-5.6-lunaKnowledge-graph code explanation, the single highest-volume analysis path1,000 - 4,000
gpt-5-miniBulk analysis paths: code parsing, unused-code-detection inspector, chat context compaction1,000 - 4,000
gpt-5.4-miniMid-tier analysis and chat sub-agent search (where heavier reasoning than gpt-5-mini is required)1,000 - 4,000
gpt-5.4Chat answering (QuickSearch and DeepSearch), query preprocessing, process extraction, fallback for analysis paths1,000 - 4,000
gpt-5.6-solVS Code coding agent, served by the in-stamp LLM proxy, the one bursting interactive path (see the sizing note below)1,000 - 8,000
gpt-5-nanoLightweight utility: chat-title generation150 - 500
text-embedding-3-largePrimary embeddings for the knowledge graph (3,072 dimensions)500 - 4,000
text-embedding-3-smallClustering embeddings (lower-dim, lower-cost)250 - 500
Total TPM quota required~ 5,900 - 29,000

Size the explanation model first. Code explanation dominates analysis token spend, so gpt-5.6-luna is the row where an under-estimate is felt first: it throttles the initial analysis of an estate, which is the longest-running job a new deployment performs. The other analysis rows are comparatively forgiving.

Choosing a point in the range. The lower bound is enough for evaluation, PoCs, and small estates. The upper bound is what a production deployment serving an estate in a risk-sensitive environment typically needs. Customers running smaller workloads can also collapse gpt-5-mini and gpt-5.4-mini into a single model (the platform supports re-mapping the affected code paths to either one via configuration), which drops one model approval and a slice of TPM quota. gpt-5.6-luna is not a candidate for that collapse; the explanation path is deliberately pinned to it.

Sizing the coding agent

The coding agent is the only path on this page that bursts. An agent turn is a loop of model calls, each carrying the whole conversation so far, several per minute while a developer works. Everything else is either a batch job (paced by the app-side ceilings below) or a single chat answer.

Size it from the peak, not the average:

TPM ≥ users who may be mid-turn in the same minute × what one bursting user costs in their busiest minute

Measured on Nomain's own development stamp over 30 days of real use: a call carries a median of 33,000 tokens and a 95th-percentile of 108,000 after the client's compaction, and a developer at work makes 5 - 7 calls a minute. A single bursting user cost 62,000 TPM in a median busy minute, 280,000 at the 95th percentile and 366,000 at peak. Plan on roughly 300,000 TPM per concurrently bursting user.

Do not reach that second factor by multiplying the per-call size by the call rate. The two are anti-correlated (a call carrying 108,000 tokens takes longer to serve than one carrying 5,000, so the largest contexts and the fastest rates never coincide), and the multiplication overshoots badly: 108,000 × 7 suggests 756,000 against a measured peak of 366,000. Use a measured per-minute figure, ideally re-measured on your own stamp.

An average would mislead badly here: of those turns, 44 cost under 5,000 tokens (a fresh session asking a couple of questions) while the largest cost 140,000. The same developer is 30× cheaper in their first minute than in their thirtieth.

Seats using the coding agentAssumed bursting at onceRecommended capacity
up to 521,000
1031,000
2572,000 - 2,500
50134,000
100258,000

The middle column assumes a third of seats bursting together up to 25 seats and a quarter above that. Replace it with your own figure after a week of real use: every agent turn records its token consumption and the quota the deployment reported it had left, in the stamp's own Log Analytics workspace. Where the stamp is configured with alert recipients, sustained throttling also raises a "coding agent throttled" alert.

Three things that surprise people sizing this:

  • Rate limiting admits on the estimated full prompt, not on what you are billed for. Standard and GlobalStandard deployments evaluate a request against the whole prompt they estimate it will send. Prompt caching cuts the bill substantially (in the same 30-day measurement, a typical agent call re-read ~109,000 cached tokens alongside ~14,000 new ones) but it does not reduce what the rate limiter counted. Size on the full context, and never read a low billed-token figure as spare headroom.
  • Give it its own deployment where you can. If the coding agent shares the chat deployment (gpt-5.4), its bursts land on that row and compete with chat answers, which a user is sitting and waiting for. Where gpt-5.6-sol is available in the region, a dedicated deployment keeps the two apart.
  • Sustained load can leave Standard altogether. gpt-5.6-sol ships as GlobalProvisionedManaged, so a stamp with steady coding-agent use can buy guaranteed throughput instead of repeatedly raising a per-minute ceiling.

Per-service rate ceilings (app-side throttles)

Separate from the Azure quota above, the application enforces its own rate-limiter ceilings per service path so a runaway job cannot burst all token budget at once. The numbers below are an illustrative production-grade configuration; your deployment tunes them per environment.

Service pathMax concurrentRequests / minTokens / min
graphs-api LlmService3903,9003,900,000
graphs-api AiParser163,9003,900,000
graphs-api UnusedCodeDetection143,9004,000,000
recipes-worker LlmService3903,9003,900,000
recipes-worker AiParser803,9003,900,000
recipes-worker UnusedCodeDetection143,9004,000,000

Quota request to file with Microsoft

Request an Azure OpenAI resource in your target region with TPM quota in the 5,900 - 29,000 range (sum across the model families above), sized to your expected estate. DataZoneStandard SKU is recommended when available; Standard SKU also works if the region has constraints. Verify availability of each model in the region before committing.

Prerequisites you must provide

#ItemDetail
1Azure subscription with Owner (or Contributor + User Access Administrator) on the target RGNeeded for the initial Bicep deploy of role assignments and private DNS zones. After bootstrap a scoped service principal is sufficient.
2Target regionMust support Premium V3 App Service Plans, PostgreSQL Flexible Server, and every Azure OpenAI model in the models section. Common picks: swedencentral, westeurope, northeurope.
3Spoke VNet with user-access and deployment subnetsPeered to the stamp VNet 10.10.0.0/16. userAccessSubnet reaches the webapp + Key Vault; deploymentSubnet reaches App Service SCM for Azure DevOps.
4DNS zone control over the environment domainCustomer creates A / CNAME records to the App Service custom-domain endpoint at deploy time.
5Azure OpenAI quota (5,900 - 29,000 TPM range)Request approval for the model families listed in the models section and TPM quota sized to your expected estate. Submit via Azure Portal, Quotas, Cognitive Services. Allow time for review on first-time requests in the region.
6Entra ID app registrationsWorkOS AuthKit client + Nomain-issued M2M client credentials.
7Customer IdP (optional but typical)Okta / Entra ID / Auth0 / Keycloak wired through WorkOS Connect.
8Subscription policy reviewConfirm no Microsoft.Web/sites deny policies on the target RG that would conflict with the BYOC posture (publicNetworkAccess=Disabled is required and customer MG policies should permit it).
9Cost-management budgetSee the cost line items below.

Identity and access

Inside the stamp

  • No static credentials. Each App Service runs under a user-assigned managed identity bound to Azure OpenAI, Storage Blob, Key Vault Secrets User, and PostgreSQL (Entra-AD authentication mode).
  • App-to-app authentication uses Workload Identity tokens: short-lived, automatically rotated.
  • PostgreSQL admin password exists only so the first-deploy schema migration can create per-service Postgres roles; application services authenticate with Entra-AD afterwards.

End-user authentication

  • WorkOS AuthKit for end-user OIDC sign-in.
  • WorkOS Connect bridges your existing IdP (Okta, Entra ID, Auth0, Keycloak) without app-level changes: your users keep their normal SSO experience. See Single sign-on.
  • JWTs are validated locally inside the webapp BFF and each API; no per-request callback to WorkOS.
  • Outbound M2M credentials issued by Nomain at onboarding cover all four calls: license validation, webapp heartbeat, end-user feedback, and aggregated telemetry.

Security and compliance

The BYOC posture is built around three properties: your data never leaves your Azure subscription, no Nomain operator has runtime access to the stamp, and every cross-service call uses short-lived identity tokens rather than static credentials.

Defense-in-depth controls

LayerWhat's in place
Network isolationAll PaaS resources reachable only over Private Endpoints: no public endpoints. App Services run with publicNetworkAccess=Disabled. VNet integration on the Linux App Service Plan keeps East-West service-to-service traffic inside the customer's VNet. Outbound egress through a single NAT Gateway with a predictable static public IP for upstream firewall allow-lists.
Encryption in transitTLS 1.2+ enforced on every ingress and outbound call. PostgreSQL connection string uses sslmode=require. Workload Identity / Entra-AD tokens validated on every cross-service call.
Encryption at restAll Azure-managed services encrypt at rest by default with Microsoft-managed keys. Geo-redundant backups (Postgres + Storage GRS) ride the same encryption boundary.
IdentitySix user-assigned managed identities, one per App Service, bound at deploy time to the resources each service is allowed to touch (AOAI, Storage Blob, Key Vault Secrets User, PostgreSQL via Entra-AD). Zero static credentials live inside container images or app configuration. Workload Identity tokens are short-lived and rotated by the platform.
Secret managementKey Vault is the single source of truth for runtime secrets (Nomain-issued M2M ClientId/Secret, TLS certificate, etc.). App Services read secrets through Key Vault references: no secret value ever lands in App Service configuration logs.
End-user authenticationWorkOS AuthKit for OIDC, with WorkOS Connect bridging your existing IdP (Okta / Entra ID / Auth0 / Keycloak) without app-level changes. JWTs validated locally inside the webapp BFF and each API.
Tenant data isolationWithin a single BYOC stamp, tenant data is scoped through per-service PostgreSQL roles (nomain_chats, nomain_graphs) and row-level filters on tenant ID. Cross-tenant queries are forbidden at the data layer. For full physical isolation, deploy one stamp per tenant.
Audit loggingTenant lifecycle events, admin actions, authentication events, and data-deletion events emit through a dedicated IAuditLogger path that lands in audit-category Log Analytics tables retained for 12 months (separate from the 90-day default retention for operational logs).
Diagnostic + activity logsEvery Azure resource emits diagnostic logs into the stamp's Log Analytics workspace. Subscription Activity Log is captured. Alerts route to a customer-specified email recipient list (your Ops alongside Nomain Ops if you want the same view).
Vulnerability managementContainer images are scanned for CVEs at build time (Trivy). Microsoft Defender for Cloud Standard tier is supported on the App Service Plans, Key Vault, and Storage Account: recommended but not auto-enabled (your subscription policy decides).
Outbound dataflowThe only outbound calls to Nomain infrastructure are license validation, the webapp heartbeat, end-user feedback, and aggregated telemetry, all carrying license, tenant, or aggregated-metric metadata only, and no customer code, AST, embeddings, or chat content.

What Nomain does not have access to

Customer-owned perimeter

  • No standing operator credentials into your subscription. Initial deployment uses a service principal owned by your tenant.
  • No runtime read or write access to your data plane (PostgreSQL, Storage, Key Vault). Nomain support engineers receive logs and traces only on customer request, through a controlled diagnostics export.
  • No telemetry of your source code, AST, embeddings, or chat content back to Nomain.

Compliance alignment

AreaPropertyHow it holds
Data residencySingle regionEvery byte (PostgreSQL, Storage, AOAI inference) stays in the Azure region you pick at deploy time. EU customers commonly choose swedencentral or westeurope.
DORA / ICT third-party riskCustomer-controlled data planeNomain is not a critical ICT service provider for the data plane: it lives in your subscription. The four Customers API calls (license, heartbeat, feedback, telemetry) are the only Nomain runtime dependency to model.
GDPR / privacyNo PII processed by NomainCustomer authentication is delegated to your IdP. Nomain stores no end-user PII on its side; tenant-level metadata only flows back via the M2M channel.
ISO 27001 / SOC 2Audit trail in your tenant12-month retention on audit-category Log Analytics tables. Customer SIEM ingestion supported via diagnostic-settings export.

Cost line items

These are the variable-cost categories you should ask your Azure account team to model. Exact numbers depend on your EA / CSP pricing and your estate size.

LineDriver
App Service Plans (P2V3 × 4, P3V3 × 1, S1 × 1)Compute fixed cost: biggest non-AI line
PostgreSQL Flexible Server (Standard_D2ds_v4 + 128 GB + geo-redundant backup)Single-AZ by default; HA option roughly doubles compute
Azure OpenAI tokensDominant variable cost, driven by ingestion + chat volume
Azure Container Registry (Standard SKU)Negligible; small private registry
Storage Account (GRS)GB-month + transactions
Private Endpoints + NAT Gateway + private DNS zonesSmall fixed networking cost
Key Vault + secret operationsNegligible
Log Analytics ingestion + retentionModest; tunable via retention parameters

PoC kick-off checklist

When green-lighting a Nomain proof-of-concept on Azure, confirm the following are in place before deployment day:

  1. Subscription and region. Subscription with admin access; target region selected and reviewed against model availability.
  2. Azure OpenAI quota. Model approval + TPM quota in your target region, sized in the 5,900 - 29,000 range. Submit the quota request early.
  3. Network. Spoke VNet with non-overlapping CIDR; user-access and deployment subnets ready for peering.
  4. DNS and TLS. Zone control over the environment domain; certificate strategy agreed (Key Vault integration with managed certificate or customer-provided).
  5. Identity provider. WorkOS workspace provisioned (Nomain side); WorkOS Connect to your IdP scoped if you want SSO from day one.
  6. Secrets and credentials. Key Vault ready to receive the Nomain-issued M2M credentials (used for license, heartbeat, feedback, telemetry) at first deploy.
  7. Cost budget. Budget alerts set against the subscription; AOAI token spend modelled separately.

For questions, contact your Nomain customer success representative.