Artificial Intelligence (AI) Azure

Agent Zero on Ubuntu 24.04 on Azure User Guide

| Product: Agent Zero on Ubuntu 24.04 LTS on Azure

Overview

This guide covers the deployment and operation of Agent Zero on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. Agent Zero is an open source, general purpose AI agent framework. You give it a task in a chat and it works on it step by step: it plans, writes and runs code in a terminal, reads and edits files, searches and browses the web, remembers what worked, and can hand parts of a task to subordinate agents. You watch every step live in the web interface.

Upstream ships Agent Zero as a Docker image. This VM runs that official image, pinned by its digest, as a systemd managed container:

  • Agent Zero runs in its own Linux container (the upstream image is based on Kali Linux and carries Python, Node.js, a browser runtime, LibreOffice and a desktop for the agent to use). The container is the agent's workspace
  • Ollama runs on the VM itself and serves a small open weight model on the CPU, so the agent works before you add any API key
  • nginx is the only public listener: HTTPS on 443 with a certificate generated for this VM, and 80 only redirects
Component Version Licence
Agent Zero (image agent0ai/agent-zero, linux/amd64) v2.14, commit ff2a5b9c, digest sha256:307a2cef... MIT
Ollama (CPU only) 0.40.5 MIT
Model granite4:tiny-h (IBM Granite 4.0 Tiny) registry manifest sha256:1da8cadb... Apache 2.0
Docker engine Ubuntu docker.io 29.1.3 Apache 2.0

Set your expectations for the bundled model. granite4:tiny-h is a small model running on the CPU. It can complete simple, well specified tasks, such as running a command, writing and running a short script, or reading a file, and it takes two to five minutes for a task like that on a 4 vCPU VM. It is much weaker than a large hosted model: it sometimes misreads a task, repeats itself or gives up, and multi step work is slow and unreliable. For real work, add a key for a hosted model provider (Step 10). The bundled model is there so the appliance works privately the moment it boots.

No password, key or certificate is baked into the image. Upstream Agent Zero runs with no login unless you configure one. On this image, first boot generates a login, a runtime identity and the API key derived from them, and a TLS certificate, all unique to the VM. Nothing serves until that is done, and first boot proves that an anonymous visitor is sent to the login page, that a wrong password and a missing or wrong API key are refused, and that the agent's container cannot reach the Azure metadata service, before it marks the VM ready. Backed by 24/7 cloudimg support.

Agent Zero is developed by Agent Zero, s.r.o. and contributors and is licensed under the MIT licence. Ollama is developed by Ollama, Inc. under the MIT licence, and Granite is a model family from IBM. Kali Linux is a trademark of OffSec Services Limited. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by Agent Zero, s.r.o., Ollama, IBM or OffSec. The names are used here solely to identify the software and model this image contains.

The appliance units active, SSH and nginx as the only routable listeners, the Agent Zero container publishing only its web port on loopback, and the running version

What is included:

  • Agent Zero v2.14 from the official image, pinned by digest and already pulled, so first boot fetches no code
  • A private Docker network (a0net, 172.30.254.0/24) for the container. Only the web port is published, and only to 127.0.0.1:50080; the container's SSH server, tunnel API and 9000 to 9009 ports are not published
  • Ollama 0.40.5 (CPU backends only) running as a dedicated ollama account, bound to the Docker network's gateway 172.30.254.1:11434, with granite4:tiny-h already pulled
  • An nftables egress guard that stops the agent's container, and the Ollama account, from reaching the Azure Instance Metadata Service and the Azure WireServer
  • nginx terminating HTTPS on 443 with a per-VM certificate, redirecting 80 to HTTPS, rate limiting the login
  • Unattended security upgrades left on for the operating system

Prerequisites

  • An Azure subscription with permission to create VMs
  • A Standard_D4s_v3 (4 vCPU, 16 GiB) or larger non burstable size is recommended for the bundled model. The model uses about 5 GiB of memory and all four CPUs while it works, and the Agent Zero container uses another 1 to 2 GiB. B series sizes such as Standard_B4ms run it at full speed only while they have CPU credits: measured on this image, a B4ms exhausted its credits after about an hour of agent work and a single step then took more than twenty minutes. With a hosted model (Step 10) the VM only runs the container, and Standard_B2ms (2 vCPU, 8 GiB) is enough
  • Inbound TCP 443 (and 80 for the redirect) open only to the networks you connect from, plus 22 for SSH administration
  • An SSH key pair for administrative access

Step 1: Deploy from the Azure Portal

  1. In the Azure Portal, search the Marketplace for Agent Zero by cloudimg and select Create.
  2. Choose your subscription, resource group and region.
  3. Set the VM size to Standard_D4s_v3 or larger.
  4. Under Administrator account, select SSH public key and supply your own key.
  5. Under Inbound port rules, allow HTTPS (443), HTTP (80) and SSH (22), then restrict all three to your own address ranges on the networking blade.
  6. Review and create.

Step 2: Deploy from the Azure CLI

az vm image terms accept --urn cloudimg1647283583153:agent-zero-ubuntu-24-04:default:latest

az vm create \
  --resource-group my-resource-group \
  --name my-agent-zero \
  --image cloudimg1647283583153:agent-zero-ubuntu-24-04:default:latest \
  --size Standard_D4s_v3 \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/id_rsa.pub \
  --public-ip-sku Standard

# Open HTTPS (and the HTTP redirect) to YOUR network only.
MY_CIDR="203.0.113.0/24"   # replace with your own range
az network nsg rule create \
  --resource-group my-resource-group \
  --nsg-name my-agent-zeroNSG \
  --name allow-https-from-office --priority 1010 \
  --source-address-prefixes "${MY_CIDR}" \
  --destination-port-ranges 80 443 --protocol Tcp --access Allow

Step 3: Connect to your VM

ssh azureuser@<vm-ip>

Step 4: Confirm the services are running

First boot takes about a minute. It generates this VM's login, API key and certificate, starts the container and proves the gates before it finishes.

systemctl is-active agent-zero-egress-guard docker ollama agent-zero nginx
systemctl show -p Result --value agent-zero-firstboot

# SSH and nginx are the only routable listeners. Ollama is bound to the private Docker bridge.
sudo ss -lntH | awk '{print $4}' | grep -v -E '^(127\.|\[::1\])' | sort

# The container publishes only its web port, and only to loopback.
sudo docker port agent-zero

Expected output:

active
active
active
active
active
success
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
172.30.254.1:11434
[::]:22
[::]:443
[::]:80
80/tcp -> 127.0.0.1:50080

Step 5: Read this VM's credentials

Everything in this file was generated on this VM at first boot.

sudo cat /root/agent-zero-credentials.txt
# Agent Zero - per-VM credentials generated on first boot (...). Keep this file private.
# The web login and the API key below are this VM's own; change the password in Settings > Authentication
# (the API key changes with it - see Settings > External services).
AGENT_ZERO_URL=https://<vm-ip>/
AGENT_ZERO_USERNAME=a0admin
AGENT_ZERO_PASSWORD=<A0_PASSWORD>
AGENT_ZERO_API_KEY=<A0_API_KEY>

The login name is a0admin rather than admin on purpose: Agent Zero hides its login name wherever it appears in the agent's output, so a common word would be masked in ordinary text.

The credentials file, readable only by root, with the generated password and API key masked

Step 6: Sign in and give the agent a task

  1. Open AGENT_ZERO_URL in your browser. The certificate is self-signed and generated for this VM, so your browser warns you the first time. Accept it for this address, or follow Step 13 to use your own domain and a trusted certificate.
  2. Sign in as a0admin with AGENT_ZERO_PASSWORD.

The Agent Zero login page served over HTTPS by the appliance, with the a0admin login entered and the password masked

  1. Type a task into the box under How can I help you today? and press Enter. Give the bundled model short, concrete instructions. Asking it to run a specific command works best, for example:
Run this shell command in the terminal and tell me exactly what it printed: python3 -c "print([n for n in range(2, 30) if all(n % d for d in range(2, n))])" | tee primes.txt

A large hosted model (Step 10) can take open ended tasks such as write a script that ..., test it and fix any errors; the bundled model often answers those without running anything.

The chat shows each step as the agent takes it: the code it writes, the command it runs, the output, and its final answer. With the bundled model expect a few minutes for a task like this.

