MaxKB on Ubuntu 24.04 on Azure User Guide
Overview
This guide covers the deployment and operation of MaxKB on Ubuntu 24.04 on Azure using cloudimg Azure Marketplace images. MaxKB is an open source, GPL-3.0 licensed platform for building question answering agents on top of your own content (retrieval augmented generation, or RAG). You upload documents or crawl a website into a knowledge base, connect a language model, and publish an agent that answers questions from that content and shows which passages it used.
The image runs the whole stack on one VM:
- MaxKB Community Edition is the web application and API: knowledge bases, agents, tools, chat logs and user management. It runs in a single container together with its PostgreSQL database (with the
pgvectorextension for vector search) and Redis - A built-in embedding model turns your documents into vectors on the VM, so knowledge bases work without any external service
- Ollama serves a small local language model, Qwen2.5 1.5B Instruct, on the VM's CPU. It is already added to MaxKB, so you can build and test an agent the moment you sign in, with no API key
- nginx terminates HTTPS with a certificate generated for this VM
| Component | Version | Licence |
|---|---|---|
| MaxKB Community Edition | 2.10.7 LTS (v2.10.7-lts) |
GPL-3.0 |
| PostgreSQL + pgvector (inside the MaxKB container) | 17.11 + 0.8.6 | PostgreSQL Licence |
| Redis (inside the MaxKB container) | 8.0.6 | AGPL-3.0 |
| Ollama (CPU) | 0.40.5 | MIT |
Qwen2.5 1.5B Instruct (qwen2.5:1.5b, Q4_K_M) |
Ollama manifest f2b0a490 |
Apache 2.0 |
Embedding model text2vec-base-chinese (bundled in MaxKB) |
as shipped in 2.10.7 LTS | Apache 2.0 |
| Docker Engine | 29.9.1 | Apache 2.0 |
About the local model. Qwen2.5 1.5B is small enough to run on a CPU, which is what makes this image work out of the box, but it is a small model. On a Standard_B4ms it answers a typical knowledge base question in roughly 5 to 30 seconds (the first question after a quiet period also loads the model, which adds a few seconds), it handles one question at a time, and it can misread negations or questions that need several steps of reasoning. It is a good way to build, test and demonstrate agents. For production traffic, connect a hosted model such as Azure OpenAI or OpenAI in Model (Step 9); MaxKB lets you choose the model per agent.
This is the Community Edition. It is limited by MaxKB itself to 50 knowledge bases, 5 agents and 2 user accounts (the admin plus one more). The Upgrade button in the MaxKB header points to FIT2CLOUD's commercial Professional Edition, which needs a licence from FIT2CLOUD and is not included in this image; every feature this guide describes is part of the open source Community Edition.
No password, key or certificate is baked into the image. First boot generates, for this VM only, the admin password, the PostgreSQL and Redis passwords, MaxKB's secret keys and the TLS certificate, and MaxKB creates its token signing keys inside the fresh database. Nothing answers on the network until first boot has done this, and first boot proves that MaxKB's published default password is refused before it marks the VM ready. Backed by 24/7 cloudimg support.
MaxKB is developed by FIT2CLOUD and the 1Panel community under the GPL-3.0. Ollama is developed by Ollama Inc. under the MIT licence, and Qwen2.5 by the Qwen team at Alibaba Cloud under the Apache 2.0 licence. This image is produced by cloudimg and is not affiliated with, endorsed by, or sponsored by FIT2CLOUD, the MaxKB or 1Panel projects, Ollama or Alibaba Cloud. The names are used here solely to identify the software this image contains.

What is included:
- MaxKB 2.10.7 LTS Community Edition from the official image, pinned by its SHA-256 digest, published on
127.0.0.1:8080only - PostgreSQL and Redis inside the MaxKB container, listening on the container's own loopback and protected by per-VM passwords
- Ollama with Qwen2.5 1.5B Instruct already downloaded and registered in MaxKB as Qwen2.5 1.5B (local, CPU). Ollama has no login of its own, so a firewall rule lets only the VM itself and the MaxKB container reach it
- nginx terminating HTTPS on
443with a per-VM certificate, redirecting80to HTTPS, and limiting sign in attempts - Every container blocked from reaching the Azure Instance Metadata Service and the Azure wire server, so a crafted link in a web knowledge base, tool or MCP setting cannot read the VM's identity or tokens
- Unattended security upgrades left on for the operating system
Prerequisites
- An Azure subscription with permission to create VMs
- A
Standard_B4msVM (4 vCPU, 16 GiB) or larger is recommended. MaxKB with its database uses about 2 GiB and the local model about 1.5 GiB while loaded; the extra CPU makes local answers and document processing faster - Inbound TCP
443(and80for the redirect) open only to the networks you connect from, plus22for SSH administration - An SSH key pair for administrative access
- Optional: an API key or endpoint for a hosted model (Azure OpenAI, OpenAI, Anthropic, Google Gemini, DeepSeek and others) if you want faster, stronger answers than the local model
Step 1: Deploy from the Azure Portal
- In the Azure Portal, search the Marketplace for MaxKB and select the cloudimg offer, then Create.
- Choose your subscription, resource group and region.
- Set the VM size to Standard_B4ms or larger.
- Under Administrator account, select SSH public key and supply your own key.
- 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.
- Review and create.
Step 2: Deploy from the Azure CLI
az vm image terms accept --urn cloudimg1647283583153:maxkb-ubuntu-24-04:default:latest
az vm create \
--resource-group my-resource-group \
--name my-maxkb \
--image cloudimg1647283583153:maxkb-ubuntu-24-04:default:latest \
--size Standard_B4ms \
--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-maxkbNSG \
--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 two minutes: it generates this VM's secrets and certificate, initialises MaxKB's database, sets the admin password, registers the local model and proves the login gate before it finishes.
# Every unit reports "active".
systemctl is-active docker maxkb-egress-guard ollama maxkb nginx
test -f /var/lib/cloudimg/maxkb-firstboot.done && echo "first boot complete"
# Only SSH (22) and nginx (80, 443) listen publicly. MaxKB listens on 127.0.0.1:8080,
# Ollama on the private Docker bridge address 172.30.250.1:11434, and port 53 is the
# operating system's local DNS resolver.
sudo ss -lntH | awk '{print $4}' | sort -u
Expected output:
active
active
active
active
active
first boot complete
0.0.0.0:22
0.0.0.0:443
0.0.0.0:80
127.0.0.1:8080
127.0.0.53%lo:53
127.0.0.54:53
172.30.250.1:11434
[::]:22
[::]:443
[::]:80
While the local model is loaded you will also see one extra listener on a random high port of 127.0.0.1: that is Ollama's model runner, and it is loopback only. MaxKB itself runs as a single container:
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
NAMES STATUS PORTS
maxkb Up 14 minutes 5432/tcp, 6379/tcp, 127.0.0.1:8080->8080/tcp
5432/tcp and 6379/tcp are PostgreSQL and Redis inside the container. They are not published to the VM or the network.
Step 5: Read this VM's admin login
Everything in this file was generated on this VM at first boot.
sudo cat /root/maxkb-credentials.txt
# MaxKB - per-VM credentials generated on first boot (...). Keep this file private.
# Sign in at MAXKB_URL as admin, then change the password under the avatar menu > Change Password.
MAXKB_URL=https://<vm-ip>/admin/
MAXKB_USERNAME=admin
MAXKB_PASSWORD=<MAXKB_PASSWORD>
# Pre-filled as the initial password when you create the second user (Community Edition: 2 users).
MAXKB_NEW_USER_DEFAULT_PASSWORD=<generated on this VM>
# Bundled local model (CPU): already added in MaxKB as "Qwen2.5 1.5B (local, CPU)".
OLLAMA_URL=http://172.30.250.1:11434
Step 6: Sign in
- Open
MAXKB_URLfrom the credentials file in your browser. The certificate is self-signed and generated for this VM, so your browser will warn you the first time. Accept it for this address, or follow Step 13 to use your own domain and a trusted certificate. - Sign in as
adminwithMAXKB_PASSWORD. - Change the password: select the avatar in the top right corner, then Change Password. MaxKB requires 6 to 20 characters with an upper case letter, a lower case letter, a digit and a special character.
After one failed sign in MaxKB asks for a CAPTCHA, and after five failures it locks the account for ten minutes. nginx also limits each client address to 10 sign in attempts a minute.
Step 7: Check the models
Open Model in the top menu. Two models are ready:
- Qwen2.5 1.5B (local, CPU): the local language model served by Ollama on this VM
- maxkb-embedding: MaxKB's built-in embedding model, which knowledge bases use to index your documents

You can talk to the local model directly from the VM. Ollama listens only on the private Docker bridge address:
curl -s http://172.30.250.1:11434/api/tags | jq -r '.models[] | "\(.name) \(.details.parameter_size) \(.details.quantization_level)"'
curl -s http://172.30.250.1:11434/api/chat \
-d '{"model":"qwen2.5:1.5b","stream":false,"messages":[{"role":"user","content":"In one sentence, what is a knowledge base?"}]}' \
| jq -r .message.content
Example output (the second line is generated, so your wording will differ):
qwen2.5:1.5b 1.5B Q4_K_M
A knowledge base is a repository of information and knowledge, typically used as a source of data and insights for decision-making.
Step 8: Build a knowledge base
- Open Knowledge, select Create, then General Knowledge.
- Give it a name and keep maxkb-embedding as the embedding model.
- Select Upload Document and add your files. MaxKB accepts PDF, Word, Markdown, text, HTML, Excel and CSV files, among others. It splits each document into segments and indexes them; the File Status column shows Successful when a document is ready.

