Skip to main content
The defaults are tuned for the common case: running untrusted code that needs the internet. When your threat model is sharper, such as multi-tenant, credential-handling, or fully offline, tighten the knobs below. Each one is independent, so apply the ones that fit.

Turn the network off

If a workload doesn’t need the network, remove it. Nothing to filter is the strongest filter.
msb create python --name isolated --no-net

Deny egress by default

When a workload needs only a handful of destinations, switch from allow-public to deny-by-default and allow exactly what’s required. In any allow-by-default policy, remember to deny@meta so the cloud metadata service stays blocked.
msb create alpine --name restricted-worker \
  --net-default-egress deny \
  --net-rule "allow@public:tcp:443,allow@host:udp:53"
See Networking overview for the full rule syntax and SDK equivalents.

Drop in-guest privileges

Two independent steps, both worth taking for untrusted code:
  • Run the workload as a non-root user.
  • Apply the restricted security profile, which sets no_new_privs, drops the mount-admin capability, and forces nosuid,nodev on user mounts.
msb create python --name worker --user app --security restricted
The restricted profile is incompatible with workloads that need those capabilities, such as sudo or Docker-in-Docker. See Isolation boundary for the SDK forms.

Mount least privilege

  • Mount only the directories a workload actually needs.
  • Use read-only mounts wherever write isn’t required. Read-only is enforced host-side, so the guest can’t remount around it.
  • Avoid sharing a writable volume across sandboxes, since a shared volume is a deliberate isolation hole.
  • Prefer a recent Linux kernel for writable bind mounts, where openat2 containment is strongest.

Pin and trust your images

  • Pin images by digest, not a moving tag, for reproducibility.
  • Pull from registries you control or trust, because content is digest-verified but not signature-verified.

Handle credentials with secrets

  • Inject credentials with secrets and narrow allow lists, so a value only materializes at the destinations that need it.
  • Keep trust_host_cas off unless you’re behind a corporate MITM proxy.
  • For credentialed egress you want to inspect, enable TLS interception and pin DNS.

Bound resources and lifetime

  • Set vCPU and memory caps appropriate to the workload.
  • Set an idle timeout or a maximum duration so abandoned sandboxes get reclaimed.

Choosing a posture

Use caseNetworkIn-guest privilegeStorage
Trusted internal toolDefault public egressRoot is fineMounts as needed
Untrusted code / AI agent with internetDefault, or deny-by-default allowlistNon-root + restrictedRead-only mounts
Multi-tenant / per-userDeny-by-default allowlistNon-root + restrictedNo shared writable volumes, one sandbox per tenant
Credential-handling egressDeny-by-default + TLS interceptionNon-root + restrictedMinimal and read-only, with narrow secret allow lists
Fully offline / compute-only--no-netNon-root + restrictedRead-only or none

Where hardening stops

These knobs shrink the blast radius and tighten what a workload can reach. The boundary they sit on top of is still the hypervisor. See the security model overview for what that boundary does and doesn’t cover.