Skip to content

Deploying to Azure

This guide covers deploying the DBN Analytics POC API to Microsoft Azure using three production-ready options:

Option Best For Complexity
Azure Container Apps Serverless auto-scaling, easiest setup ⭐ Low
Azure App Service (Web Apps) Traditional PaaS, familiar experience ⭐⭐ Medium
Azure Kubernetes Service (AKS) Enterprise-scale, full control ⭐⭐⭐ High

Prerequisites

Before you begin, ensure you have:

Install and verify the Azure CLI:

az --version
az login

Common Setup (All Options)

1. Set Environment Variables

Set these shell variables once. They will be referenced throughout all deployment options.

RESOURCE_GROUP="dbn-analytics-rg"
LOCATION="eastus"
ACR_NAME="dbnanalyticsacr"
IMAGE_NAME="dbn-analytics-poc"
IMAGE_TAG="latest"

2. Create a Resource Group

az group create \
  --name $RESOURCE_GROUP \
  --location $LOCATION

3. Create an Azure Container Registry (ACR)

ACR is used to store your Docker image privately in Azure.

az acr create \
  --resource-group $RESOURCE_GROUP \
  --name $ACR_NAME \
  --sku Basic \
  --admin-enabled true

4. Build and Push the Docker Image to ACR

# Log in to ACR
az acr login --name $ACR_NAME

# Build the image
docker build -t $ACR_NAME.azurecr.io/$IMAGE_NAME:$IMAGE_TAG .

# Push to ACR
docker push $ACR_NAME.azurecr.io/$IMAGE_NAME:$IMAGE_TAG

Alternatively, use ACR's built-in cloud build (no local Docker required):

az acr build \
  --registry $ACR_NAME \
  --image $IMAGE_NAME:$IMAGE_TAG \
  .

Option 1: Azure Container Apps

Recommended — serverless, scales to zero, built-in HTTPS and ingress.

Architecture

flowchart LR
    Internet(["🌐 Internet"]) -->|HTTPS| Ingress
    subgraph Azure Container Apps Environment
        Ingress["🔀 Managed Ingress\n(TLS terminated)"]
        App["📦 Container App\ndbn-analytics-poc"]
    end
    App --> ACR[("🐳 Azure Container\nRegistry (ACR)")]
    App --> KV["🔑 Azure Key Vault\n(Secrets)"]

Step 1: Create the Container Apps Environment

az containerapp env create \
  --name dbn-analytics-env \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION

Step 2: Deploy the Container App

ACR_PASSWORD=$(az acr credential show \
  --name $ACR_NAME \
  --query "passwords[0].value" \
  --output tsv)

az containerapp create \
  --name dbn-analytics-poc \
  --resource-group $RESOURCE_GROUP \
  --environment dbn-analytics-env \
  --image $ACR_NAME.azurecr.io/$IMAGE_NAME:$IMAGE_TAG \
  --registry-server $ACR_NAME.azurecr.io \
  --registry-username $ACR_NAME \
  --registry-password $ACR_PASSWORD \
  --target-port 8080 \
  --ingress external \
  --min-replicas 0 \
  --max-replicas 5 \
  --cpu 0.5 \
  --memory 1.0Gi \
  --secrets openai-api-key=<YOUR_OPENAI_API_KEY> \
  --env-vars OPENAI_API_KEY=secretref:openai-api-key

Scales to zero

--min-replicas 0 means the app costs nothing when idle and auto-starts on the first request.

Step 3: Get the Live URL

az containerapp show \
  --name dbn-analytics-poc \
  --resource-group $RESOURCE_GROUP \
  --query "properties.configuration.ingress.fqdn" \
  --output tsv

This returns a URL like dbn-analytics-poc.nicemeadow-abc12345.eastus.azurecontainerapps.io.

Updating a Deployment

After code changes, rebuild, push, and update in one command:

az acr build --registry $ACR_NAME --image $IMAGE_NAME:$IMAGE_TAG .

az containerapp update \
  --name dbn-analytics-poc \
  --resource-group $RESOURCE_GROUP \
  --image $ACR_NAME.azurecr.io/$IMAGE_NAME:$IMAGE_TAG

Option 2: Azure App Service (Web Apps)

Ideal for teams familiar with traditional Azure PaaS hosting. Includes built-in CI/CD integration with GitHub Actions.

Step 1: Create an App Service Plan

az appservice plan create \
  --name dbn-analytics-plan \
  --resource-group $RESOURCE_GROUP \
  --sku B2 \
  --is-linux
SKU vCPUs Memory Monthly Cost (approx.)
B1 1 1.75 GB ~$13 USD
B2 2 3.5 GB ~$26 USD
P1v3 2 8 GB ~$82 USD

Step 2: Create the Web App

ACR_PASSWORD=$(az acr credential show \
  --name $ACR_NAME \
  --query "passwords[0].value" \
  --output tsv)

az webapp create \
  --resource-group $RESOURCE_GROUP \
  --plan dbn-analytics-plan \
  --name dbn-analytics-poc \
  --deployment-container-image-name $ACR_NAME.azurecr.io/$IMAGE_NAME:$IMAGE_TAG \
  --docker-registry-server-url https://$ACR_NAME.azurecr.io \
  --docker-registry-server-user $ACR_NAME \
  --docker-registry-server-password $ACR_PASSWORD

Step 3: Set Application Settings (Environment Variables)

