Repository navigation
vm driver: /etc/hosts is empty in a MicroVM sandbox, so localhost does not resolve #3934
Description
Summary
In a sandbox started by the MicroVM (vm) compute driver, /etc/hosts is an
empty file, and the guest's loopback DNS relay (nameserver 127.0.0.53) answers
a localhost query with SERVFAIL. As a result nothing in the sandbox can
resolve localhost: getent hosts localhost fails, dev servers and local MCP
servers that listen on localhost fail, and programs that open a local
listener at start exit at once. Antigravity CLI 1.2.12 (agy), for example,
exits with:
Failed to start: listen tcp: lookup localhost on 127.0.0.53:53: server misbehaving
The docker driver does not have the problem: Docker bind-mounts a generated
/etc/hosts (with 127.0.0.1 localhost and ::1 localhost) into every
container.
Environment
- OpenShell 0.1.1 release binaries (
openshell-gateway,openshell-driver-vm),
macOS 27.0 on Apple silicon, Docker Desktop 29.1.5 (containerd image store). - Sandbox image built locally on
ghcr.io/nvidia/openshell-community/sandboxes/base@sha256:aeef1c63…
(Ubuntu 24.04, glibc 2.39,hosts: files dnsin/etc/nsswitch.conf). [openshell.drivers.vm] sandbox_uid = 501,sandbox_gid = 20.
Steps to reproduce
-
Start a gateway with
compute_driver = "vm". -
Create a sandbox from any local image, for example the community base.
-
In the sandbox:
$ ls -l /etc/hosts; wc -c /etc/hosts -rwxr-xr-x 1 root root 0 … /etc/hosts 0 /etc/hosts $ grep '^hosts:' /etc/nsswitch.conf hosts: files dns $ cat /etc/resolv.conf nameserver 127.0.0.53 options timeout:2 attempts:2 $ getent hosts localhost; echo $? 2 $ node -e 'require("dns").lookup("localhost", (e, a) => console.log(e ? e.code : a))' EAI_AGAIN
The same failure reproduces without OpenShell by running the image with an
empty file mounted over /etc/hosts and a resolver at 127.0.0.53 that
answers SERVFAIL: getent, Node (EAI_AGAIN), Python
(Temporary failure in name resolution), Go programs with and without cgo
(lookup localhost on 127.0.0.53:53: server misbehaving) and agy all fail.
(curl resolves localhost internally and is not affected.)
Expected
localhost resolves to 127.0.0.1 and ::1 in every sandbox, as on the docker
driver, and the sandbox hostname resolves to a loopback address.
Cause
- The vm driver prepares a local image's root filesystem through the Docker
API (/containers/create, then/containers/{id}/export). Docker's init
layer creates empty placeholder files for/etc/hosts,/etc/hostnameand
/etc/resolv.conf(mode 0755) over the image's own, and the export includes
them, so the image's/etc/hostsis lost. - The guest init (
run_post_overlay_setupin the driver's embedded init
script) writes/etc/hostname(configure_hostname) and/etc/resolv.conf
(the loopback DNS relay), but never/etc/hosts. - The DNS relay does not answer
localhost(SERVFAIL), which RFC 6761 says
resolvers should answer with loopback themselves. - The workload runs as the unprivileged sandbox uid with no capabilities and
cannot write/etc, and init drop-ins run only when the driver injects them,
so an image cannot repair it.
Suggested fix
In the guest init, next to configure_hostname, write /etc/hosts before
handing control to the workload, for example:
configure_hosts() {
cat >"$(root_path /etc/hosts)" <<EOF
127.0.0.1 localhost
::1 localhost ip6-localhost ip6-loopback
127.0.1.1 ${sandbox_hostname}
EOF
chmod 0644 "$(root_path /etc/hosts)"
}(root-owned, 0644, with the hostname configure_hostname chose), or keep the
image's own /etc/hosts when it has one instead of Docker's init-layer
placeholder. Independently, the loopback DNS relay could answer localhost
and *.localhost with loopback addresses (RFC 6761 section 6.3) instead of
SERVFAIL, which also covers programs that skip /etc/hosts.
Workaround used downstream
DefenseClaw installs libnss-myhostname in its sandbox images and sets
hosts: files myhostname dns, which answers localhost for programs that use
the system resolver (glibc, Node, Python, Go built with cgo). Go's pure
resolver and static musl binaries read /etc/hosts and DNS themselves and stay
broken until the guest writes /etc/hosts.
🤖 Generated with Claude Code
Activity
shiju-nv commentedon Oct 4, 2026
I reproduced this on a newer build than the report used, and traced where the empty file comes from.
Reproduction.
0.1.3-dev.82+g8983642e2(the rollingdevrelease), macOS 26.7 on Apple Silicon, VM driver with the default bootstrap imagenvcr.io/nvidia/base/ubuntu:24.04, no local image. In a new sandbox:Cause, from reading the guest init.
crates/openshell-driver-vm/scripts/openshell-vm-sandbox-init.shwrites/etc/hostnameand/etc/resolv.confinrun_post_overlay_setup, but nothing writes/etc/hosts, so the image's empty placeholder survives.A fix is up in #4182. It makes guest init write the loopback names when
/etc/hostsis missing or empty, and leaves a non-empty file or a symlink alone.fix(vm): populate an empty guest /etc/hosts with loopback names (NVID…