Skip to content
OpenSmartRoute
Documentation
Get started

Install the platform

Run the whole platform yourself: Azure Marketplace or Docker Compose, first sign-in, custom domain, upgrade and backup.

Get started 14 min read

The hosted platform at opensmartroute.ai needs no installation - sign up, create a key, follow the platform quickstart. This page is for running the whole platform yourself: the routing API with its OpenAI-compatible endpoint, the web app (public site, dashboard, operator console), the routing SLM service and the state behind them. The same images and the same OSR_PLATFORM_* settings run everywhere; only the way they are started differs.

WhereHowFor
Azure, from the portalAzure Marketplace - the solution template with a wizard, one resource group, no tooling (the same template deploys from the published package until the listing is live)Teams that want the platform in their own subscription
Your own servers or any cloud VMDocker Compose - PostgreSQL, Redis, Kafka and the platform as containers, from the published Compose bundleOn-premises, air-gapped, other clouds
Kubernetesosr serve Helm chart for the router alone; the platform on Kubernetes runs the same containers as ComposeSidecar / control-plane routing without the hosted-platform surfaces

Every file named on this page - the Compose bundle, the Helm chart, the Marketplace package and its two files, the checksums of the PyPI release - is on the downloads page with its version and SHA-256; the stable links https://opensmartroute.ai/download/<compose|helm|marketplace|marketplace-template|marketplace-ui|checksums> always resolve to the current release and https://opensmartroute.ai/download/latest.json lists them for scripts. The source repository is private; nothing here needs it.

Just the Python library or the osr command line: README - Install. Calling the platform from TypeScript or JavaScript: npm install @opensmartroute/sdk (TypeScript SDK).

What you get

Every install path deploys the same product:

  • osr-platform-api - the FastAPI platform: POST /api/v1/route, the OpenAI-compatible /v1 proxy, MCP server, accounts, keys, workspaces, tenants, governance, marketplace, billing, notifications, support tickets and the operator API. In cluster mode it also runs once per domain (docs, rankings, marketplace, providers, onboarding, accounts, billing, admin, mcp, openai, routing) behind the API gateway, plus the routing SLM service.
  • osr-platform-web - the Next.js web app: the public site and documentation, /platform (sign-in, dashboard, playground, publish flow) and /platform/admin (the operator console).
  • State: PostgreSQL (always), Redis and Kafka (cluster mode), a data volume for the audit chain and the routing SLM.
  • Model providers: open-weight models through Ollama by default - on Azure the template runs the curated set as its own container apps (CPU) or one serverless-GPU server and maps every tier and specialist target to it, with Ollama Cloud for the frontier open weights when you give it a key; Azure OpenAI deployments only when you switch them on (azureOpenAiEnabled). Anything OpenAI-compatible everywhere (connected in the console under Providers, or by workspaces with their own keys under Model providers).

Release images: crosrua2c6onyidguw.azurecr.io/osr-platform-api:2.1.0 and crosrua2c6onyidguw.azurecr.io/osr-platform-web:2.1.0 (anonymous pull; :latest follows the newest release, :<major.minor> the newest patch). The web image is built without a baked hostname: it reads OSR_PUBLIC_WEB_URL at run time and the browser uses the origin it is talking to, so one image serves any domain.

Azure Marketplace

The offer OpenSmartRoute platform is an Azure Application (solution template): it deploys into a resource group of your subscription, you own every resource, nothing is shared with the publisher and there is no software charge on the Azure bill - you pay the Azure resources it creates.

Listing status. The offer package is the Partner Center submission; until the listing is live the offer does not appear in a Marketplace search. The same template deploys today from the published package - mainTemplate.json and createUiDefinition.json from the downloads page - with the same wizard when you use the portal's Deploy a custom template, or from the command line:

curl -fsSLO https://opensmartroute.ai/download/marketplace-template     # opensmartroute-mainTemplate-<version>.json
az group create -n rg-osr -l centralindia
az deployment group create -g rg-osr -f opensmartroute-mainTemplate-*.json \
   -p adminUsername=admin adminPassword='<at least 8 characters>' platformEdition=enterprise clusterMode=false

Every wizard field below is a parameter of that template (az deployment group what-if lists them with their descriptions); the outputs, first sign-in, custom-domain, upgrade and backup steps are identical.

