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 dns in /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/hostname and
/etc/resolv.conf (mode 0755) over the image's own, and the export includes
them, so the image's /etc/hosts is lost.
- The guest init (
run_post_overlay_setup in 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
Summary
In a sandbox started by the MicroVM (
vm) compute driver,/etc/hostsis anempty file, and the guest's loopback DNS relay (
nameserver 127.0.0.53) answersa
localhostquery with SERVFAIL. As a result nothing in the sandbox canresolve
localhost:getent hosts localhostfails, dev servers and local MCPservers that listen on
localhostfail, and programs that open a locallistener at start exit at once. Antigravity CLI 1.2.12 (
agy), for example,exits with:
The docker driver does not have the problem: Docker bind-mounts a generated
/etc/hosts(with127.0.0.1 localhostand::1 localhost) into everycontainer.
Environment
openshell-gateway,openshell-driver-vm),macOS 27.0 on Apple silicon, Docker Desktop 29.1.5 (containerd image store).
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:
The same failure reproduces without OpenShell by running the image with an
empty file mounted over
/etc/hostsand a resolver at127.0.0.53thatanswers 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) andagyall fail.(curl resolves
localhostinternally and is not affected.)Expected
localhostresolves to127.0.0.1and::1in every sandbox, as on the dockerdriver, and the sandbox hostname resolves to a loopback address.
Cause
API (
/containers/create, then/containers/{id}/export). Docker's initlayer creates empty placeholder files for
/etc/hosts,/etc/hostnameand/etc/resolv.conf(mode 0755) over the image's own, and the export includesthem, so the image's
/etc/hostsis lost.run_post_overlay_setupin the driver's embedded initscript) writes
/etc/hostname(configure_hostname) and/etc/resolv.conf(the loopback DNS relay), but never
/etc/hosts.localhost(SERVFAIL), which RFC 6761 saysresolvers should answer with loopback themselves.
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/hostsbeforehanding control to the workload, for example:
(root-owned, 0644, with the hostname
configure_hostnamechose), or keep theimage's own
/etc/hostswhen it has one instead of Docker's init-layerplaceholder. Independently, the loopback DNS relay could answer
localhostand
*.localhostwith loopback addresses (RFC 6761 section 6.3) instead ofSERVFAIL, which also covers programs that skip
/etc/hosts.Workaround used downstream
DefenseClaw installs
libnss-myhostnamein its sandbox images and setshosts: files myhostname dns, which answerslocalhostfor programs that usethe system resolver (glibc, Node, Python, Go built with cgo). Go's pure
resolver and static musl binaries read
/etc/hostsand DNS themselves and staybroken until the guest writes
/etc/hosts.🤖 Generated with Claude Code