Sandbox: allow outbound connections to localhost
Problem
The sandbox blocks outbound TCP connections to localhost/127.0.0.1/::1 even when these are listed in sandbox.network.allowedDomains. The sock.connect() syscall gets EPERM (Operation not permitted).
This makes it impossible to run integration tests against local Docker services (e.g. http://localhost:8000) from within the sandbox.
What I've tried
In settings.json:
"sandbox": {
"enabled": true,
"network": {
"allowLocalBinding": true,
"allowedDomains": [
"localhost",
"127.0.0.1",
"::1"
]
}
}
None of these settings allow the outbound connection. The only workaround is either dangerouslyDisableSandbox: true on every bash call, or adding the command to excludedCommands, both of which defeat the purpose of the sandbox.
Requested feature
A sandbox setting that allows outbound TCP connections to loopback addresses, e.g.:
"sandbox": {
"network": {
"allowLocalhostConnections": true
}
}
Or have allowedDomains with localhost/127.0.0.1/::1 actually allow the TCP connection, not just DNS resolution.
Use case
Running integration tests against services in local Docker containers. For example, starting a service via docker compose and then running integration tests that hit http://localhost:8000.
8 Comments
I'm hitting this as well, would really like to be able to hit local servers with claude code, and stay inside the sandbox.
Same problem here
On MacOS, there's
network.allowLocalBinding: https://code.claude.com/docs/en/settings#sandbox-settingsWould be nice to have fine-grained per port restrictions though.
Cannot believe this hasn't been fixed yet. It's impossible to do local development if required to test things on the local webapp, etc.
The problem is that claude is running inside an isolated network namespace, so claude's localhost and the real localhost are different. Claude has a proxy to access resources outside of it's network namespace (that's how
allowedDomainsworks). Unfortunately, you almost never want proxy requests to localhost, except in this case.So the easiest fix to start all commands with
no_proxy=""(for example:NO_PROXY="" no_proxy="" ./run-tests.shorcurl --no-proxy "" localhost:1234also works). Unfortunately, I am not sure what the proper way to fix this is. settings the following claude config:Also doesn't seem to work because this setting gets either ignored or overwritten once it reaches the sandbox. Even claude itself has a hard time understanding the problem. I've just updated my skills to use curl --no-proxy "" if it's running inside of a sandbox and wants to access localhost).
This issue also blocks
bin/rails testconnections to PostgreSQL (which is listening on localhost).Basically this sandbox is completely useless without a way to connect to local services / ports.
Ideally, it would be useful to allow sandbox connections to localhost only on specific ports.
+1 — hit this exact class in production automation, adding empirical detail from macOS (Claude Code 2.x, seatbelt sandbox) in case it helps scope a fix.
Mechanism as observed: the sandbox injects
HTTP_PROXY/HTTPS_PROXY/ALL_PROXYpointing at the local relay, andNO_PROXY=localhost,127.0.0.1,::1,<private ranges>. So any loopback request bypasses the relay and attempts a directconnect(), which seatbelt denies (curl:Immediate connect fail for 127.0.0.1: Operation not permitted, exit 7). SinceallowedDomains/allowedHostsfiltering applies at the proxy rather than theconnect()syscall, listinglocalhostthere (even*) has no effect — loopback is unreachable by construction.Why this bites harder than a plain block: curl maps the EPERM to exit 7, which is indistinguishable at the exit-code level from
ECONNREFUSED. Every script that treats "curl failed" as "service down" turns the sandbox denial into a false negative. In our case, health probes against a local dev daemon read as "daemon offline" on every sandboxed run, and downstream tooling killed healthy long-running processes based on that signal — twice — before we traced it to the sandbox.Observation for maintainers: forcing the request through the relay (
curl --noproxy "" -x "$HTTP_PROXY" http://127.0.0.1:<port>/...) does reach loopback services from inside the sandbox, at least in current builds. We're deliberately not depending on it since the relay env shape looks like internal plumbing — but it suggests the relay could be the enforcement point for an opt-in localhost allowance (e.g.allowLocalhostConnectionswith a port list), keeping deny-by-default while giving dev servers/daemons a sanctioned path.Until then we're working around it with file-based health witnesses (the sandbox allows file reads and
lsof -iTCP), which works but shouldn't be necessary for plain localhost HTTP. A port-scoped loopback allowlist would resolve this cleanly.@malsalem514 than you for clue. Hot-fixed this with: