Caleta AI Appliance / Documentation

Caleta AI Appliance user guide

Deploy the VM, SSH in, type llm, and serve an open-weight model privately in your own Azure subscription. This guide covers the menu, the chat UI and API, publishing over HTTPS, and locking the box down.

The appliance listens on loopback only. Nothing is reachable from the network until you deliberately expose it.

Sections 1 to 11 apply to both architectures, with x86 sizes and models in sections 10 and 11. Section 12 covers Azure Cobalt (ARM64).

The Caleta appliance llm menu: status block and action list
SSH in and type llm: boot banner, live status, and one-key actions. Shown on a single-H100 GPU box serving Qwen3-Coder-30B.

Section 1

What the appliance is

The Caleta AI Appliance is one Azure Marketplace VM image that runs open-weight large language models privately in your own Azure subscription. No data leaves the VM during inference.

A single image serves both CPU and GPU sizes. It detects a GPU at run time and uses the CUDA engine automatically, otherwise it runs on CPU.

The engine is llama.cpp (only). The chat interface is Open WebUI. There is nothing to install, it is all baked into the image.

Section 2

Deploy and first login

Deploy the offer from the Azure Marketplace into your own subscription. We recommend Trusted Launch (the image supports it) and one of the sizes in the supported sizes table.

Connect with an SSH key

ssh <admin-user>@<vm-public-ip>

Connect with Microsoft Entra ID (recommended)

Entra brings MFA and conditional access with no SSH keys to manage. Add the "Login with Entra ID" extension at deploy time, assign the signed-in user the Virtual Machine Administrator Login role on the VM, then connect:

az ssh vm -g <resource-group> -n <vm-name>

The menu launches automatically

On interactive login the llm menu starts by itself. A short boot banner appears first (the Caleta cloud logo and a few hints). For a plain shell, choose quit in the menu, or connect with the environment variable CALETA_NO_MENU=1 set.

Section 4

Download a model, then serve it

These are two separate steps. download a catalogue model puts the weights on disk. serve / swap model loads them into the engine. Downloading does not serve.

Downloading

Downloads pull direct from Hugging Face using the fast Xet transfer path. The progress bar shows size in GB and speed in Gbit/s, with peak transfer on completion. No model weights ship inside the image, and each model downloads under its own Hugging Face licence.

Model download progress in the llm menu
Models download direct from Hugging Face at multi-Gbit/s. No weights ship in the image.

Weights are cached at /mnt/nvme/models on sizes with a local NVMe disk. On sizes with no local disk they fall back to the OS disk, so size the OS disk for the model you want.

Serving

Serving uses a measured preset of engine flags per catalogue model, which you can edit at the prompt (press Enter to keep the preset).

Presets serve a single user with the full context window. For a team, raise --parallel to the number of concurrent users. The context is split evenly between them, so --ctx-size 65536 --parallel 16 gives each user 4096 tokens.

Fit guards

  • On CPU sizes, an oversized model warns and asks before serving.
  • On GPU sizes, the menu declines a model that does not fit in GPU memory. A spilled GPU model fails outright rather than just running slowly.

Use the supported sizes and model catalogue tables to choose.

Test prompt streaming output in the terminal
The built-in test prompt streams a live answer from the served model.

Section 5

Chat UI and API

The chat UI and API bind to loopback only (127.0.0.1). Nothing is reachable from the network until you expose it (section 6).

Open an SSH tunnel from your laptop

With key auth:

ssh -L 3000:localhost:3000 -L 8080:localhost:8080 <admin-user>@<vm-public-ip>

With Entra:

az ssh vm -g <resource-group> -n <vm-name> -- -L 3000:localhost:3000 -L 8080:localhost:8080

Chat UI

Open http://localhost:3000 for Open WebUI. The first account you create becomes the local admin. It is a local account on the VM, not a Caleta or Microsoft login.

Open WebUI start screen with a prompt entered
The private chat UI (Open WebUI) reached over an SSH tunnel at localhost:3000.
Open WebUI showing a generated Terraform answer
A real answer, generated locally: the model writes and explains an Azure Terraform module.
Open WebUI showing Terraform module code and an inputs table
Rendered code and an inputs table, all served on the box.

API

The API is at http://localhost:8080 and is OpenAI-compatible (/v1/chat/completions, /v1/models). Every request needs the per-boot key as an Authorization: Bearer <key> header.

The per-boot API key

A fresh key is minted on every boot, stored root-only on the VM, never baked into the image and never reused across boots. Print it with the menu's show API key option. The chat UI is wired to the current boot's key automatically.

Section 6

Publish over HTTPS with Let's Encrypt

The appliance uses Caddy to obtain and renew a real Let's Encrypt certificate automatically. This is the expose on :443 (TLS) menu option.

Do these two things first

These are the top support issue. Exposure will not work without them.

  1. Create a DNS A record for a domain you own, pointing at the VM's public IP.
  2. Open ports 80 and 443 inbound in the VM's Azure Network Security Group, in the portal. The menu only manages the host firewall. The NSG is cloud-side and separate.

Then expose

  1. Choose expose on :443 (TLS), enter the domain, and optionally restrict access to a source CIDR.
  2. Caddy obtains the certificate over ports 80 and 443. The menu verifies the domain actually resolves to this box and warns if not.
  3. Chat is then at https://<your-domain>/ and the API at https://<your-domain>/v1/.... The API key is still required on /v1.

Watch issuance and look for "certificate obtained":

sudo journalctl -u caddy -f

To stop publishing, run the same menu option and enter off.

Section 7

Lock the box down

The host firewall (ufw) is on by default: deny inbound, SSH allowed.

Recommended first line: restrict SSH at the NSG

Restrict SSH to your own IP in the Azure Network Security Group, in the portal, not in the VM. A ufw SSH lock can shut you out if your IP changes, and the serial console is then the only way back.

The firewall (ufw) menu option

  • Allow a CIDR to reach 443 and 80 (for expose)
  • Remove a rule
  • Restrict SSH to a CIDR, with a lockout warning and a typed confirmation
  • Re-open SSH

Remember that the chat UI (port 3000) and the API (port 8080) are loopback-bound, so opening them in ufw does nothing on its own. Reach them by SSH tunnel, or by exposing on :443.

Section 8

Extras

Auto-restore

Toggle it on and, after a restart or deallocate, the box re-downloads the last model to the local disk and serves it again by itself. Useful for parking the box overnight. Note that the ephemeral NVMe at /mnt/nvme is wiped on deallocate, which is why auto-restore re-downloads.

Spot placement

On a Marketplace-deployed appliance this shows the cheapest Azure regions with acceptable eviction rates for the VM size. On a test or gallery VM, which is not a real Marketplace deployment, this feature shows nothing. That is by design.

Update notifications

Optional. Registering sends only the email address to Caleta, rate limited, with no other data. See the privacy policy for what is stored. Unsubscribe via support@caleta.io.

Offline and sovereign operation

Once a model is downloaded, the appliance serves fully offline. Block all outbound traffic and it keeps working after a reboot. The only outbound calls are model downloads from Hugging Face, Let's Encrypt when you expose, and the disclosed Caleta update and attestation channel. All three are set out in the privacy policy.

Section 9

Built-in tools

Baked into the image, nothing to install.

llama.cpp
The inference engine, CPU and CUDA builds, pinned versions.
Open WebUI
The private chat UI (unbranded), loopback only.
Hugging Face CLI (hf)
Fast model downloads (Xet transfer).
Caddy
Automatic HTTPS and Let’s Encrypt reverse proxy (off until you expose).
btop and nvtop
System and GPU monitors.
ufw
Host firewall.
gum
The interactive menu UI (the menu falls back to plain prompts if absent).
btop resource monitor on the appliance
btop system monitor, built in.
nvtop GPU monitor on the appliance
nvtop GPU monitor, built in (GPU sizes).

Section 10

Supported and recommended VM sizes

The appliance runs on many sizes, but we quote only configurations we have measured. These are the recommended sizes.

CPU tier (measured, recommended)

SizevCPU / physical coresRAMWhat fits
Standard_E16ads_v716 / 8128 GBEntry. Models up to ~100 GB (Qwen3-Coder-30B, gpt-oss-20b). 145 GB DeepSeek-V4-Flash does not fit.
Standard_E32ads_v732 / 16256 GBDeepSeek-V4-Flash Q4 fits. ~35 tokens/sec on Qwen3-Coder.
Standard_E64ads_v764 / 32512 GBFlagship CPU box. Frontier mixture-of-experts models at usable speed.

CPU serving is beta. It genuinely works at single-user reading speed and is a striking capability, but GPU sizes are the recommended production experience.

GPU tier (tested on this image)

SizeGPUsTested modelMeasured (single-stream)
Standard_NC24ads_A100_v41x A100 80GBgpt-oss-120b~140 tokens/sec
Standard_NC40ads_H100_v51x H100 NVL(Trusted Launch verified)GPU tier
Standard_NC80adis_H100_v52x H100 NVLQwen3-235B 4-bit~64 tokens/sec
Standard_NC96ads_A100_v44x A100 80GBGLM-5.2 IQ2_M~27 tokens/sec

The image supports Trusted Launch (Secure Boot and vTPM), NVMe disk, accelerated networking and Microsoft Entra login.

These are the x86 sizes. For Azure Cobalt (ARM64) sizes, see section 12.

Section 11

Model catalogue

"Caleta Score" is our in-house infrastructure-as-code benchmark, where higher is better. CPU speeds are the beta lane, GPU is recommended. This is the x86 catalogue. Azure Cobalt (ARM64) has its own curated list in section 12.

ModelTypeDownloadCaleta ScoreMeasured GPUMeasured CPU (beta)
Qwen3.6-27B (Q4)dense 27B17 GB90NC80 2xH100: 67 t/s-
Qwen3-Coder-30B (Q4), the trialMoE, 3B active18 GB80NC80: 240 t/sE32: 35 t/s
Qwen3-Coder-Next-80B (Q4)MoE, 3B active46 GB78NC80: 143 t/s-
Devstral-Small-2 (Q4)dense 24B14 GB84NC80: 90 t/s-
Qwen2.5-Coder-32B (Q4)dense 32B19 GB86NC80: 66 t/s-
Phi-4 (Q4)dense 14B9 GB74NC80: 137 t/s-
gpt-oss-20b (MXFP4)MoE, 4B active12 GB60NC80: 307 t/s-
Gemma-4-26B-A4B (Q4)MoE, 4B active15 GB82NC80: 195 t/s-
Qwen3.6-35B-A3B (Q4)MoE, 3B active21 GB76NC80: 179 t/s-
GLM-4.7-Flash (Q4)MoE, 3B active18 GB56NC80: 156 t/s-
gpt-oss-120b (MXFP4)MoE, 5B active61 GBunscoredNC24 1xA100: 140 t/sE64: 48 t/s
Qwen3-235B (Q4)MoE, 22B active126 GB86NC80: 75 t/sE64: 13 t/s
DeepSeek-V4-Flash (Q4)MoE, 13B active145 GB90NC80: 24 t/sE64: 9.7 t/s
GLM-5.2 IQ2_MMoE, 40B active239 GB90NC96 4xA100: 28 t/sE64: 7 t/s
GLM-5.2 IQ3_SMoE, 40B active288 GB98NC96: 26 t/s-
Kimi-K2.7-Code (Q2)MoE, 32B active339 GBunscored-E64: 9.4 t/s
DeepSeek-R1-0528 (Q2)MoE, 37B active234 GBunscored-E64: 9 t/s

Licences are permissive (Apache 2.0, MIT or equivalent) except Kimi, which is modified MIT: products over roughly 100 million monthly active users must display the model name. That is not a constraint for appliance use.

Section 12

ARM / Azure Cobalt

The appliance also ships as an ARM64 build for Azure Cobalt 100. It is a separate plan in the same Marketplace offer, and you choose x86 or Azure Cobalt at deploy.

What is the same

The llm menu, the download and serve flow, the chat UI, the SSH tunnel, expose with Let's Encrypt, the firewall, the per-boot API key and Entra login are all identical to x86. They are shared code. Trusted Launch (Secure Boot and vTPM), NVMe disk, accelerated networking and Entra login are supported on ARM exactly as on x86, verified on the shipping ARM image.

The menu looks the same on ARM. It simply reports gpu: none, because Cobalt is CPU-only.

What is different

  • CPU-only. Cobalt is an ARM CPU platform, so there is no GPU tier on ARM. The engine is llama.cpp. There is no vLLM path, and none is offered on ARM. All ARM figures are CPU inference.
  • Separate, curated model list. ARM has its own measured catalogue, because not every model is built and verified for ARM. It is a focused list of models proven on Cobalt.
  • Spot placement. The spot-placement advisory does not yet cover Cobalt sizes, so on an ARM box that menu item may show nothing for now. It is on the roadmap.

CPU serving is beta on both architectures. It genuinely works at single-user reading speed, and the x86 GPU sizes remain the recommended production experience.

Supported ARM sizes

Azure Cobalt Epsv6.

SizeNotes
Standard_E8ps_v6Fits the small models (gpt-oss-20b, ERNIE-4.5-21B-A3B).
Standard_E16ps_v6Minimum for GLM-4.5-Air.
Standard_E32ps_v6The default, about USD 0.31/hour on spot. Fits most of the list.
Standard_E64ps_v6About USD 0.63/hour on spot. For the largest models, including GLM-5.2 (744B).

ARM model catalogue

Measured on Azure Cobalt Epsv6 with llama.cpp on CPU. Measured on Standard_E32ps_v6 unless noted. CPU inference, single-user speeds.

ModelTypeDownloadMeasured (Cobalt CPU)Notes
gpt-oss-120bMoE reasoner, 5B active63 GB~53 t/s120B brain at 5B-active speed; value pick
Qwen3-Coder-30B-A3BMoE coder, 3B active19 GB~56-64 t/sfast mature coder; safe default
Qwen3.6-35B-A3BMoE coder, 3B active22 GB~34 t/sbest coder quality (SWE-bench 73.4)
Qwen3-30B-A3B-InstructMoE general, 3B active19 GB~63 t/sfast general daily-driver
gpt-oss-20bMoE reasoner, 4B active12 GB~75 t/sfastest strong reasoner; fits E8ps_v6
ERNIE-4.5-21B-A3BMoE general, 3B active13 GB~67 t/stiny, Apache-2.0; fits E8ps_v6
Qwen3-Next-80B-A3BMoE general, 3B active49 GB~28 t/ssmart, not a speed pick
GLM-4.5-AirMoE mid, 12B active73 GB~25 t/sbest mid-tier, MIT; needs E16ps_v6 or larger
Qwen3-235B-A22BMoE big, 22B active142 GB~15 t/sbig brain, RAM-heavy
GLM-5.2 (744B)MoE frontier, 40B active467 GB~9.5-11.7 t/sfrontier flagship; E64ps_v6 only

Licences are Apache 2.0, MIT or equivalent permissive, the same as x86. Models download from Hugging Face under each model's own licence.

Related pages