Deploy

  1. In the Azure portal open Marketplace, search for OpenSmartRoute, choose the plan and Create (or Deploy a custom template -> Build your own template in the editor -> Load file with the downloaded mainTemplate.json, then Edit UI definition with createUiDefinition.json).
  2. Basics - subscription, a new or existing resource group, the region (Container Apps must be offered there - and Azure OpenAI with the default gpt-4.1 family when you switch those deployments on), a deployment name, the first operator username and password (the console sign-in), the edition (Enterprise: tenants, audit, statistics, persisted learners; Community: the lightest footprint).
  3. Size and state - Cluster mode (Redis + Kafka, the API split into domain services scaled on demand; off = one API replica with PostgreSQL only, right for a trial) and Managed state (Azure Database for PostgreSQL with point-in-time restore, Azure Cache for Redis, Event Hubs - recommended for production; off = state containers on Azure Files, cheapest).
  4. Hostnames - leave empty to use the Container Apps hostnames, or enter your web and API hostnames (and let the template create the Azure DNS zone). TLS certificates are issued in a second run once DNS resolves (below).
  5. E-mail - Microsoft 365 (an Entra app registration with the application permission Mail.Send, sending as the From mailbox), Azure Communication Services or SMTP for verification, reset, invitation, alert, usage-report and support-ticket mail; the first transport set wins in that order. Without a transport every message waits in the console's Mail page; nothing is lost.
  6. Models and learning - the self-hosted model server (Ollama, CPU or serverless GPU; on by default, it answers every tier), an Ollama Cloud key for the frontier open weights, Azure OpenAI deployments (off by default), the autopilot (retrain the routing model from your own feedback) and the marketplace catalogue refresh.
  7. Sign-in and access - password sign-in, self-serve signups per IP (0 = operators create every workspace), SSO providers, a static admin token for scripts, public /metrics.
  8. Images and billing - the release to deploy and the registry to pull from (yours, after mirroring), Stripe for self-serve paid plans.
  9. Review + create. A cluster-mode deployment takes about ten minutes; the first Ollama start pulls about 16 GB of weights in the background.

The deployment's Outputs tab lists webUrl, platformUrl, adminLoginUrl, apiUrl, the hostnames and static IP for your DNS records, the asuid verification id and, when enabled, the Azure OpenAI endpoint.

First sign-in

  1. Open adminLoginUrl (<webUrl>/platform/admin/login) and sign in with the operator username and password from Basics. The console shows the deployment's health, users, workspaces, plans, providers and the support desk.
  2. Providers: the Ollama model server the template started is connected and every tier (llm-small, llm-mid, llm-frontier, llm-onprem) and specialist target already executes on it - Qwen3.5 4B on a CPU profile, the dedicated-node models (GLM-4.7-Flash, gpt-oss-20b) on a dedicated profile, gpt-oss-120b / DeepSeek-V4-Flash through Ollama Cloud when a key was given. Add OpenAI, Azure OpenAI, Anthropic, Bedrock, Vertex, vLLM or any OpenAI-compatible endpoint here, or let workspaces bring their own keys.
  3. Open webUrl, Sign up (or create the workspace from the console when self-serve signup is off), create an API key and route a request - platform quickstart with OSR_API_URL=<apiUrl>.
  4. osr login --url <webUrl> signs the command line in to your deployment.

Custom domain (two runs)

  1. First run with your hostnames entered and Issue TLS certificates off: the apps bind the hostnames without TLS.
  2. Create the DNS records from the outputs: CNAME <web hostname> -> webHostname, CNAME <api hostname> -> apiHostname (an apex domain gets A -> staticIp), and TXT asuid.<hostname> -> domainVerificationId for each hostname. If the template created the zone, delegate the domain to dnsNameServers instead - the records are already there.
  3. Run the deployment again (Redeploy on the deployment, same values) with Issue TLS certificates on. Azure issues free managed certificates and the hostnames answer over HTTPS.

Upgrade, backup, remove

  • Upgrade: redeploy with a newer OpenSmartRoute release (image tag). The database schema migrates itself at start-up - the same numbered ledger, pre-upgrade backup on the data share (backups/pre-upgrade-*.jsonl.gz) and downgrade rules as under Operate below; state lives in PostgreSQL and on the data share, never in the containers. When an upgrade replaces the database container itself, the new replica waits for the previous one to shut down before it opens the database (Container Apps runs both for a moment); expect the API to pause for up to a minute.
  • Backup: a nightly job writes a logical database backup to the deployment's backup share (14 days kept). Managed state adds point-in-time restore on the server. Restore: load the newest archive into a fresh database with psql, then restart the API.
  • Remove: delete the resource group. Nothing is left outside it.

Sizing and cost (Azure list prices, rough)

ConfigurationMonthly Azure costSuits
Community, cluster mode off, Azure Files state~40-70 USD (Container Apps consumption, storage, Log Analytics) + model usageTrial, a team
Enterprise, cluster mode, Azure Files state~150-250 USDProduction without SLA requirements
Enterprise, cluster mode, managed state (B1ms PostgreSQL, C0 Redis, 1 TU Event Hubs)~250-400 USDProduction with backups, SLAs, HA options
+ Ollama on serverless GPU (T4)pay per second of GPU while replicas runPrivate / specialist models without cloud keys

