Skip to main content
This guide explains how to run sovara-server on Azure, protect it with Microsoft Entra, connect the Remote desktop app, and connect project agents. The default setup uses Azure CLI and deploys the published Sovara Server container image to Azure Container Apps. Azure Container Apps is the recommended starting point for small teams because it gives you managed HTTPS ingress, container hosting, logs, and environment configuration without operating a VM or Kubernetes cluster. The same Entra and server configuration applies if you host Sovara on an Azure VM, Azure App Service for Containers, AKS, or another reachable Azure container platform.

Architecture Requirements

Any Azure hosting option must provide:
  • HTTPS endpoint reachable from user desktops.
  • WebSocket support from the desktop app to the server.
  • Reachability from agent machines that will report runs.
  • Environment variable or secret configuration for sovara-server.
  • Reachable PostgreSQL or MongoDB database for Sovara state.
  • Outbound HTTPS from the server to Microsoft identity endpoints for OIDC/JWKS.

Choose an Azure Hosting Model

Hosting modelUse whenNotes
Azure Container AppsYou want the simplest managed serverless container deployment.Recommended default for initial enterprise setup.
Azure App Service for ContainersThe organization standardizes on App Service.Configure the same image, env vars, HTTPS, and WebSocket support.
Azure VMYou already operate VMs or want direct OS/reverse-proxy control.Run the container or service behind Nginx, Caddy, Azure Application Gateway, or another HTTPS proxy.
AKSYou already operate Kubernetes.Use normal Deployment, Service, Ingress, env var, and persistent volume primitives.
This guide gives full commands for Azure Container Apps. For the other hosting models, use the same Entra apps, runtime environment variables, database requirements, and verification steps. Useful Microsoft docs:

Prerequisites

You need:
  • An Azure subscription in the Microsoft Entra tenant where your users sign in.
  • Permission to create app registrations in that tenant.
  • Permission to create the selected Azure hosting resources.
  • A PostgreSQL or MongoDB database reachable from the Sovara Server hosting environment.
  • Azure CLI installed and authenticated.
  • The published Sovara Server container image tag from Sovara.
  • The Remote desktop app build.
Useful Microsoft docs if something is not installed or enabled:

Setup Variables

Choose names before running commands. Container App names must be lowercase, start with a letter, end with a letter or number, and be less than 32 characters. Set SOVARA_IMAGE to the published image tag Sovara gives you. Then choose one database backend and set only the variables for that backend. The deployment examples below use MongoDB Atlas first, but PostgreSQL and SQLite are also supported.

Common Variables

export TENANT_ID="<tenant-guid>"
export SUBSCRIPTION_ID="<subscription-guid>"
export RESOURCE_GROUP="rg-sovara-enterprise"
export LOCATION="westeurope"
export CONTAINERAPPS_ENV="sovara-env"
export CONTAINER_APP_NAME="sovara-server"
export SERVER_API_APP_NAME="Sovara Server API"
export DESKTOP_APP_NAME="Sovara Desktop"
export SOVARA_IMAGE="<published-sovara-server-image>:<version>"
export REDIRECT_URI="http://localhost/auth/callback"

Database Variables

Choose exactly one database backend for the server deployment.
export SOVARA_DB_BACKEND="mongodb"
export SOVARA_MONGODB_URI="mongodb+srv://<user>:<password>@<cluster>.mongodb.net/?retryWrites=true&w=majority"
export SOVARA_MONGODB_DB="sovara"

Sign In and Select the Subscription

az login --tenant "$TENANT_ID"
az account set --subscription "$SUBSCRIPTION_ID"
az account show --query "{name:name,id:id,tenantId:tenantId,user:user.name}" -o table
Check that the printed TenantId and subscription are the ones you expect. If az account set says the subscription does not exist, sign in with the tenant explicitly again and confirm that your user has access to that subscription.

Create the Entra App Registrations

Sovara uses two app registrations:
  • The Server API app represents the Sovara Server API and exposes the access_as_user delegated scope.
  • The Desktop app is a public client. It uses PKCE and the loopback redirect URI http://localhost/auth/callback. It has no client secret.

