How Do I Run Home Assistant in a Cloud Sandbox to Try It Out?
Updated October 27, 2026 .md
Short answer: Run the official Home Assistant Container image in a Sandboxer sandbox and host port 8123 with sbx host. Write an http block that trusts the sandbox's gateway as a reverse proxy before the first start, or every request through the URL gets 400 Bad Request. Onboarding, the REST API and WebSockets then work.
This is a place to try Home Assistant, build dashboards and test automations without a Raspberry Pi. It is not a replacement for a box at home: the sandbox is in a data center, not on your home network, and it has no USB, Zigbee or Bluetooth radio.
At a glance#
Everything below ran on one default sandbox (4 vCPU, 8 GB RAM) on October 27, 2026. Single runs, so treat timings as rough.
| Step | What we saw |
|---|---|
Pull ghcr.io/home-assistant/home-assistant:stable |
2.35 GB image, 45 s |
| Home Assistant version | 2026.10.4 |
docker run to first HTTP response |
5.2 s |
| Hosted URL, default settings | 400: Bad Request on every page |
| Hosted URL, gateway trusted before first start | 302 to /onboarding.html, then 200 |
| RAM in use after onboarding | 281 MiB |
| REST service call through the URL (20 calls) | median 129 ms, p90 131 ms, from our test machine |
| WebSocket API through the URL | auth_ok, state_changed events arrive |
sbx stop --wait + sbx resume |
memoryRestored: true, same container PID |
| Reboot | Home Assistant answering 3.7 s after boot, helper state restored |
1. Create a sandbox and pull the image#
sbx new --name home-assistant --type default
id=sb_kuf6ceh6ps # the id that sbx new printed
sbx exec $id --timeout 600 "docker pull -q ghcr.io/home-assistant/home-assistant:stable"Docker is preinstalled. Pass --timeout 600: sbx exec stops waiting after 30 seconds by default, and the pull took 45.
2. Why the hosted URL returns 400 by default#
We first started Home Assistant exactly as its Linux install page shows and hosted port 8123. The token URL set its cookie, then every page returned this:
withtoken 302 -> https://rare-even-atlas-8123.box.sandboxer.com/
cookie / 400 ->
400: Bad RequestThe container log says why:
ERROR (MainThread) [homeassistant.components.http.forwarded] A request from a reverse proxy was received from 10.202.0.69, but your HTTP integration is not set-up for reverse proxiesThe Sandboxer proxy adds X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host to each request and connects to the VM from its gateway address (10.202.0.69 in this sandbox). Home Assistant blocks forwarded requests from any proxy it hasn't been told to trust. See the HTTP integration.
Adding an http: block to configuration.yaml after the first start did not fix it. Since 2026.8, per the HTTP integration page, Home Assistant imports that block once, on its first start, and keeps the settings in .storage/http from then on. A block added later is ignored. So write it before the first start.
3. Write the proxy setting, then start Home Assistant#
Save this as ha-setup.sh on your computer. It reads the gateway from the sandbox's default route and writes a minimal configuration.yaml before the container ever runs.
# ha-setup.sh
docker rm -f homeassistant >/dev/null 2>&1
sudo rm -rf ~/homeassistant && mkdir -p ~/homeassistant
gw=$(ip route show default | awk '{print $3}')
cat > ~/homeassistant/configuration.yaml <<YAML
default_config:
http:
use_x_forwarded_for: true
trusted_proxies:
- $gw
YAML
cat ~/homeassistant/configuration.yaml
s=$(date +%s%N)
docker run -d --name homeassistant --privileged --restart=unless-stopped --stop-timeout 60 \
-e TZ=America/New_York -v ~/homeassistant:/config -v /run/dbus:/run/dbus:ro \
--network=host ghcr.io/home-assistant/home-assistant:stable >/dev/null
until curl -sf -o /dev/null localhost:8123/manifest.json; do sleep 0.5; done
echo "first response after $(( ($(date +%s%N)-s)/1000000 )) ms"sbx scp ha-setup.sh $id:ha-setup.sh
sbx exec $id --timeout 300 "bash ha-setup.sh"default_config:
http:
use_x_forwarded_for: true
trusted_proxies:
- 10.202.0.69
first response after 5210 msThe docker run line is the official one with two additions from the same page: a real time zone and --stop-timeout 60, so Home Assistant has time to close its database on a stop. --restart=unless-stopped is what brings it back after a cold boot.
Keep the default_config: line. Without a configuration.yaml, Home Assistant writes one with default_config: in it. Because you write the file first, you have to add that line yourself.
After the first start, .storage/http held "trusted_proxies": ["10.202.0.69/32"] and Home Assistant raised a repair asking you to remove the YAML block. We removed it and restarted: the hosted URL kept working, because the setting now lives in storage. From then on you change it in Settings > System > Network > HTTP server.
4. Give it a private HTTPS URL#
sbx host $id 8123 --title "Home Assistant"https://rare-even-atlas-8123.box.sandboxer.com?_token=<token>| Request | Status |
|---|---|
https://rare-even-atlas-8123.box.sandboxer.com/ (no token) |
401 from the Sandboxer proxy |
The full URL with ?_token= |
302 to /, sets the __sbx_auth cookie |
/ with the cookie, before onboarding |
302 to /onboarding.html |
/onboarding.html with the cookie |
200, <title>Home Assistant |
Open the token URL in your browser and create the owner account. The first account becomes the owner, so do it before you send the link to anyone. Give a sandbox port an HTTPS URL covers public URLs and revoking a link.
We ran onboarding through the API from Node 24 so we could record each response. In a browser you fill in the same four steps:
onboarding/users 200 auth_code received
auth/token 200 Bearer expires_in 1800
onboarding/core_config 200
onboarding/analytics 200
onboarding/integration 200
GET /api/ 200 {"message":"API running."}
GET /api/config 200 2026.10.4 UTC RUNNING/api/config reported UTC even though the container has TZ=America/New_York. The TZ variable sets the container's clock. Home Assistant's own time zone, used by automations, is the one you pick during onboarding (we sent an empty core_config step, so it stayed UTC).
5. Two locks: the Sandboxer token and the Home Assistant login#
Both checks apply to every request through a private URL:
Request to /api/ |
Status |
|---|---|
| Home Assistant bearer token, no Sandboxer cookie | 401 (the proxy stops it) |
| Sandboxer cookie, no Home Assistant token | 401 (Home Assistant stops it) |
| Both | 200 |
A WebSocket to /api/websocket without the cookie was refused at the handshake. With the cookie it got auth_required from Home Assistant 2026.10.4.
The proxy keeps the real client address. We sent a wrong password twice, once with a forged X-Forwarded-For: 203.0.113.9 header. Both times Home Assistant logged our machine's real public IP, not the forged one. So its login-attempt ban works against the actual caller.
6. Try it without devices#
The sandbox can't see anything on your home network, so there is nothing to discover. Helpers work well for testing automations. We created a toggle over the WebSocket API (input_boolean/create, name "Porch light"), then called a service 20 times through the hosted URL:
const t = performance.now();
const r = await fetch(`${base}/api/services/input_boolean/toggle`, {
method: "POST",
headers: { Cookie: cookie, Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
body: JSON.stringify({ entity_id: "input_boolean.porch_light" }),
});input_boolean/create true porch_light
toggle x20 status 200 median 129 ms p90 131 ms
ws: auth_required 2026.10.4
ws: auth_ok
ws: result true
ws: state_changed input_boolean.porch_light off -> onThe 129 ms is the whole round trip from our test machine to the sandbox's eu-central region and back, so your number depends on where you are. The WebSocket subscription saw the state change made by a REST call, which is how the frontend gets live updates.
Starting the demo integration through the config-flow API returned abort with reason not_implemented, so we used helpers instead.
7. Stop, resume and reboot#
sbx exec $id "docker inspect -f pid={{.State.Pid}} homeassistant"
sbx stop $id --wait
sbx resume $id
sbx exec $id "docker inspect -f pid={{.State.Pid}} homeassistant; ip route show default"pid=1341
ready memoryRestored=true # from the resume JSON
pid=1341
default via 10.202.0.69 dev eth0Stop and resume each took about 2 seconds. Home Assistant kept running in memory, and the URL, cookie and login kept working.
A resume on a machine with a different CPU type boots from disk instead (memoryRestored: false). To test that path we rebooted with sudo systemctl reboot:
1.62 3.64 # /proc/uptime when exec answered
3.67 9.70 # /proc/uptime when Home Assistant answered
pid=561 restarts=0 status=running
default via 10.202.0.69 dev eth0
porch_light 200 onThe container came back on its own because of its restart policy. "Porch light" was still on, and the refresh token from before still worked. The gateway address was the same after the resume and the reboot.
If the URL ever starts returning 400 again, compare ip route show default with the trusted proxy in Settings > System > Network. You can reach the UI without the proxy, so without X-Forwarded-For, through an SSH tunnel:
sbx forward $id --remote 8123 --local 18123
# then open http://127.0.0.1:18123 on your computerforward localhost:18123/ 200What fits and what doesn't#
| You want to | Fits? |
|---|---|
| Try Home Assistant before buying hardware | Yes |
| Build dashboards, test automations and templates against helpers | Yes |
| Test an integration that talks to a cloud API over the internet | Yes, it has outbound internet |
| Control devices on your home LAN, or use Zigbee, Z-Wave or Bluetooth | No. It isn't on your network and has no radios |
| Apps (the add-on store) | No. Home Assistant's Linux install page says the Container install has no access to apps |
| Run your home 24/7 | No. Automations don't run while the sandbox is stopped |
FAQ#
Which size should I use?#
We used default. Home Assistant used 281 MiB of RAM after onboarding, and the image is 2.35 GB. By those numbers small (2 vCPU, 4 GB RAM, 12 GB disk) should be enough for a trial, but we didn't run it there.
Can I add the http block after Home Assistant has started once?#
Not in YAML. In 2026.10.4 a block added after the first start had no effect and every proxied request still got 400. Use Settings > System > Network, reached through sbx forward, or start over with an empty config folder.
Should I trust all of 10.0.0.0/8 instead of one address?#
No need. Trust only the gateway from ip route show default. That is the address the Sandboxer proxy connects from.
Does the sandbox stop on its own?#
Yes. It stops after one hour by default and pauses after a period of inactivity, and no automation runs while it is stopped. Change these with sbx extend $id --hours 12 and sbx idle $id 0. See idle auto-pause and wake on use.
Do I need --privileged?#
Home Assistant's own command uses it, and it worked in the sandbox, which runs Docker inside its own VM. We didn't test without it.
Related#
More guides
For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.
For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.
The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.
If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.