Model usage runs on the self-hosted Ollama apps (part of the Container Apps figure; a serverless GPU is billed per second while replicas run) and, when you connect them, on Ollama Cloud or a closed-model provider at their own prices; the dashboard's Savings page shows what routing saved against always calling the frontier model.

Your own servers (Docker Compose)

Runs on any host with Docker Engine 24+ (Linux, or Docker Desktop for a workstation): on-premises, air-gapped, or a VM in any cloud. Minimum 4 vCPU / 8 GiB / 40 GB disk for the platform; add what your models need if you run them on the same host.

1. Get the stack

Download the Compose bundle of the current release - docker-compose.yml, .env.example and the Ollama overlays in the layout this page uses - and unpack it:

curl -fsSLO https://opensmartroute.ai/download/compose        # opensmartroute-compose-<version>.zip
unzip opensmartroute-compose-*.zip && cd opensmartroute-*/
cp platform/.env.example platform/.env

The bundle's .env.example already names the images of its own release; the stack runs from the published images and needs no source.

2. Configure

Edit platform/.env: the passwords and the session secret (all of them - the defaults are placeholders), the public URLs of the web app and the API as your users will reach them, the release images, and the e-mail transport. Everything is documented in the file; the same variables are explained in platform/README.md.

3. Start

docker compose -f platform/docker-compose.yml --env-file platform/.env pull
docker compose -f platform/docker-compose.yml --env-file platform/.env run --rm api python -m osr_platform.app --check
docker compose -f platform/docker-compose.yml --env-file platform/.env up -d
curl -fsS http://localhost:8080/readyz          # every check ok once PostgreSQL is up and the schema is created

The --check line is the configuration preflight: it reads .env the way the API will and lists what is still a placeholder or inconsistent - the shipped passwords, a session secret missing on a multi-replica stack, public URLs still on localhost, e-mail confirmation required without a mail transport, a providers file that is not in the container - each with the variable that fixes it, and exits non-zero on an error. The same findings are logged at start-up and shown on the console's Health page. Web http://localhost:3000, operator console http://localhost:3000/platform/admin/login (the operator username and password from .env), API http://localhost:8080/api/v1/info. OSR_API_IMAGE / OSR_WEB_IMAGE in .env name the release images; a checkout of the source can leave them empty to build locally (up -d --build).

Just the router with a dashboard? platform/docker-compose.minimal.yml starts four containers - PostgreSQL, Redis, one API process serving every domain, the web app - with OSR_PLATFORM_PROFILE=minimal: accounts, keys, routing, the OpenAI-compatible /v1 proxy, MCP, billing and the console, and none of the jobs a hosted deployment runs for itself (blog writer, newsletter, growth engine, marketplace harvest). No Kafka, no separate SLM trainer. The same .env applies; switch to docker-compose.yml later without migrating anything - both stacks use the same database schema.

4. Connect models

The stack has no cloud account. Either connect providers in the console under Providers (any OpenAI-compatible endpoint: OpenAI, Azure OpenAI, Anthropic through a gateway, vLLM, Ollama, ...) or start the on-prem model server next to it - the llm profile of the same Compose file - and point the router at its providers file:

# in platform/.env:
#   COMPOSE_PROFILES=llm
#   OSR_PLATFORM_PROVIDERS=/config/providers.ollama.yaml
docker compose -f platform/docker-compose.yml --env-file platform/.env up -d
# on an NVIDIA host add the GPU overlay:
docker compose -f platform/docker-compose.yml -f deploy/compose/ollama.gpu.yaml --env-file platform/.env up -d

The ollama service pulls the curated open weights of deploy/compose/providers.ollama.yaml (Qwen 3.5, Qwen2.5-Coder, GLM-OCR, TranslateGemma, DeepSeek-R1, a Qwen3 embedding model; OSR_OLLAMA_MODELS trims the list on a small machine) and that file maps the private / specialist targets to it - deploy/compose is mounted at /config in every platform container, which is why the path above works. OSR_PLATFORM_PROVIDERS points the API at a providers file for anything else.

5. Put it behind TLS

Terminate TLS in your reverse proxy (nginx, Caddy, Traefik, an ingress) in front of ports 3000 (web) and 8080 (API), set OSR_PLATFORM_WEB_URL / OSR_PLATFORM_BASE_URL to the public https:// origins, and remove the host port mappings you do not want exposed. Several API replicas share PostgreSQL / Redis / Kafka: docker compose up -d --scale api=3 behind the proxy, exactly what the Azure template does.