Create the Server API App

SERVER_APP_ID=$(
  az ad app create \
    --display-name "$SERVER_API_APP_NAME" \
    --sign-in-audience AzureADMyOrg \
    --query appId \
    -o tsv
)

SERVER_OBJECT_ID=$(az ad app show --id "$SERVER_APP_ID" --query id -o tsv)

az ad app update \
  --id "$SERVER_APP_ID" \
  --identifier-uris "api://$SERVER_APP_ID"

echo "SERVER_APP_ID=$SERVER_APP_ID"
echo "SERVER_OBJECT_ID=$SERVER_OBJECT_ID"

Expose the access_as_user Scope

The scope ID must be a GUID. The commands below patch the app manifest through Microsoft Graph because the scope object is easier to set exactly that way.
SCOPE_ID=$(uuidgen)
SCOPE_FILE=$(mktemp)

cat > "$SCOPE_FILE" <<EOF
{
  "api": {
    "requestedAccessTokenVersion": 2,
    "oauth2PermissionScopes": [
      {
        "adminConsentDescription": "Allow Sovara Desktop to access Sovara Server as the signed-in user.",
        "adminConsentDisplayName": "Access Sovara Server",
        "id": "$SCOPE_ID",
        "isEnabled": true,
        "type": "User",
        "userConsentDescription": "Allow Sovara Desktop to access Sovara Server as you.",
        "userConsentDisplayName": "Access Sovara Server",
        "value": "access_as_user"
      }
    ]
  }
}
EOF

az rest \
  --method PATCH \
  --uri "https://graph.microsoft.com/v1.0/applications/$SERVER_OBJECT_ID" \
  --headers "Content-Type=application/json" \
  --body "@$SCOPE_FILE"

SERVER_SCOPE_ID=$(
  az ad app show \
    --id "$SERVER_APP_ID" \
    --query "api.oauth2PermissionScopes[?value=='access_as_user'].id | [0]" \
    -o tsv
)

echo "SERVER_SCOPE_ID=$SERVER_SCOPE_ID"

Create the Desktop Public Client App

DESKTOP_APP_ID=$(
  az ad app create \
    --display-name "$DESKTOP_APP_NAME" \
    --sign-in-audience AzureADMyOrg \
    --is-fallback-public-client true \
    --public-client-redirect-uris "$REDIRECT_URI" \
    --query appId \
    -o tsv
)

az ad app permission add \
  --id "$DESKTOP_APP_ID" \
  --api "$SERVER_APP_ID" \
  --api-permissions "$SERVER_SCOPE_ID=Scope"

az ad sp create --id "$SERVER_APP_ID" >/dev/null
az ad sp create --id "$DESKTOP_APP_ID" >/dev/null

echo "DESKTOP_APP_ID=$DESKTOP_APP_ID"
If tenant users are allowed to consent to this delegated permission themselves, this step may not be required. For enterprise tenants, ask a tenant admin to run it or grant consent in the Portal.
az ad app permission admin-consent --id "$DESKTOP_APP_ID"

Configure Sovara Server Auth

Every hosting model must pass these Entra/OIDC environment variables to sovara-server:
SOVARA_AUTH_MODE=enterprise
SOVARA_ENTRA_TENANT_ID=<tenant-guid>
SOVARA_OIDC_ISSUER=https://login.microsoftonline.com/<tenant-guid>/v2.0
SOVARA_OIDC_AUDIENCE=<server-api-client-id>
SOVARA_OIDC_REQUIRED_SCOPE=access_as_user
SOVARA_DESKTOP_CLIENT_ID=<desktop-client-id>
SOVARA_DESKTOP_SCOPES=api://<server-api-client-id>/access_as_user
The container should listen on 0.0.0.0:5959. The Azure hosting layer should terminate HTTPS and forward traffic to port 5959.

Configure Database and Storage

Sovara needs persistent state for projects, memberships, agent tokens, runs, annotations, priors, and model settings. For Azure deployments, configure the database at server deployment time. Do not store the MongoDB URI or PostgreSQL DSN in the desktop app or in normal project settings. Use one of these supported backends:
  • PostgreSQL: set SOVARA_DB_BACKEND=postgresql and SOVARA_PG_DSN.
  • MongoDB: set SOVARA_DB_BACKEND=mongodb, SOVARA_MONGODB_URI, and SOVARA_MONGODB_DB.
For Azure Container Apps, store the connection string as a Container App secret and reference that secret from the environment variable. The database must be reachable from the Sovara Server container, not only from an administrator’s laptop.

MongoDB Atlas on Azure

MongoDB Atlas is the recommended first MongoDB path because it gives you a managed MongoDB cluster without running database servers yourself.
  1. In MongoDB Atlas, create or select an Atlas project.
  2. Create a cluster with Microsoft Azure as the cloud provider. Choose the same Azure region as the Sovara Server when possible.
  3. In Database Access, create a database user for Sovara. Grant it read/write access to the sovara database.
  4. In Network Access, allow the Sovara Server hosting environment to connect. For a temporary validation deployment, 0.0.0.0/0 works because it allows any IPv4 source address, including Azure Container Apps outbound IPs. Before using the deployment with real workspace data, replace it with Atlas private networking or a stable Azure outbound IP allowlist.
  5. In the cluster Connect flow, choose Drivers and copy the mongodb+srv://... connection string.
  6. Replace the placeholder username and password in that string with the database user credentials.
  7. Set SOVARA_MONGODB_URI to that full connection string and SOVARA_MONGODB_DB to sovara.
For a new Azure Container Apps deployment, the create command later in this guide declares the mongodb-uri secret inline. If you are updating an existing Container App, create or replace the secret and reference it from the runtime environment:
az containerapp secret set \
  --resource-group "$RESOURCE_GROUP" \
  --name "$CONTAINER_APP_NAME" \
  --secrets mongodb-uri="$SOVARA_MONGODB_URI"

az containerapp update \
  --resource-group "$RESOURCE_GROUP" \
  --name "$CONTAINER_APP_NAME" \
  --set-env-vars \
    SOVARA_DB_BACKEND=mongodb \
    SOVARA_MONGODB_URI=secretref:mongodb-uri \
    SOVARA_MONGODB_DB="$SOVARA_MONGODB_DB"
Updating a Container App secret does not automatically change already-running revisions. If you update the secret value later, restart the active revision or create a new revision so the container reads the new value.
ACTIVE_REVISION=$(
  az containerapp revision list \
    --resource-group "$RESOURCE_GROUP" \
    --name "$CONTAINER_APP_NAME" \
    --query "[?properties.trafficWeight==\`100\`].name | [0]" \
    -o tsv
)

az containerapp revision restart \
  --resource-group "$RESOURCE_GROUP" \
  --name "$CONTAINER_APP_NAME" \
  --revision "$ACTIVE_REVISION"

PostgreSQL

If your organization standardizes on PostgreSQL, create a reachable PostgreSQL database and use a DSN like:
postgresql://<user>:<password>@<host>:5432/sovara
For a new Azure Container Apps deployment, use --secrets pg-dsn=... in the create command and set SOVARA_PG_DSN=secretref:pg-dsn. If you are updating an existing Container App, store the DSN as a secret and reference it from SOVARA_PG_DSN:
az containerapp secret set \
  --resource-group "$RESOURCE_GROUP" \
  --name "$CONTAINER_APP_NAME" \
  --secrets pg-dsn="$SOVARA_PG_DSN"

az containerapp update \
  --resource-group "$RESOURCE_GROUP" \
  --name "$CONTAINER_APP_NAME" \
  --set-env-vars \
    SOVARA_DB_BACKEND=postgresql \
    SOVARA_PG_DSN=secretref:pg-dsn

SQLite

