Because the default is open, an environment that handles sensitive data should set an explicit allowlist, or deny all egress. Code running in a sandbox can send workspace contents to any reachable host, so the allowlist is what bounds where data can go.
Network Egresspublic
Last verified 12 Nov 2026
DigitalOcean Harness Runtime provides managed, hardware-isolated microVM sandboxes with built-in tools such as Chromium to run harnesses and execute arbitrary code. Rich lifecycle APIs preserve conversational history and working state across sessions, letting you pause, resume, and fork work to control costs and adapt to the nonlinear nature of agentic workflows. Scale complete agents such as Claude Code or use sandboxes independently for code execution, all through the same service. See What You Can Build for example use cases.
Egress is the outbound network access a Harness Runtime sandbox has. By default there is no restriction: a session with no egress field in its environment spec reaches any host it wants.
Restricting egress is opt-in, and it is a switch rather than a dial. The moment you name one allowed host, the allowlist turns on and every host you did not name is denied.
Forms of the Egress Field
The egress field takes one of four forms, counting the omitted field.
| Form | Example | Meaning |
|---|---|---|
| Omitted | no egress field |
Unrestricted. The session reaches any host, and no platform defaults are added. |
| Scalar | egress: unrestricted |
The same thing, stated explicitly. Cannot be combined with an allowlist or a VPC. |
| Host list | egress: [api.github.com, pypi.org] |
Allowlist on. Hostnames only. Platform defaults are merged in. An empty list, egress: [], denies all egress. See Deny All Egress. |
| Object | see Allowlists and VPCs | Allowlist and VPC path together, through allow_hosts, allow_ips, vpc_uuid, and subnet_uuid. |
The omitted and scalar forms mean the same thing. Writing unrestricted explicitly is worth doing when you want a reader of the spec to see that the choice was deliberate.
What the Platform Adds
Once the allowlist is on, the platform merges in the hosts a session cannot function without, so a short allowlist does not break the basics:
- Platform hosts, currently
api.github.com,github.com, andregistry.digitalocean.com. - Hosts the adapter requires. A
codex-agentapisession gets the OpenAI endpoints it depends on. - The model inference endpoints configured for the session.
Omitting egress never adds these, because an unset field must not restrict a session that would otherwise be unrestricted. There is nothing to add to when there is no allowlist.
One exception is worth knowing. An allowlist built only from allow_ips does not pull in the platform hosts, since injecting hostnames into an IP-only request would invent destinations you never named. That allowlist is still on, so hostname traffic, including GitHub and model endpoints, is denied. Session creation returns a warning when it sees this. Pair allow_ips with allow_hosts when you need both.
Allowlists and VPCs
The object form separates two independent choices: which destinations are allowed, and which path the traffic takes out.
egress:
allow_hosts: [api.github.com, mcp.example.com]
allow_ips: ["203.0.113.10"]
vpc_uuid: 01901234-5678-7890-abcd-ef0123456789allow_hosts and allow_ips are the destination allowlist. allow_ips takes exact IPv4 or IPv6 literals rather than CIDR ranges, and it matches only traffic addressed to a literal IP. A hostname that resolves to an allowed IP is still denied unless that hostname also appears in allow_hosts.
vpc_uuid and subnet_uuid choose the path. Without them, traffic leaves through public NAT. With them, it leaves through your VPC. An object that sets only vpc_uuid and no allowlist gives you the VPC path with unrestricted destinations. Adding allow_hosts turns the allowlist on as well.
Your team must own the VPC and it must be in a region the service runs in, both checked when the session is created. VPC attachment cannot change afterward, so a session cannot be moved onto or off a VPC once it exists. Setting subnet_uuid without vpc_uuid is rejected.
Deny All Egress
Deny-all applies when you write an allowlist field and every list you write is empty. Write it as egress: [], or as an object where every written allow_hosts or allow_ips list is empty. The API also accepts the scalar egress: none, but current doctl releases reject that spelling during client-side validation, so use the empty list.
An empty list on one field with entries on the other is not deny-all. For example, allow_hosts: [] with allow_ips: ["203.0.113.10"] is an IP-only allowlist.
An empty list is not the same as an omitted field. Omitting egress leaves the session unrestricted; writing egress: [] closes it.
Under deny-all, every public destination is blocked. A few platform paths stay open so the session can still run: workspace file transfer, the platform inference endpoint, and Action Gateway. These are not hosts merged into an allowlist. Deny-all merges nothing, so hosts such as github.com that the platform would merge into a normal allowlist are denied like any other public destination.
Two combinations are rejected at session create. Deny-all cannot set vpc_uuid or subnet_uuid: drop the empty list for VPC egress with no allowlist, or drop the VPC for deny-all. Deny-all also cannot carry scoped secrets, because credential injection needs the bound host to be reachable. List the host in an allowlist, or remove the secret.
Requesting Hosts
When you do set an allowlist, declare every host the agent needs: package registries the build pulls from, your own APIs, and self-hosted services.
egress:
- registry.npmjs.org
- api.internal.example.comSome package managers download from more than one host. pip reads package indexes from pypi.org and downloads the packages themselves from files.pythonhosted.org, so allow both.
The most common omission is an MCP server. Attaching one under tools does not reliably allowlist its host, so list that host under egress as well.
Tools reached through Action Gateway are the exception that needs no entry. The gateway calls the provider from its own infrastructure rather than from your sandbox, so the provider’s host never appears in your allowlist.
A connection to a host outside an active allowlist fails inside the sandbox, and the error you see depends on the client. An HTTPS request is refused at the egress proxy, which curl reports as CONNECT tunnel failed, response 404. Package managers can report the same refusal as a missing package: pip can print only No matching distribution found, and npm can return an E404 error as if the package does not exist. A plain HTTP request returns 403, and a connection made directly to an IP address is reset. None of these errors names the blocked host or the egress policy, so a missing host can look like an outage, a bad URL, or a package that was never published. When a build fails fetching a dependency, an absent egress entry is the first thing to check.
Egress Requests Are Narrowed, Never Widened
Everything in an environment spec is a request. The platform resolves the final policy by combining your request with team and platform policy, and that resolution can only narrow the result. A host your team’s policy prohibits stays blocked no matter what the spec asks for.
The same principle governs the relationship between egress and permissions. The permissions.network block restricts what the agent may reach within whatever egress allows. It is a second, tighter gate, not an alternative route to a host egress excludes. See Permission Policies.
Traffic to Third-Party Services
Outbound HTTPS to a service you declared travels as ordinary TLS. DigitalOcean does not terminate it, inspect it, or add identity to it. A third-party MCP server therefore cannot tell which session called it from the connection alone, and must rely on the credential you configured.
Requests to DigitalOcean-operated endpoints are treated differently. The platform stamps session identity onto them at the network boundary and overwrites any values the sandbox tried to set, so an agent cannot present itself as a different session.
Inbound Connections
Egress covers outbound traffic. Sandboxes accept no inbound connections from the internet, and a service the agent starts is not reachable by URL. To use one, tunnel to it with port forwarding, which opens a local port on your machine rather than exposing the sandbox.