Operate

  • Backups: docker compose -f platform/docker-compose.yml exec postgres pg_dump -U osr osr | gzip > osr-$(date +%F).sql.gz (and osr_telemetry); restore with psql into a fresh volume. Without pg_dump at hand, the API container does it from its own connection: docker compose ... run --rm api python -m osr_platform.maintenance backup /data/backups/osr-$(date +%F).jsonl.gz writes every row of every table as one gzip file, ... restore <file> --yes (API stopped) empties the tables and loads it back in one transaction.
  • Upgrade: set the new tag in .env, pull, run --rm api python -m osr_platform.app --check, up -d. The schema is a numbered ledger (schema_migrations): the new release applies the migrations it knows and the database has not run yet, in order, and records each with the release that wrote it - python -m osr_platform.maintenance schema shows the ledger, applied and pending. Before a pending migration that is not additive runs on a database that already holds data, the API writes a backup to <data dir>/backups/pre-upgrade-<schema version>-<time>.jsonl.gz on its own (OSR_PLATFORM_PRE_UPGRADE_BACKUP=false turns that off, OSR_PLATFORM_BACKUPS_KEEP is how many it keeps); an additive change needs none - the way back from it is the older release itself - and a fresh install never writes one. The backup never holds the schema lock, so replicas already serving keep answering while it is written. Skipping releases is fine: the migrations between them run in sequence at the next start.
  • Downgrade: set the previous tag and up -d. Every migration is marked additive (it only added columns, indexes or tables) or not. An older release starts against a database a newer one migrated when every newer change is additive - it logs a warning (also shown on the console's Health page under Configuration) and ignores the columns it does not know. A non-additive newer change stops start-up with the release that wrote it named: run that release, or put the database back with python -m osr_platform.maintenance restore of the pre-upgrade-* backup the upgrade took (a restore refuses a backup written by a newer schema than the running release knows; an older one loads and the columns added since take their defaults).
  • Edition switch: OSR_PLATFORM_EDITION is read at start-up and every table exists in both editions, so moving between community and enterprise is a restart with the new value and back - nothing to migrate. The database remembers which edition opened it: the first start after a change logs what the switch means (community writes no audit log and does not read the persisted learner state, so an enterprise stack that ran as community for a while has that gap in its audit chain) and the console's Health page shows the same sentence under Configuration until the next restart.
  • Air-gapped: on a connected machine docker pull both images (and postgres:16-alpine, redis:7-alpine, apache/kafka:3.9.0), docker save | gzip, move, docker load; or mirror them into your registry (az acr import --source crosrua2c6onyidguw.azurecr.io/osr-platform-api:2.1.0 ...) and set OSR_API_IMAGE / OSR_WEB_IMAGE (or the Marketplace wizard's Image registry).
  • Health: GET /readyz on the API (database, Redis, Kafka, mail transport, routing probe), /platform/admin/health in the console (readiness, traffic, breakers, alerts, the configuration preflight), GET /metrics for Prometheus.

Monitoring

Every install monitors itself: the platform's own telemetry store keeps every span and event in PostgreSQL, GET /metrics serves Prometheus text, GET /readyz and GET /api/v1/status answer probes and status pages, the console's Health page shows readiness, traffic, latency, circuit breakers, alerts and the configuration preflight. Nothing else is required.

On Azure the template and the Marketplace offer also create a workspace-based Application Insights resource (appi-osr-<token>, next to the Log Analytics workspace that already receives the container console logs; the Application Insights checkbox of the wizard, OSR_APPLICATION_INSIGHTS=false in the azd environment turns it off) and hand every platform container its connection string. Every process then exports requests, routing spans (dependencies), routing events (customEvents), warnings and exceptions under the request's operation id, so the end-to-end transaction view, the application map, KQL, workbooks and Azure Monitor alerts work out of the box. The export is OSR_PLATFORM_APPINSIGHTS=auto: it only runs where a connection string is present and the host is Azure.

On your own servers nothing is exported anywhere. To report a self-hosted stack into an Azure subscription anyway, create an Application Insights resource there, put its connection string in OSR_PLATFORM_APPINSIGHTS_CONNECTION_STRING and set OSR_PLATFORM_APPINSIGHTS=on in .env; OSR_PLATFORM_APPINSIGHTS_SAMPLE_RATE bounds the ingestion volume. The settings are described in Platform guide.

Where the downloads come from

Every release publishes the Compose bundle, the Helm chart, the Marketplace package and the checksums of the PyPI build to the downloads page, together with a latest.json manifest (version, build time, per-file name, size and SHA-256) for scripts. Versioned files are immutable; latest.json always points at the current release. The Azure Marketplace offer is the same solution template, published through Partner Center; a plan's image tag always names release images that already exist, so a deployment never pulls a moving target.

Something wrong or out of date on this page? Tell us - the page is named for you, so a one-line note is enough.