SQLite is acceptable for local development, single-machine testing, and temporary validation deployments. Do not use SQLite as the system of record for an Azure Container Apps deployment unless you have explicitly attached durable storage and accepted the operational tradeoffs. For shared Azure deployments, choose a managed MongoDB or PostgreSQL backend.

Deploy Sovara Server on Azure Container Apps

This is the recommended default path for small Azure deployments. The command below uses MongoDB Atlas as the database example and stores the Atlas connection string as a Container App secret. If your organization uses PostgreSQL, use the PostgreSQL secret and env vars from Configure Database and Storage instead.

Create the Resource Group and Container Apps Environment

az extension add --name containerapp --upgrade
az provider register --namespace Microsoft.App
az provider register --namespace Microsoft.OperationalInsights

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

az containerapp env create \
  --name "$CONTAINERAPPS_ENV" \
  --resource-group "$RESOURCE_GROUP" \
  --location "$LOCATION"

Create the Container App

The published container listens on port 5959. Azure Container Apps gives it a public HTTPS endpoint and forwards traffic to that port.
az containerapp create \
  --name "$CONTAINER_APP_NAME" \
  --resource-group "$RESOURCE_GROUP" \
  --environment "$CONTAINERAPPS_ENV" \
  --image "$SOVARA_IMAGE" \
  --ingress external \
  --target-port 5959 \
  --cpu 0.5 \
  --memory 1.0Gi \
  --min-replicas 1 \
  --max-replicas 1 \
  --secrets mongodb-uri="$SOVARA_MONGODB_URI" \
  --env-vars \
    SOVARA_AUTH_MODE=enterprise \
    SOVARA_ENTRA_TENANT_ID="$TENANT_ID" \
    SOVARA_OIDC_ISSUER="https://login.microsoftonline.com/$TENANT_ID/v2.0" \
    SOVARA_OIDC_AUDIENCE="$SERVER_APP_ID" \
    SOVARA_OIDC_REQUIRED_SCOPE=access_as_user \
    SOVARA_DESKTOP_CLIENT_ID="$DESKTOP_APP_ID" \
    SOVARA_DESKTOP_SCOPES="api://$SERVER_APP_ID/access_as_user" \
    SOVARA_DB_BACKEND=mongodb \
    SOVARA_MONGODB_URI=secretref:mongodb-uri \
    SOVARA_MONGODB_DB="$SOVARA_MONGODB_DB"

Get the Server URL

SERVER_FQDN=$(
  az containerapp show \
    --name "$CONTAINER_APP_NAME" \
    --resource-group "$RESOURCE_GROUP" \
    --query properties.configuration.ingress.fqdn \
    -o tsv
)

SERVER_URL="https://$SERVER_FQDN"
echo "$SERVER_URL"

Alternative Azure Hosting Notes

Azure VM

Run the published Sovara Server image or service on the VM, bind it to an internal port, and put HTTPS in front of it with your standard reverse proxy. The reverse proxy must pass WebSocket upgrades. Make sure the VM can reach the configured PostgreSQL or MongoDB database endpoint.

Azure App Service for Containers

Use the published image, configure the same server environment variables, enable HTTPS, and ensure WebSocket support is enabled. Store the database connection in App Service configuration or Key Vault references, and pass it to sovara-server with the same environment variable names.

AKS

Use the published image in a Deployment, expose it through your Ingress controller, configure the same environment variables as Kubernetes secrets/config maps, and make sure the cluster can reach the configured PostgreSQL or MongoDB database endpoint.

Verify the Server

curl -fsS "$SERVER_URL/health"
curl -fsS "$SERVER_URL/auth/config"
The /auth/config response should include:
{
  "auth_mode": "enterprise",
  "provider": "entra",
  "tenant_id": "<tenant-guid>",
  "desktop_client_id": "<desktop-client-id>",
  "scopes": ["api://<server-api-client-id>/access_as_user"],
  "server_audience": "<server-api-client-id>"
}

Connect the Remote Desktop App

  1. Open the Remote desktop app.
  2. Enter the server HTTPS URL.
  3. Click Connect.
  4. Click Sign in with Microsoft.
  5. Complete the browser login.
  6. Confirm the desktop app shows your Entra profile.
  7. Create a project. The creator becomes project admin.

