Op
Artificial Intelligence (AI) Azure

OpenHands on Ubuntu 24.04 on Azure User Guide

| Product: OpenHands on Ubuntu 24.04 LTS on Azure

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.service as systemd units, enabled and active
  • An unauthenticated /healthz endpoint 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.

docker.service and nginx.service active, the openhands-app container healthy on the loopback connector 3000, the Docker socket mounted, and nginx listening on port 80

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.

The OpenHands image reference and the per-VM credentials file with the generated admin password and URL

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.

nginx returning 401 for an unauthenticated request and 200 once the per-VM admin password is supplied, with a note that OpenHands ships no built in login and no LLM key

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.

The OpenHands AI Provider Configuration first run screen behind the nginx Basic Auth gate, where you paste your own LLM provider API key

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.

The signed in OpenHands home screen with Open Repository and Start from Scratch, recent conversations and suggested tasks

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.

The OpenHands Settings LLM tab showing model profiles and the full settings navigation

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.

The OpenHands Integrations settings page with GitHub, GitLab and Bitbucket token fields

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-app container from the pinned stable image, and first boot pulls it fresh automatically. To upgrade an already running VM, run sudo docker pull docker.openhands.dev/openhands/openhands:1.8 then 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:/.openhands binding.
  • 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.