az webapp config appsettings set \
  --resource-group $RESOURCE_GROUP \
  --name dbn-analytics-poc \
  --settings \
    OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>" \
    VANNA_MODEL="gpt-4o" \
    WEBSITES_PORT=8080

WEBSITES_PORT

Azure App Service requires WEBSITES_PORT=8080 so the platform knows which port your container is listening on.

Step 4: Enable Continuous Deployment from ACR

az webapp deployment container config \
  --resource-group $RESOURCE_GROUP \
  --name dbn-analytics-poc \
  --enable-cd true

Step 5: Get the Live URL

az webapp show \
  --resource-group $RESOURCE_GROUP \
  --name dbn-analytics-poc \
  --query "defaultHostName" \
  --output tsv

The app will be available at https://dbn-analytics-poc.azurewebsites.net.


Option 3: Azure Kubernetes Service (AKS)

For enterprise deployments requiring custom scaling policies, network isolation, or multi-service orchestration.

Step 1: Create an AKS Cluster

az aks create \
  --resource-group $RESOURCE_GROUP \
  --name dbn-analytics-aks \
  --node-count 1 \
  --node-vm-size Standard_B2s \
  --enable-managed-identity \
  --attach-acr $ACR_NAME \
  --generate-ssh-keys

Step 2: Get Credentials

az aks get-credentials \
  --resource-group $RESOURCE_GROUP \
  --name dbn-analytics-aks

Verify connectivity:

kubectl get nodes

Step 3: Create Kubernetes Secret for OpenAI

kubectl create secret generic openai-secret \
  --from-literal=OPENAI_API_KEY="<YOUR_OPENAI_API_KEY>"

Step 4: Apply Kubernetes Manifests

Create the deployment and service configuration:

k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: dbn-analytics-poc
  labels:
    app: dbn-analytics-poc
spec:
  replicas: 2
  selector:
    matchLabels:
      app: dbn-analytics-poc
  template:
    metadata:
      labels:
        app: dbn-analytics-poc
    spec:
      containers:
        - name: api
          image: dbnanalyticsacr.azurecr.io/dbn-analytics-poc:latest
          ports:
            - containerPort: 8080
          env:
            - name: OPENAI_API_KEY
              valueFrom:
                secretKeyRef:
                  name: openai-secret
                  key: OPENAI_API_KEY
            - name: VANNA_MODEL
              value: "gpt-4o"
          resources:
            requests:
              cpu: "250m"
              memory: "512Mi"
            limits:
              cpu: "1000m"
              memory: "1Gi"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 15
            periodSeconds: 20
          readinessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 10
---
apiVersion: v1
kind: Service
metadata:
  name: dbn-analytics-poc-svc
spec:
  type: LoadBalancer
  selector:
    app: dbn-analytics-poc
  ports:
    - protocol: TCP
      port: 80
      targetPort: 8080
kubectl apply -f k8s/deployment.yaml

Step 5: Get the External IP

kubectl get service dbn-analytics-poc-svc --watch

Wait for the EXTERNAL-IP column to populate, then access: http://<EXTERNAL-IP>/docs.


Securing Secrets with Azure Key Vault

For all deployment options, it is best practice to store secrets in Azure Key Vault rather than passing them as plain environment variables.

Create a Key Vault and Store the Secret

az keyvault create \
  --name dbn-analytics-kv \
  --resource-group $RESOURCE_GROUP \
  --location $LOCATION

az keyvault secret set \
  --vault-name dbn-analytics-kv \
  --name "OPENAI-API-KEY" \
  --value "<YOUR_OPENAI_API_KEY>"

Reference Key Vault in Container Apps

# Grant Container App identity access to Key Vault
PRINCIPAL_ID=$(az containerapp show \
  --name dbn-analytics-poc \
  --resource-group $RESOURCE_GROUP \
  --query "identity.principalId" -o tsv)

az keyvault set-policy \
  --name dbn-analytics-kv \
  --object-id $PRINCIPAL_ID \
  --secret-permissions get list

CI/CD with GitHub Actions

Automate your deployment pipeline by creating .github/workflows/azure-deploy.yml:

.github/workflows/azure-deploy.yml
name: Build and Deploy to Azure Container Apps

on:
  push:
    branches: [ main ]

jobs:
  build-and-deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Log in to Azure
        uses: azure/login@v1
        with:
          creds: ${{ secrets.AZURE_CREDENTIALS }}

      - name: Build and push to ACR
        run: |
          az acr build \
            --registry dbnanalyticsacr \
            --image dbn-analytics-poc:${{ github.sha }} \
            .

      - name: Deploy to Azure Container Apps
        run: |
          az containerapp update \
            --name dbn-analytics-poc \
            --resource-group dbn-analytics-rg \
            --image dbnanalyticsacr.azurecr.io/dbn-analytics-poc:${{ github.sha }}

Set AZURE_CREDENTIALS in your GitHub repository secrets using:

az ad sp create-for-rbac \
  --name "dbn-analytics-github-actions" \
  --role contributor \
  --scopes /subscriptions/<SUB_ID>/resourceGroups/$RESOURCE_GROUP \
  --sdk-auth

Clean Up Resources

When done, remove all Azure resources to avoid charges:

az group delete --name $RESOURCE_GROUP --yes --no-wait

Comparison Summary

Feature Container Apps App Service AKS
Scale to zero ✅ (with KEDA)
Managed TLS ⚙️ Manual
Custom domains
Setup complexity Low Medium High
CI/CD integration
Best for Serverless APIs Traditional teams Enterprise scale