Connect an Agent Project

Open the project settings in the Remote desktop app and create an agent token. The generated command should already include the server URL, project ID, and agent token.
sovara connect-agent \
  --project-root /path/to/agent/project \
  --server-url "$SERVER_URL" \
  --project-id "<project-id>" \
  --agent-token "<agent-token>"
Then run the agent normally from that project folder. The registered run should appear in the project without the runner sending a user ID.

Access Model

The Entra token only proves who the user is. Sovara project access still comes from the Sovara database:
  • Project creators become admin.
  • Admins can add members and create agent tokens.
  • Editors and viewers cannot create agent tokens.
  • Agent tokens can only write runs to their bound project.
Adding a member by email or user ID requires that the user has signed in at least once, so their Entra identity exists in Sovara. Directory-wide user search is not part of this setup because it requires Microsoft Graph permissions and admin consent.

Troubleshooting

az account set Cannot Find the Subscription

Confirm that you logged into the correct tenant:
az account list --query "[].{name:name,id:id,tenantId:tenantId,isDefault:isDefault}" -o table
az login --tenant "$TENANT_ID"
If the subscription still does not appear, the signed-in user does not have access to that subscription in this tenant.

App Registration Creation Fails

The tenant may block users from creating app registrations. Ask an Entra admin to either run the app registration steps or temporarily grant the required permission. Microsoft documents the Portal flow in Register an application.

Desktop Login Shows Redirect URI Mismatch

Check the Desktop app registration:
  • It must be a public client/native app.
  • It must have http://localhost/auth/callback as a redirect URI.
  • The Sovara Server env var SOVARA_DESKTOP_CLIENT_ID must be the Desktop app application client ID, not the Server API app client ID.

Desktop Login Shows Invalid Audience or Scope

Check the Server API app and server env vars:
  • SOVARA_OIDC_AUDIENCE must equal the Server API app client ID.
  • SOVARA_DESKTOP_SCOPES must equal api://<server-api-client-id>/access_as_user.
  • The Desktop app must have the delegated access_as_user permission for the Server API app.
  • If tenant policy requires it, admin consent must be granted.

Desktop Cannot Load Auth Config

Run:
curl -v "$SERVER_URL/auth/config"
If this fails, check the server URL, HTTPS ingress, container or VM startup, and network policy between the desktop machine and Azure.

MongoDB Connection Fails

Check the Container App logs first. Common causes:
  • Authentication failed: the Atlas database username or password in the mongodb-uri secret is wrong. Reset the Atlas database user password, update the Container App secret, and restart the active revision.
  • ServerSelectionTimeoutError or connection timeout: the Atlas IP access list or private networking is blocking the Sovara Server outbound connection.
  • Cannot specify multiple hosts with directConnection=true: the server image is too old for Atlas mongodb+srv://... URIs. Deploy a Sovara Server image that supports Atlas SRV connection strings.
For a temporary validation deployment, 0.0.0.0/0 in the Atlas IP access list confirms that the database and credentials work. Before using the deployment with real workspace data, replace it with private networking or a restricted allowlist.

Inspect Azure Container Apps Logs

az containerapp logs show \
  --name "$CONTAINER_APP_NAME" \
  --resource-group "$RESOURCE_GROUP" \
  --follow

Inspect Azure Container Apps Environment Variables

az containerapp show \
  --name "$CONTAINER_APP_NAME" \
  --resource-group "$RESOURCE_GROUP" \
  --query "properties.template.containers[0].env" \
  -o table

Clean Up and Retry

To retry from scratch, delete the Azure resource group and both app registrations.
az group delete --name "$RESOURCE_GROUP" --yes
az ad app delete --id "$DESKTOP_APP_ID"
az ad app delete --id "$SERVER_APP_ID"
If the Remote desktop app still remembers an old backend, use the app’s remote connection settings to replace it. If an agent project points at an old project, run the new sovara connect-agent command from that project’s settings.