- Web Knowledge crawls a website instead of uploaded files, and can sync it again later.
- Retrieval Testing in the knowledge base's menu shows which segments a question would retrieve, so you can check your content before you build an agent. For English content, the hybrid search mode usually ranks best with the built-in embedding model, which was trained mainly on Chinese text. You can add another embedding model under Model if your knowledge base needs it.
- Indexing runs on the VM's CPU: a few seconds for a small document, minutes for a large PDF.
Step 9: Create an agent and ask it questions
- Open Agent, select Create, then Simple Agent. Give it a name and a description.
- Under AI Model, choose Qwen2.5 1.5B (local, CPU), or any hosted model you have added.
- Next to Related Knowledge, select + and add your knowledge base.
- Under Prologue, write the greeting your users will see, with suggested questions as list items.
- Select Save, then ask a question in Debug Preview on the right. The answer shows the Knowledge Source it used.

Connect a hosted model. For faster and stronger answers, open Model, select Add Model, choose the provider (Azure OpenAI, OpenAI, Anthropic, Google Gemini, DeepSeek, an OpenAI compatible service, or another Ollama server) and enter its endpoint and key. Then pick it under AI Model in your agent. Keys you enter are stored encrypted in MaxKB's database on this VM. Your provider bills you for the tokens your agents use.
Step 10: Publish the agent and share it
Select Publish in the agent's settings. The agent's Overview page then shows:
- Public URL: a chat page anyone with the link can use without signing in, with an On switch to turn it off. The link contains a random 16 character token, and Access Control holds the agent's further access settings
- Get Embed Code: a snippet that puts the chat on your own website
- API Access Credentials: the base URL and API Key for the agent's API (Step 12)

Public chat is per agent. A new VM has no agents, so it has no public chat page at all. An agent's public page only answers once you publish the agent, and you can switch it off at any time. Anyone with the link can ask questions, which uses your model, so share it as you would share an API key, and keep the VM's network rule limited to the people who should reach it.
Step 11: Understand the access model
- Sign in. The MaxKB admin console and every management API need a signed in user. The browser keeps a bearer token, and the one cookie MaxKB sets is marked
SecureandHttpOnlyby nginx. - Network. nginx is the only public listener: it serves MaxKB on port
443with a certificate generated for this VM, and port80only redirects to HTTPS. MaxKB listens on127.0.0.1:8080. PostgreSQL and Redis listen on the container's own loopback and require their per-VM passwords, so nothing else on the VM or the network can reach them. - Fail closed. MaxKB and Ollama start only after first boot has written this VM's secrets, and nginx only after first boot has set the admin password and proven MaxKB's published default is refused. If first boot fails, it removes both markers and stops everything, so nothing serves a half-configured VM. Before every start, MaxKB's unit also refuses to run if its secrets are missing, too short or an upstream default.
- Custom tools. Python tools you write in MaxKB run as a separate
sandboxuser that cannot reach PostgreSQL, Redis, Ollama, the Azure metadata service or the wire server. - Outbound requests. MaxKB fetches web pages for web knowledge bases and talks to the model providers you add. Every container is blocked from the Azure Instance Metadata Service (
169.254.169.254) and the Azure wire server (168.63.129.16), so a link that points at them cannot read this VM's identity or managed identity tokens. DNS keeps working, and private address ranges stay reachable on purpose, so you can use a model server elsewhere on your network.

You can check the sign in from the VM itself:
code() { curl -sk -o /dev/null -w '%{http_code}' "$@"; }
echo "anonymous API : $(code https://127.0.0.1/admin/api/user/profile)"
echo "anonymous agents : $(code https://127.0.0.1/admin/api/workspace/default/application)"
echo "plain HTTP : $(code http://127.0.0.1/admin/)"
PW=$(sudo sed -n 's/^MAXKB_PASSWORD=//p' /root/maxkb-credentials.txt)
printf '{"username":"admin","password":"%s"}' "$PW" \
| curl -sk -H 'Content-Type: application/json' --data @- https://127.0.0.1/admin/api/user/login \
| jq -r '"this VM'"'"'s login : code \(.code)"'
Expected output:
anonymous API : 401
anonymous agents : 401
plain HTTP : 301
this VM's login : code 200
And the outbound block, from inside the MaxKB container:
sudo docker exec maxkb python3 -c '
import urllib.request
for name, url in [("Azure IMDS ", "http://169.254.169.254/metadata/instance?api-version=2021-02-01"),
("Azure WireServer", "http://168.63.129.16/?comp=versions"),
("Internet ", "https://www.example.com/")]:
try:
print(name, urllib.request.urlopen(urllib.request.Request(url, headers={"Metadata": "true"}), timeout=6).status)
except Exception:
print(name, "blocked")
' </dev/null
Expected output:
Azure IMDS blocked
Azure WireServer blocked
Internet 200

