Chroma on Azure Container Apps

Chroma 1.5.9 on Azure Container Apps, with the data on an Azure Files share. The app scales to zero when nobody uses it, so it costs almost nothing on idle days.

For a few hours of use a day. For a server in use all day, the VM template is cheaper, faster and needs no manual step at updates: it is the one we recommend.

Disclaimer. This template and its scripts are provided as is, without warranty of any kind, as stated in the Apache License 2.0. The numbers on this page are what we measured on 25, 26, 27 and 28 October 2026 in westeurope: your region, data and versions may behave differently. You are responsible for your data: back it up, and test a restore on a copy before you rely on it.

Not sure about your case? Open an issue in the VecturaDB repository, describe your data and your setup, and we will take a look on a best-effort basis.

What it creates

ResourceNotes
Container appChroma 1.5.9 (ghcr.io/chroma-core/chroma:1.5.9); one replica at most, scale to zero, HTTPS ingress on the Azure name of the app
Container Apps environmentConsumption plan
Storage account and file shareSMB, transaction optimized, 100 GiB quota; soft delete of the share, 7 days
Log Analytics workspaceThe logs of the app, 30 days by default

The server reaches the share through two mounts:

Deploy

Deploy to Azure

The button opens the form of the Azure portal with the template of the tag azure-v1.1.0. When the portal shows “Your deployment is complete”, open Outputs in the menu on the left: url is the address of the server, for the clients.

In our testsTime
Deployment from zero, most of it for the Container Apps environment15-18 min (881-1067 s)

With the Azure CLI:

az group create -n my-chroma -l westeurope
az deployment group create -g my-chroma \
  --template-uri https://raw.githubusercontent.com/VecturaDB/VecturaDB.Deploy/azure-v1.1.0/azure/container-apps/azuredeploy.json \
  -p allowedIpRanges='["203.0.113.10/32"]'

The template and its README are in the VecturaDB.Deploy repository, under azure/container-apps.

Access and HTTPS

AccessWho reaches the serverAddress
Allowed IP addressesOnly the IP ranges you givehttps://, on the Azure name of the app
InternalOnly the apps in the same Container Apps environmenturl, inside the environment

Cost

Container Apps bills the seconds a replica runs: active while it serves, at a lower rate while it waits before scaling to zero. The first 180,000 vCPU-seconds, 360,000 GiB-seconds and 2 million requests of each month are free, for the whole subscription.

With 0.5 CPU and 1 GiB, at the list prices of westeurope in USD on 27 October 2026:

UseContainer Apps, with the free grantWithout the free grantVM template (B2pts_v2)
In use all day, every day47.6355.1914.26
8 hours a day10.6918.2514.26
3 hours a day0.006.9114.26
One hour a month0.000.0914.26

Not in the table:

Also billedIn our tests
Azure Files, the stored data0.06 USD per GB a month
Azure Files, the transactions of the writesAbout 0.03 USD for every 1,000 writes; reads cost almost nothing
A replica that waits without requestsAbout 27 transactions a minute
Log AnalyticsThe logs it receives

Other apps in the same subscription use the same free grant.

Backups and restore

No backups: the template does not back up the share. Soft delete keeps a deleted share for 7 days, but it does not bring back data that was changed or deleted inside it. For backups, use the VM template.

Updates

A new deployment, with other settings or another image, creates a new revision. Its replica waits for the lock held by the old one, and the deployment ends anyway.

az containerapp revision list -g my-chroma -n chroma --query "[?properties.active].{name:name, state:properties.runningState}" -o table
az containerapp revision deactivate -g my-chroma -n chroma --revision <the old revision, the one that is not Activating>

In our tests the old revision kept serving while the new one waited, and the new revision answered with all the data.

In our testsTime
No answer after the deactivation, until the new revision servesAbout 6 s
Wait after which the new revision shows Failed5 min
Start of a Failed revision, after a later deactivation22 s

Delete everything

There is no backup vault here: deleting the resource group deletes the whole deployment. In the portal, open the resource group and select Delete resource group. Type the name of the group, select Delete, then Delete again to confirm. With the Azure CLI:

az group delete -n my-chroma --yes
In our testsTime
Deletion of the resource group, most of it for the Container Apps environment27-30 min (1632-1810 s)

Two things stay recoverable for 14 days after the deletion, as Azure does for these resources, and then go away:

To remove the logs at once, delete the workspace permanently before the group:

az monitor log-analytics workspace delete -g my-chroma -n chroma-logs --force --yes

Limits

In our testsTime
From the last request to zero replicas582-613 s
First request after scaling to zero26-32 s
Lock kept after its host disappeared655 s

Medians from a client in the same region, in milliseconds:

OperationVM (B2pts_v2, Standard SSD)Container Apps (0.5 CPU, 1 GiB, SMB)
query2.55.2-5.7
count2.14.4-4.5
get4.18.6-8.8
add112434-448
small add74136-147
8 concurrent writers, total16.2 s25.3-26.2 s

A lock left by a lost replica

When the host of the replica disappears without closing its files, Azure Files keeps the lock on the lock file until it gives up on that host. Until then the next replica waits, and the server does not answer. To free the lock earlier, close only the handle of the lost replica:

ACCOUNT=$(az storage account list -g my-chroma --query "[0].name" -o tsv)
KEY=$(az storage account keys list -g my-chroma -n $ACCOUNT --query "[0].value" -o tsv)
az storage share list-handle --account-name $ACCOUNT --account-key $KEY --name data --path .lock -o table
az storage share close-handle --account-name $ACCOUNT --account-key $KEY --name data --path .lock \
  --handle-id <the handle of the lost replica>

The list shows a handle for each replica that opened the file, with its client IP and the time it was opened: the lost replica is the older one. Never use --close-all: in our tests it also closed the handle of the process that was waiting, which then failed with an input/output error.

VecturaDB is an independent fork of Chroma, under the Apache License 2.0. The original code is copyright Chroma Inc. and its contributors.

Not affiliated with, endorsed by or sponsored by Chroma Inc. CHROMA is a registered trademark of Chroma Inc., used here only to describe compatibility.