Deployment guide

OpenHands

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.

This SecureAnyCloud 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. The container is bound to the loopback connector 127.0.0.1:3000 behind an nginx reverse proxy on port 80, and it mounts the host Docker socket so it can launch a fresh, isolated runtime container for each conversation.

OpenHands has no login of its own, so nginx protects the whole app with HTTP Basic Auth, using a unique password 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.

What you get:

  • 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 and nginx.service as enabled, active systemd units
  • An unauthenticated /healthz endpoint for Azure Load Balancer health probes
  • Streaming and WebSocket-friendly nginx proxy configuration for the agent event log
  • 24/7 SecureAnyCloud support

Prerequisites

You need an active Azure subscription, an SSH key pair, and a VNet with a subnet in the target region.

Standard_B2ms (2 vCPU / 8 GiB RAM) is a comfortable starting size. It gives the OpenHands control plane room to run alongside a runtime container for your task.

Configure the network security group (NSG) to allow inbound 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 also need your own API key for the LLM provider you plan to use (OpenAI, Anthropic, Google Gemini and many others). You enter it inside OpenHands after you sign in.

Step 1 - Deploy from the Azure Marketplace

Sign in to the Azure Portal, choose Create a resource, search the Marketplace for OpenHands by SecureAnyCloud, and select Create.

On the Basics tab, 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 select Review + create and Create.

Step 2 - Deploy from the Azure CLI

# Accept the Marketplace terms once per subscription (required before the first CLI deploy)
az vm image terms accept --urn sac1647283583153:openhands-ubuntu-24-04:default:latest
az vm create \
  --resource-group <your-rg> \
  --name openhands \
  --image sac1647283583153:openhands-ubuntu-24-04:default:latest \
  --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

Connect to the VM over SSH:

ssh azureuser@<vm-public-ip>

Step 4 - Confirm the services are running

Check the status of the services:

systemctl is-active docker.service nginx.service
sudo 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, which is bound to the loopback connector 127.0.0.1:3000. 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. Read it as root:

sudo cat /root/openhands-credentials.txt

This file contains OPENHANDS_URL, OPENHANDS_USERNAME and OPENHANDS_PASSWORD. Store the password somewhere safe. OpenHands can run code and control Docker on this VM, so 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. Query it:

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

A password is set on first boot, so an unauthenticated request to OpenHands is rejected with HTTP 401. 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 your first visit. Choose your LLM provider, pick a model and paste your own API key, then select 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 you save your key, you land on the Let's Start Building! home screen. Choose Open Repository to connect a GitHub, GitLab, Bitbucket or Azure DevOps account and get suggested tasks. Or choose Start from Scratch to begin a conversation that is not tied to a repository.

Describe a task. OpenHands plans it, then edits files and runs commands inside a runtime container that 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, including the provider and key you configured in Step 8. The other tabs cover the Agent, Condenser, Verification, MCP servers, Skills, Integrations, Application preferences and Secrets.

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 on 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. Inspect the mount:

sudo docker inspect openhands-app --format '{{range .Mounts}}{{.Source}} -> {{.Destination}}{{"\n"}}{{end}}' | grep docker.sock

The output shows /var/run/docker.sock -> /var/run/docker.sock. This mount is how the agent runs your task in a sandbox that is separate from the control plane. It is also 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 on this VM under /var/lib/openhands. Back up that path 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 you enter real API keys or run it in production.
  • Security patches. unattended-upgrades stays enabled, so the OS keeps receiving security updates automatically.
  • License. OpenHands is MIT licensed. This image ships the open-source OpenHands. The SecureAnyCloud charge covers packaging, security patching, image maintenance and support.

Support

SecureAnyCloud provides 24/7 expert support for this image. For questions or help with your deployment, contact sales@secureanycloud.com.

Deploy on Azure

Launch OpenHands on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure