Sandbox: allow outbound connections to localhost

Status Open
Maintainer reply None cached
Activity 8 comments · opened Feb 24, 2026

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.

View original on GitHub ↗

8 Comments

akda5id · 7 months ago

I'm hitting this as well, would really like to be able to hit local servers with claude code, and stay inside the sandbox.

GautierBlandin · 7 months ago

Same problem here

lgruen-vcgs · 6 months ago

On MacOS, there's network.allowLocalBinding: https://code.claude.com/docs/en/settings#sandbox-settings

Would be nice to have fine-grained per port restrictions though.

zupd · 4 months ago

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.

Matt3o12 · 3 months ago

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 allowedDomains works). 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.sh or curl --no-proxy "" localhost:1234 also works). Unfortunately, I am not sure what the proper way to fix this is. settings the following claude config:

"env": {
    "no_proxy": "",
    "NO_PROXY": ""
  },

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).

collimarco · 3 months ago

This issue also blocks bin/rails test connections 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.

malsalem514 · 1 month ago

+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_PROXY pointing at the local relay, and NO_PROXY=localhost,127.0.0.1,::1,<private ranges>. So any loopback request bypasses the relay and attempts a direct connect(), which seatbelt denies (curl: Immediate connect fail for 127.0.0.1: Operation not permitted, exit 7). Since allowedDomains/allowedHosts filtering applies at the proxy rather than the connect() syscall, listing localhost there (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. allowLocalhostConnections with 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.

crawler · 1 month ago

@malsalem514 than you for clue. Hot-fixed this with:

# CLAUDE.md

IMPORTANT! when you in sandbox you should use $HTTP_PROXY to reach localhost. Direct requests would fail!