The per-VM secrets live in /etc/maxkb/maxkb.env (readable by root only). Do not share or copy that file between VMs.

Step 12: Use the agent API
Each published agent has an OpenAI compatible chat endpoint. On the agent's Overview page, open API Key, create a key (it starts with agent-), and copy the Base URL. Then call it from any OpenAI compatible client, or with curl:
MAXKB_BASE="https://<vm-ip>/chat/api/<agent-id>"
API_KEY="<your-token>"
curl -sk "$MAXKB_BASE/chat/completions" \
-H "Authorization: Bearer $API_KEY" -H 'Content-Type: application/json' \
-d '{"messages":[{"role":"user","content":"What are your phone support hours?"}],"stream":false}' \
| jq -r '.choices[0].message.content'
Requests without a valid key are refused with 401. The full chat API reference is linked from the agent's Overview page under API Documents.
Step 13: Use your own domain and a trusted certificate
Point a DNS record at the VM's public IP, open port 80 to the internet for the certificate check, then replace the self-signed certificate with one from Let's Encrypt:
sudo apt-get install -y certbot
sudo certbot certonly --webroot -w /var/www/html -d maxkb.example.com
sudo sed -i \
-e 's#/etc/maxkb/tls/server.crt#/etc/letsencrypt/live/maxkb.example.com/fullchain.pem#' \
-e 's#/etc/maxkb/tls/server.key#/etc/letsencrypt/live/maxkb.example.com/privkey.pem#' \
/etc/nginx/sites-available/maxkb
sudo nginx -t && sudo systemctl reload nginx
Certbot renews the certificate automatically; add --deploy-hook "systemctl reload nginx" to the certonly command so nginx picks up each renewal.
Step 14: Day-2 operations
Logs and status.
sudo tail -n 20 /var/log/maxkb-firstboot.log
systemctl show maxkb -p ActiveState -p SubState -p ActiveEnterTimestamp
Restart. sudo systemctl restart maxkb restarts the MaxKB container (about a minute until the console answers again). sudo systemctl restart ollama restarts the local model server.
Back up. Everything MaxKB stores (database, uploaded files, logs) is under /var/lib/maxkb, and its secrets are in /etc/maxkb. Stop MaxKB for a consistent copy:
sudo systemctl stop maxkb
sudo tar -C / -czf /root/maxkb-backup-$(date +%F).tar.gz var/lib/maxkb etc/maxkb
sudo systemctl start maxkb
Copy the archive off the VM. It contains your secrets, so keep it as private as the VM itself. You can also snapshot the VM's OS disk in Azure.
Add more local models. Choose a model whose licence suits you and pull it into the local Ollama, then add it in MaxKB under Model > Add Model > Ollama with the API URL http://172.30.250.1:11434:
OLLAMA_HOST=172.30.250.1:11434 ollama pull qwen2.5:3b
Larger models answer better but more slowly on a CPU and need more memory: allow roughly the model's download size again in free RAM.
Updates. Ubuntu security updates install automatically. MaxKB is pinned to the image digest of 2.10.7 LTS for a repeatable deployment; to move to a later MaxKB release, back up first, then change the image reference in /etc/systemd/system/maxkb.service to the new release's digest from Docker Hub (1panel/maxkb), run sudo systemctl daemon-reload and restart MaxKB. MaxKB migrates its database on start.
Troubleshooting.
- The browser shows 502 Bad Gateway. MaxKB is still starting (after a reboot or restart it takes about a minute). Wait and reload.
- Sign in asks for a CAPTCHA. A previous attempt failed. Enter the CAPTCHA, or wait ten minutes.
- Answers are slow. The local model runs on the CPU. Use a larger VM, keep questions and knowledge base segments short, or connect a hosted model (Step 9).
- First boot did not finish. Read
/var/log/maxkb-firstboot.log. It never prints a password.
Support
cloudimg provides 24/7 support for this image. Contact support@cloudimg.co.uk with your VM's region and the output of sudo tail -n 50 /var/log/maxkb-firstboot.log.