OpenHands on Ubuntu 24.04 on Azure User Guide
Overview
OpenHands is a popular open source AI software development agent. It works like an autonomous developer: you describe a task in plain language and the agent plans the work, edits files, runs commands and reads documentation inside a sandboxed environment that it creates and controls for itself. The cloudimg image installs Docker CE from the official Docker repository and runs the OpenHands control plane as a container (managed by Docker with a restart policy), bound to the loopback connector 127.0.0.1:3000 behind an nginx reverse proxy on port 80. The app mounts the host Docker socket so it can launch a fresh isolated runtime container for each conversation. Because OpenHands ships no login of its own, nginx protects the whole app with HTTP Basic Auth, and a unique login password is generated on the first boot of every VM. No model key is baked into the image: you bring your own LLM provider key and enter it in the OpenHands Settings screen, so your provider account and data stay yours. Each boot pulls the OpenHands image fresh so your VM is never left on a stale build. Backed by 24/7 cloudimg support.
What is included:
- OpenHands running as a container managed by Docker, pulled fresh on first boot
- Docker CE preinstalled from the official Docker apt repository, with the socket mounted so the agent can spawn per session runtime containers
- The OpenHands agent UI on
:80, fronted by nginx with HTTP Basic Auth - A unique Basic Auth login password generated on first boot and recorded in a root only file
- No LLM key baked in: you add your own provider key in the OpenHands Settings screen
docker.service+nginx.serviceas systemd units, enabled and active- An unauthenticated
/healthzendpoint for Azure Load Balancer health probes - Streaming and websocket friendly nginx proxy configuration for the agent event log
- 24/7 cloudimg support
Prerequisites
An active Azure subscription, an SSH key pair, and a VNet + subnet in the target region. Standard_B2ms (2 vCPU / 8 GiB RAM) is a comfortable starting point, giving the OpenHands control plane room to run alongside a runtime container for your task. NSG inbound: allow 22/tcp from your management network and 80/tcp. OpenHands serves plain HTTP on port 80; for production, terminate TLS in front of it with your own domain and consider adding 443/tcp (see Maintenance). You will also need your own API key for whichever LLM provider you plan to use (OpenAI, Anthropic, Google Gemini and many others), which you enter inside OpenHands after signing in.
Step 1 - Deploy from the Azure Marketplace
Sign in to the Azure Portal, choose Create a resource, search the Marketplace for OpenHands by cloudimg, and select Create. On Basics pick your subscription, resource group, region and size; under Administrator account choose SSH public key and paste your key; under Inbound port rules allow SSH (22) and HTTP (80). Then Review + create -> Create.
Step 2 - Deploy from the Azure CLI
az vm create \
--resource-group <your-rg> \
--name openhands \
--image <marketplace-image-urn> \
--size Standard_B2ms \
--admin-username azureuser \
--ssh-key-values ~/.ssh/id_ed25519.pub \
--vnet-name <your-vnet> --subnet <your-subnet> \
--public-ip-sku Standard
az vm open-port --resource-group <your-rg> --name openhands --port 80 --priority 1010
Step 3 - Connect to your VM
ssh azureuser@<vm-public-ip>
Step 4 - Confirm the services are running
systemctl is-active docker.service nginx.service
docker ps --filter name=openhands-app --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
Both systemd units report active, and Docker shows the openhands-app container Up. Docker manages the OpenHands control plane (bound to the loopback connector 127.0.0.1:3000), and nginx fronts it on port 80 behind HTTP Basic Auth.

Step 5 - Retrieve your login credentials
The appliance's network access gate is HTTP Basic Auth (user admin). The password is generated uniquely on the first boot of your VM and written to a root only file:
sudo cat /root/openhands-credentials.txt
This file contains OPENHANDS_URL, OPENHANDS_USERNAME and OPENHANDS_PASSWORD. Store the password somewhere safe: because OpenHands can run code and control Docker on this VM, this gate is what keeps the agent private to you.

Step 6 - Confirm the health endpoint
nginx serves an unauthenticated health endpoint for load balancers and probes:
curl -s http://localhost/healthz
It returns ok. This endpoint never requires authentication, so it is safe for an Azure Load Balancer health probe.
Step 7 - Confirm the login password is enforced
Because a password is set on first boot, an unauthenticated request to OpenHands is rejected (HTTP 401), so nobody reaches the agent without the password:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1/
It prints 401. A wrong password is also rejected with 401; only the per-VM password from Step 5 returns 200:
curl -s -o /dev/null -w '%{http_code}\n' -u 'admin:<OPENHANDS_PASSWORD>' http://127.0.0.1/
This prints 200.

Step 8 - Sign in and connect your LLM provider
Browse to http://<vm-public-ip>/. Your browser first prompts for HTTP Basic Auth credentials: enter admin and the password from Step 5. Behind that gate, OpenHands opens an AI Provider Configuration screen on first visit. Choose your LLM provider, pick a model, and paste your own API key, then Save. No key ships in the image, so your provider account stays yours; your key is stored only on this VM.

Step 9 - Start building
After saving your key you land on the Let's Start Building! home screen. From here you can Open Repository to connect a GitHub, GitLab, Bitbucket or Azure DevOps account and get suggested tasks, or Start from Scratch with a new conversation that is not tied to a repository. Describe a task and OpenHands plans it, then edits files and runs commands inside a runtime container it launches for the session.

Step 10 - Manage your model in Settings
Open Settings from the left rail to manage the agent. The LLM tab holds your model profiles (the provider and key you configured in Step 8), and the other tabs cover the Agent, Condenser, Verification, MCP servers, Skills, Integrations, Application preferences and Secrets. You can add more model profiles here and switch between them per conversation.

Step 11 - Connect your repositories
Open Integrations in Settings to connect a Git provider. Add a GitHub, GitLab or Bitbucket token so OpenHands can read your repositories, propose changes and open pull requests as it works. Tokens are stored only in this VM.

Step 12 - Confirm the runtime model
OpenHands launches an isolated runtime container for each conversation through the host Docker socket, which the control plane container has mounted:
docker inspect openhands-app --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' | grep docker.sock
You see /var/run/docker.sock -> /var/run/docker.sock. This is how the agent runs your task in a sandbox that is separate from the control plane, and it is why the nginx Basic Auth gate in front of the app matters.
Maintenance
- Edge password: the Basic Auth password in front of OpenHands is set on first boot. To change it, run
sudo sh -c 'printf "admin:%s\n" "$(openssl passwd -apr1 NEW_PASSWORD)" > /var/lib/openhands/.htpasswd' && sudo systemctl reload nginx. - LLM provider key: your model key is entered under Settings and stored only in this VM under
/var/lib/openhands; back that path up to preserve your configuration and conversation history. - Upgrades: OpenHands runs as the
openhands-appcontainer from the pinned stable image, and first boot pulls it fresh automatically. To upgrade an already running VM, runsudo docker pull docker.openhands.dev/openhands/openhands:1.8then recreate the container with the same-p 127.0.0.1:3000:3000 -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/openhands:/.openhandsbinding. - TLS: OpenHands serves plain HTTP on port 80; front it with TLS (for example certbot with your own domain) before production use, especially before entering real API keys.
- Security patches: unattended-upgrades remains enabled so the OS continues to receive security updates automatically.
- License: OpenHands is MIT licensed. This image ships the open source OpenHands; the cloudimg charge covers packaging, security patching, image maintenance and support.
Support
cloudimg provides 24/7 expert support for this image. Contact support@cloudimg.co.uk.