A chat in which the agent, using the bundled CPU model, ran the command in its terminal and reported the prime numbers it printed

Files the agent creates are in its work directory, /a0/usr/workdir inside the container, which is /var/lib/agent-zero/usr/workdir on the VM. Open Files on the home screen to browse them.

The Agent Zero file browser showing primes.txt, which the command wrote in the agent's work directory

Step 7: Understand the code execution model

Agent Zero runs code. That is what it is for, and you should treat it accordingly:

  • Every command and script the agent runs executes as root inside the Agent Zero container. The container is not privileged, has no access to the Docker socket and runs with no-new-privileges, but within it the agent can install packages, write files and run any program.
  • The agent can read and change everything under /a0, including its own settings and /a0/usr/.env, which holds this VM's login password and any provider API keys you add. Anyone who can give the agent instructions, including text on a web page or in a file it reads (prompt injection), can try to make it reveal or misuse them. Only give it tasks and data you trust, and use provider keys with spending limits.
  • The container has full outbound internet access, which the agent needs to browse, download and install. It cannot reach the Azure Instance Metadata Service (169.254.169.254, which hands out managed identity tokens) or the Azure WireServer (168.63.129.16): an nftables guard drops that traffic for every Docker network and for the Ollama account, and Docker will not start without the guard loaded.
  • The container can reach the VM itself through the Docker network gateway (172.30.254.1): Ollama there, and the VM's own SSH and nginx ports.
  • Your chats, the agent's memory, uploaded files and the work directory persist in /var/lib/agent-zero/usr on the VM. Packages the agent installs live in the container and survive restarts, but not a re-creation of the container (Step 14).

You can check the egress guard and the gates from the VM:

M='http://169.254.169.254/metadata/instance?api-version=2021-02-01'
printf 'VM itself            -> IMDS %s\n' "$(curl -s -o /dev/null -w '%{http_code}' -m 5 -H Metadata:true "$M")"
printf 'agent-zero container -> IMDS %s\n' "$(sudo docker exec agent-zero curl -s -o /dev/null -w '%{http_code}' -m 5 -H Metadata:true "$M")"
printf 'agent-zero container -> WireServer %s\n' "$(sudo docker exec agent-zero curl -s -o /dev/null -w '%{http_code}' -m 5 'http://168.63.129.16/?comp=versions')"
printf 'agent-zero container -> pypi.org %s\n' "$(sudo docker exec agent-zero curl -s -o /dev/null -w '%{http_code}' -m 20 https://pypi.org/simple/pip/)"
printf 'anonymous /          -> %s\n' "$(curl -sk -o /dev/null -w '%{http_code} %{redirect_url}' https://127.0.0.1/)"
printf 'API without a key    -> %s\n' "$(curl -sk -o /dev/null -w '%{http_code}' -X POST -H 'Content-Type: application/json' -d '{"message":"hi"}' https://127.0.0.1/api/api_message)"

Expected output (000 means the connection was dropped):

VM itself            -> IMDS 200
agent-zero container -> IMDS 000
agent-zero container -> WireServer 000
agent-zero container -> pypi.org 200
anonymous /          -> 302 https://127.0.0.1/login?next=/
API without a key    -> 401

The login, API key and RFC gates, and the egress guard: the VM reaches IMDS while the container and the Ollama account are blocked from IMDS and the WireServer

Step 8: Understand the access model

  • Web interface. Every page and API route needs this VM's login, except the login page and a health check. Agent Zero reads the login from AUTH_LOGIN and AUTH_PASSWORD in /var/lib/agent-zero/usr/.env (mounted at /a0/usr/.env); the container will not start if either is empty, so it never runs without a login. The session cookie leaves nginx marked Secure, HttpOnly and SameSite=Lax, and nginx allows ten login attempts a minute per client.
  • External API, MCP and A2A. Agent Zero's external API (/api/api_message and related routes), its MCP server (/mcp) and its A2A server (/a2a) authenticate with one API key, which Agent Zero derives from its runtime identity, login and password. That key is AGENT_ZERO_API_KEY. The MCP and A2A servers are off until you enable them in Settings.
  • Network. nginx is the only public listener. The container's port is published to 127.0.0.1 only, and Ollama listens only on the Docker network gateway. Development only routes are closed: nginx refuses /api/rfc (remote function calls from a development copy of Agent Zero).

Step 9: The bundled model

Agent Zero's Default model preset on this VM uses Ollama on the VM for both its main and utility model, and a local sentence transformer (all-MiniLM-L6-v2, baked into the image) for memory embeddings, so nothing leaves the VM unless the agent itself browses. The default agent profile is upstream's Tiny Local profile, which gives small local models a simpler tool calling contract. Each reply from the bundled model is capped at 2,048 tokens (max_tokens in the preset), because a small model can fall into a repetition loop that would otherwise occupy the CPU for many minutes; raise or remove the cap if you switch to a larger model.

ollama --version
ollama list
ollama show granite4:tiny-h --license | grep -m1 'Apache'
ollama version is 0.40.5
NAME               ID              SIZE      MODIFIED
granite4:tiny-h    566b725534ea    4.2 GB    ...
                                 Apache License

This image adds one small configuration plugin, cloudimg_ollama_chat in /a0/usr/plugins. Upstream's built in Ollama provider sends requests to Ollama's /api/generate endpoint with a generic prompt format; with that, the bundled model ignored tasks and replied with a greeting. The plugin points the same Ollama provider at Ollama's /api/chat endpoint, where each model's own chat template is applied. It contains no code and needs no key.

The bundled model, its Apache 2.0 licence and the digest pinned Agent Zero image

You can also call the agent through its external API. With the bundled model each call takes a few minutes, so run it from your own shell:

KEY=$(sudo sed -n 's/^AGENT_ZERO_API_KEY=//p' /root/agent-zero-credentials.txt)
# The key is read from standard input (-H @-), so it never appears on a command line.
printf 'X-API-KEY: %s\n' "$KEY" | curl -sk -m 1500 -X POST -H @- -H 'Content-Type: application/json' \
  -d '{"message":"Run this shell command in the terminal and reply with only its output: date -u"}' \
  https://127.0.0.1/api/api_message

A real reply from the bundled model on this image (shortened):

{"context_id": "ZCrk0mco", "response": "`Fri Oct 16 02:07:13 UTC 2026`\n\n(venv) root@agent-zero:/a0/usr/workdir#\n\n# Current system date and time of user\n ..."}

The agent really ran date -u in its terminal (the chat in the web interface shows the command and its output), but the small model then pasted part of its own context after the answer. In another run it ran the command and then quoted a different time zone, and for uname -m it once answered aarch64 on this x86_64 VM without running anything. Always check the tool output in the chat rather than trusting the bundled model's summary; a hosted model (Step 10) answers these cleanly.

Step 10: Use a hosted model with your API key

For real work, use a large hosted model:

  1. Open Settings (the gear in the top left) and go to Agent Settings > Models to edit the presets, or choose a preset from the menu in a chat's status bar.
  2. Pick a provider, for example OpenAI, Anthropic, Google, OpenRouter or Azure OpenAI, enter your API key for it, and choose the main and utility models. Keep HuggingFace all-MiniLM-L6-v2 as the embedding model unless you have a reason to change it.
  3. For a capable model, switch the agent profile from Tiny Local back to upstream's default Agent 0 profile under Agent Settings > Agent Config.

The model configuration: the Default preset using Ollama and the bundled model, with local embeddings

With a hosted model, your prompts, the agent's working context and anything it reads go to that provider under the provider's terms. You can then stop Ollama to free its memory: sudo systemctl disable --now ollama.

Step 11: Add a larger local model

Ollama can run any model from its library that fits in the VM's memory. Pull it and select it in the preset editor (the list is read from Ollama):

ollama pull qwen3:8b        # about 5 GB; needs 16 GiB of memory or more, and is slower on CPU
ollama list

Check each model's licence before using it commercially (ollama show <model> --license), prefer models that support tool calling, and expect larger models to be slower on the CPU. This image ships Ollama's CPU backends only; for GPU inference use a GPU VM size with a GPU build of Ollama.

Step 12: MCP and A2A

Agent Zero can act as an MCP server (so tools such as Claude Desktop or Cursor can delegate tasks to it) and as an A2A agent. Both are off by default. Enable them under Settings > MCP/A2A (A0 MCP Server and A0 A2A Server), which also shows the connection URLs. Every URL contains or requires AGENT_ZERO_API_KEY; without it the endpoints refuse the connection. Use your domain name (Step 13) in the URLs you give to other tools, and only enable these if those tools can reach the VM over HTTPS.

Step 13: Use your own domain and certificate

The self-signed certificate is fine for a trial. For regular use, point a DNS name at the VM's public IP and install a trusted certificate. nginx already serves the Let's Encrypt HTTP-01 challenge path on port 80.

sudo apt-get update && sudo apt-get install -y certbot
sudo certbot certonly --webroot -w /var/www/html -d agent.example.com
sudo sed -i \
  -e 's#/etc/agent-zero/tls/server.crt#/etc/letsencrypt/live/agent.example.com/fullchain.pem#' \
  -e 's#/etc/agent-zero/tls/server.key#/etc/letsencrypt/live/agent.example.com/privkey.pem#' \
  /etc/nginx/sites-available/agent-zero
sudo nginx -t && sudo systemctl reload nginx

Add --deploy-hook "systemctl reload nginx" to the certonly command so nginx picks up each renewal.

Step 14: Day-2 operations

# Restart Agent Zero or Ollama.
sudo systemctl restart agent-zero
sudo systemctl restart ollama

# Recent logs.
sudo journalctl -u agent-zero -n 20 --no-pager

# What is installed.
cat /usr/share/agent-zero/VERSIONS
  • Data. Everything Agent Zero stores is in /var/lib/agent-zero/usr (mounted at /a0/usr): settings and keys (.env), chats, memory, knowledge, uploads, projects and the work directory. Back it up, together with /var/lib/ollama/models if you pulled extra models, or take Azure disk snapshots.
  • Changing the password. Change it in Settings > External Services > Authentication. The API key is derived from the password, so it changes too; read the new one under Settings > External Services > External API and update the credentials file if you keep it.
  • Remote Control (tunnels). Settings > External Services > Remote Control can publish Agent Zero through a public tunnel service. The login still applies, but the tunnel bypasses your network security group, so leave it off unless you need it.
  • Upgrading Agent Zero. The image is pinned in /etc/agent-zero/agent-zero.env as A0_IMAGE. To upgrade, take a snapshot, pull a newer official image by digest, set A0_IMAGE to it and run sudo systemctl restart agent-zero; the container is re-created and your data in /var/lib/agent-zero/usr is kept. Packages the agent installed inside the old container are not carried over. Agent Zero's own in app updater works too, but changes the code inside the running container only.
  • Operating system updates. Unattended security upgrades are on, including the Docker engine from the Ubuntu archive. Reboot when /var/run/reboot-required exists; everything starts again on boot.
  • Docker network. The container network uses 172.30.254.0/24. If that overlaps an address range your VM must reach, contact cloudimg support before changing it, because Ollama and the egress guard are bound to it.

Troubleshooting

  • The browser shows a certificate warning. Expected with the per-VM self-signed certificate; see Step 13.
  • The page does not load at all. Check that the VM's network security group allows 443 from your address, and that systemctl is-active agent-zero nginx reports active.
  • "Too many requests" when signing in. nginx allows ten login attempts a minute per client. Wait a minute and sign in with the password from the credentials file.
  • The agent is very slow. Expected with the bundled model on the CPU. On a B series size, check the VM's CPU Credits Remaining metric in the Azure Portal; once credits run out the CPU is throttled and each step can take many minutes. Use a D series size, or a hosted model (Step 10).
  • The agent stops with "consecutive unusable model responses", repeats itself or answers something else. The small bundled model lost track of the task. Start a new chat and phrase the task more simply, or use a hosted model.
  • The agent cannot reach a site. Outbound internet is open except for the Azure platform addresses in Step 7. Check name resolution from inside the container: sudo docker exec agent-zero curl -sI https://example.com.

Support

cloudimg provides 24/7 support for this image by email at support@cloudimg.co.uk, covering deployment, VM sizing, model and provider choice, the code execution and network model, MCP and A2A, TLS and upgrades.