OpenHands AI Software Development Agent on AWS User Guide
Overview
This image runs OpenHands 1.8, the open source AI software engineering agent (OpenHands/OpenHands, MIT), on Ubuntu 24.04 LTS. You describe a task in a browser and the agent plans it, then reads and edits files, runs shell commands and browses the web to carry it out, working inside a sandboxed runtime container that it launches on demand.
OpenHands runs as the official application container under Docker, bound to the loopback address 127.0.0.1:3000; nginx fronts it on port 80 with HTTP Basic Authentication. An unauthenticated /healthz endpoint stays open for load balancer probes; everything else requires the password. The default security group opens port 22 (SSH), port 80 (HTTP) and port 443 (HTTPS) only, so port 3000 is never reachable from outside the instance.
On the first boot of every deployed instance a one-shot service generates a fresh password, unique to that instance, and writes it to /root/openhands-credentials.txt (mode 0600, root only). No shared or default credential ships in the image.
You supply your own model provider API key
No model API key, model credits or hosted inference endpoint ship with this image. OpenHands serves its interface immediately after launch, but before the agent can run a task you must enter your own provider API key (for example Anthropic or OpenAI) on the AI Provider Configuration screen that opens on first visit. This is a required first-run step, not an optional extra.
Your key is stored only on your own instance, under /var/lib/openhands, and every model request goes directly from your instance to the provider you chose.

Why the whole application sits behind a password
OpenHands has no built-in login of its own — upstream designs it to be run on localhost. It also mounts the host Docker socket so it can start its sandboxed runtime containers, which makes it root-equivalent on the instance. An ungated internet-facing instance would therefore be completely open. The nginx Basic Authentication gate in front of the whole application is what keeps the agent private to you, and it is the load-bearing security control on this image. Do not remove it without putting an equivalent gate in its place.
Storage layout
Two dedicated EBS volumes keep the stateful tiers off the operating system disk, each independently resizable:
| Mount point | Contents |
|---|---|
/var/lib/openhands |
OpenHands state: settings, conversation history, the agent workspace and the nginx password file |
/var/lib/docker |
Docker image and container store, including the agent runtime images |
Both are mounted by filesystem UUID from /etc/fstab, so the layout reproduces on every instance you launch.
Prerequisites
- An AWS account subscribed to this product in AWS Marketplace.
- An EC2 key pair in your target region for SSH access.
- A security group allowing inbound TCP 22 (SSH) from your IP and TCP 80 (HTTP) from your users.
- An API key from a model provider that OpenHands supports (for example Anthropic or OpenAI). You will paste it in on first visit.
- Recommended instance type:
m5.large. Usem5.xlargeor larger if you expect several concurrent agent conversations, since each one starts its own runtime container.
Connecting to your instance
| OS variant | Login user | Example |
|---|---|---|
| Ubuntu 24.04 | ubuntu |
ssh -i your-key.pem ubuntu@<instance-public-ip> |
Step 1 - Launch from the AWS Marketplace console
- Open the product page in AWS Marketplace and choose Continue to Subscribe, then Continue to Configuration.
- Select the OpenHands on Ubuntu 24.04 delivery option and your region, then Continue to Launch.
- Choose an instance type (
m5.largeor larger), your VPC/subnet, key pair and the security group described above, and launch.
Step 2 - Launch from the AWS CLI
aws ec2 run-instances \
--image-id ami-xxxxxxxxxxxxxxxxx \
--instance-type m5.large \
--key-name your-key \
--security-group-ids sg-xxxxxxxx \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=openhands}]'
Step 3 - Connect to your instance
ssh -i your-key.pem ubuntu@<instance-public-ip>
Step 4 - Confirm the services are running
systemctl is-active docker.service nginx.service
Both report active:
active
active
Step 5 - Confirm the OpenHands container
docker ps --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}'
One application container runs. This image ships OpenHands, Docker and nginx and nothing else — there is no separate database or cache tier to operate:
NAMES IMAGE STATUS
openhands-app docker.openhands.dev/openhands/openhands:1.8 Up 15 minutes
Step 6 - Confirm the dedicated storage volumes
df -h / /var/lib/docker /var/lib/openhands
Agent state and the Docker image store are each on their own volume, separate from the operating system disk:
Filesystem Size Used Avail Use% Mounted on
/dev/root 29G 3.4G 25G 13% /
/dev/nvme1n1 79G 11G 64G 15% /var/lib/docker
/dev/nvme2n1 30G 176K 28G 1% /var/lib/openhands
Step 7 - Confirm the security gate
curl -s -o /dev/null -w 'healthz=%{http_code}\n' http://127.0.0.1/healthz
curl -s -o /dev/null -w 'unauthenticated=%{http_code}\n' http://127.0.0.1/
The health endpoint is open for load balancer probes; everything else is refused without the password:
healthz=200
unauthenticated=401
Step 8 - Retrieve your instance password
sudo grep -E '^OPENHANDS_(URL|USERNAME)=' /root/openhands-credentials.txt
That prints the address and the user name. To read the password itself:
| Purpose | Command |
|---|---|
| Show the whole credentials file, password included | sudo cat /root/openhands-credentials.txt |
| Show only the password | sudo grep '^OPENHANDS_PASSWORD=' /root/openhands-credentials.txt |
The user name is always admin. The password is generated on the first boot of your instance and is unique to it.
Step 9 - Open OpenHands in a browser
Browse to http://<instance-public-ip>/ and sign in as admin with the password from Step 8 when the browser prompts you.
Enter the password in the browser's sign-in prompt — do not put it in the address bar. A URL of the form
http://admin:password@<instance-public-ip>/does log you in, but browsers forbid a page whose address contains credentials from makingfetchrequests, and OpenHands loads its interface text that way. The result is a UI where every label shows a raw code such asHOME$LETS_START_BUILDINGinstead of English. If you see that, you signed in via the address bar: clear it back tohttp://<instance-public-ip>/and use the sign-in prompt instead.
Step 10 - Connect your model provider
On the first visit OpenHands opens AI Provider Configuration. Choose your LLM Provider, pick a LLM Model, paste your own API Key, and choose Save. Until you do, the agent cannot run a task.
You can change this at any time under Settings. A newly launched instance has no model profile saved, which confirms no credential ships in the image:

Step 11 - Start a task
Choose Open Repository to point the agent at a repository, or start a conversation and describe the task in plain language. When the conversation starts, OpenHands launches a sandboxed runtime container through the host Docker socket; the agent edits files and runs commands inside that container, so it can install packages, run tests and iterate without touching the rest of your environment.
You can watch the runtime container appear while a conversation is running:
docker ps --format '{{.Names}} {{.Image}}'
The first conversation takes longer to start than later ones, because the runtime image is pulled on demand if it is not already cached.
Step 12 - Extend the agent with MCP servers (optional)
OpenHands supports Model Context Protocol servers, which give the agent extra tools. A new instance starts with none configured, so you decide exactly what the agent can reach:

Operations
Change the sign-in password
Replace NEW_PASSWORD with your own value:
| Step | Command |
|---|---|
| Write the new password | sudo sh -c 'printf "admin:%s\n" "$(openssl passwd -apr1 NEW_PASSWORD)" > /var/lib/openhands/.htpasswd' |
| Apply it | sudo systemctl reload nginx |
Restart OpenHands
sudo docker restart openhands-app
View the application logs
sudo docker logs --tail 20 openhands-app
Back up your state
Everything OpenHands keeps — settings, conversation history and the agent workspace — lives under /var/lib/openhands. Snapshot that EBS volume, or archive the directory:
| Purpose | Command |
|---|---|
| Archive the state directory | sudo tar -czf openhands-state.tar.gz -C /var/lib/openhands . |
Enable HTTPS
For production use, put a domain name in front of the instance and terminate TLS on port 443. Point the DNS record at your instance's public address, then obtain a certificate with your preferred tool and configure it in /etc/nginx/sites-available/cloudimg-openhands. Keep the auth_basic directives in place when you do — they are the control that keeps the agent private.
Security notes
- The agent can run arbitrary commands and controls Docker on this instance. Treat access to the web interface as equivalent to shell access to the instance.
- Restrict port 80 (and 443) in the security group to the addresses your team uses, rather than leaving it open to the internet.
- Keep the per-instance password, or replace it with your own, but never remove the gate without an equivalent control.
- Your model provider key is stored on the instance. Anyone with access to the interface can use it, and so can the agent.
Support
This image is published by cloudimg with 24/7 technical support by email and chat. Support covers deployment, instance sizing, connecting your model provider, the sandboxed runtime and Docker configuration, TLS termination and scaling. OpenHands itself is open source software distributed under the MIT licence; cloudimg charges for support services, not for the software.