Skip to content
Guides · Who it's for

How Do I Give Every Student in a Class Their Own Cloud Sandbox?

Updated October 27, 2026 .md

Short answer: Build the lab once in a sandbox and save it as a named snapshot. Before class, create one small --no-env sandbox per student from it, with a TTL and an idle timeout, and send each student their own private code-server URL or SSH access. Delete them all at term end. A 30-student, 2-hour lab on small costs $1.08.

This page is for instructors running a course, a bootcamp or a one-day workshop. Students don't need a Sandboxer account, a card or anything installed: you run the sandboxes, and each student gets a link. Everything in the end-to-end section ran on prod on 2026-10-27. For a weekend event with teams, see Sandboxes for hackathons; for a vendor workshop or runnable docs, see Sandboxes for DevRel.

The decisions, and what to use for each#

Decision Short answer Sandboxer feature Worked guide
One lab image for everyone Install the tools and the starter files once, then save the result under a versioned name sbx snapshot <id> lab01-v1, sbx new --from lab01-v1 Turn a sandbox into a reusable template
How students get in A private hosted code-server or JupyterLab URL for browser-only students; SSH with their own key for students with a terminal sbx host <id> 8080, POST /sandboxes/{id}/sshkey code-server, JupyterLab, SSH from VS Code
Keeping your account out of student hands Create every student sandbox with --no-env: none of your secrets inside, and its built-in token can only read that sandbox and host its ports sbx new --no-env Environment variables inside a sandbox
Bounding the cost per student Small size, a TTL as long as the session, an idle timeout so breaks cost nothing --type small --ttl 7200 --idle 15 Idle auto-pause and wake on use, Cap what sandboxes can cost
Starting 30 at once Your plan's starts-per-minute limit sets the pace; start early sbx limits --json Rate limits and concurrency caps
End of term Copy out what you grade, then delete every sandbox and the snapshot sbx scp, sbx delete <id> --yes, sbx snapshot rm Clean up orphaned sandboxes

You run the sandboxes, or students join an organization?#

There are two ways to set up a class. For most classes, you run the sandboxes.

You create every sandbox (recommended) Students are members of your organization
Student needs A link (or an SSH key) A Sandboxer sign-in
Who pays Your wallet The organization's wallet, per seat: plan price × seats (Organizations)
Can students see each other's sandboxes? No. Each student has only their own URL or key Yes. Every member sees the organization's sandboxes, and --no-env ones are usable by every member from the start
Can they start from your lab snapshot? You do it for them Not by name: named snapshots are visible only to their creator. You'd give them a share code (sbx snapshot share)
Per-student cap Size × TTL bounds each sandbox; there is no per-student dollar setting An owner can cap a member's machine time with PATCH /orgs/{orgId}/members/{userId} and usageCapSeconds, API only. Over the cap, new starts get 402 team_member_cap_reached; running sandboxes keep running

An organization fits the staff, not the students. Put yourself and your teaching assistants in one: every member can see the class sandboxes your organization pays for, and can open and exec into the --no-env ones to help a stuck student. Use the owner-only member caps for the TAs who start their own sandboxes. We didn't create an organization or set a cap for this guide; that column is from the organization docs and the API code.

Getting students in#

Route Student needs What we measured on prod
Private code-server URL A browser 401 without the token. The token URL sets a cookie and redirects, then the editor page loads with 200. One student's token on another student's URL got 401
Private JupyterLab URL A browser Not run here; the JupyterLab guide runs the same pattern on port 8888
SSH with the student's own key An SSH client (or VS Code Remote-SSH) The student's public key is authorized through the API, then plain ssh to the sandbox's IPv4 sshEndpoint connects in 1.78 s. The same key against the other student's sandbox got Permission denied (publickey)
sbx ssh with a per-student API key The sbx CLI Not run. API keys can be limited to one sandbox, a list of actions and an expiry, for example --sandbox <id> --actions sandbox.read,ssh --ttl 120d. Never give students your own key

sbx handoff isn't a student-access tool. It moves your own Claude Code or Codex session and repo from your laptop into a sandbox (MCP docs).

One lab, two students, end to end#

We built a lab image with code-server and a Python exercise, saved it, started two student sandboxes from it, checked each one and deleted everything. Sizes were all small. Private URLs and tokens are replaced with <token>.

1. Build the lab image#

sbx new --name classroom-1 --type small --no-env --ttl 3600
id=sb_11ub6acn7l

sbx exec $id --timeout 300 "curl -fsSL https://code-server.dev/install.sh | sh >/tmp/cs.log 2>&1; code-server --version"
4.143.1 22ddf60dbc52f164cff13620ab515086dd08c90a with Code 1.143.1

The code-server install script took 6.8 s. Then the lab itself: a starter file with a TODO, the tests that grade it, a venv with pinned pytest, and code-server as a systemd service that starts on every boot. We piped this script in with sbx ssh $id -- bash -s < lab.sh:

set -e
mkdir -p ~/lab01 && cd ~/lab01
cat > stats.py <<'P'
def mean(xs):
    """Return the arithmetic mean of xs. TODO: handle an empty list."""
    raise NotImplementedError
P
cat > test_stats.py <<'P'
import pytest
from stats import mean

def test_mean():
    assert mean([1, 2, 3, 4]) == 2.5

def test_empty():
    with pytest.raises(ValueError):
        mean([])
P
uv venv -q --seed ~/lab01/.venv --python 3.12
VIRTUAL_ENV=~/lab01/.venv uv pip install -q pytest==8.3.3
echo 'source ~/lab01/.venv/bin/activate' >> ~/.bashrc
cat > ~/.config/code-server/config.yaml <<C
bind-addr: 0.0.0.0:8080
auth: none
cert: false
C
sudo systemctl enable --now code-server@user
sleep 3
systemctl is-active code-server@user
curl -s -o /dev/null -w "local %{http_code}\n" localhost:8080/
cd ~/lab01 && ~/lab01/.venv/bin/python -m pytest -q -p no:cacheprovider --tb=no | tail -1
active
local 200
2 failed in 0.01s

Both tests fail before any student touches the code, so the grader works. auth: none is safe only because the hosted URL's token guards the editor; never host this port with --public.

2. Save it and delete the builder#

sbx --json snapshot $id classroom-lab01-v1
sbx delete $id --yes
{"ok":true,"type":"snapshot.named.info","snapshot":{"name":"classroom-lab01-v1","snapshotId":"69994b2c-…","sourceSandboxId":"sb_11ub6acn7l","status":"ready","type":"small",…}}

The builder lived about 40 seconds, create to delete.

3. Start a student's sandbox and hand it over#

These are the commands we ran for each student. For a class, run them in a loop over your roster and write each student's id and URL to a file you keep private.

sbx --json new --from classroom-lab01-v1 --name classroom-2 --type small --no-env --ttl 7200 --idle 15
sbx host sb_4ac1yp0yd9 8080 --title "Lab 1"
{"id":"sb_4ac1yp0yd9","name":"classroom-2","type":"small","noEnv":true,"ttlSeconds":7200,"idleStopMinutes":15,"sshEndpoint":"62.238.91.250:22021","subdomain":"exact-epic-salmon"}
https://exact-epic-salmon-8080.box.sandboxer.com?_token=<token>

That URL is what the student gets. Checking it from outside:

curl -s -o /dev/null -w '%{http_code}\n' https://exact-epic-salmon-8080.box.sandboxer.com/
curl -s -c jar -o /dev/null -w '%{http_code} %{redirect_url}\n' "https://exact-epic-salmon-8080.box.sandboxer.com?_token=<token>"
curl -s -b jar -w '%{http_code}\n' -o page.html https://exact-epic-salmon-8080.box.sandboxer.com/
401
302 https://exact-epic-salmon-8080.box.sandboxer.com/
200

The page that came back was code-server's workbench. The service started by itself in the new sandbox, because the snapshot holds the enabled systemd unit, and the lab files and venv were there:

sbx exec sb_4ac1yp0yd9 "systemctl is-active code-server@user; ls ~/lab01; cd ~/lab01 && .venv/bin/python -m pytest -q -p no:cacheprovider --tb=no | tail -1"
active
__pycache__
README.md
stats.py
test_stats.py
2 failed in 0.00s

For a student who wants a terminal instead, authorize their public key (never a private key) and give them the sshEndpoint:

curl -sS -X POST https://sandboxer.com/api/v1/sandboxes/sb_4ac1yp0yd9/sshkey \
  -H "Authorization: Bearer $SANDBOXER_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"key\":\"$(cat student1_key.pub)\"}"
# the student runs:
ssh -i student1_key -p 22021 user@62.238.91.250 'hostname; cd lab01 && .venv/bin/python -m pytest -q -p no:cacheprovider --tb=no | tail -1'
exact-epic-salmon
2 failed in 0.00s

4. The second student#

The second sandbox, sb_15e0iaw16a, came from the same snapshot and checked out the same way: its own subdomain (tiny-cyan-sierra), 401 without its token, 200 with it, code-server active, the same two failing tests. Two isolation checks:

  • The first student's token on the second student's URL: 401.
  • The first student's SSH key against the second student's sandbox: Permission denied (publickey). Each sandbox also had its own SSH host key.

We ran the two students one after another, not side by side, to keep our test small. In class they run side by side.

5. Clean up#

sbx delete sb_4ac1yp0yd9 --yes
sbx delete sb_15e0iaw16a --yes
sbx snapshot rm classroom-lab01-v1
sbx --json info sb_4ac1yp0yd9
{"event":"error","error":"Sandbox not found","code":"not_found","status":404,…}

After the delete, the first student's old private URL answered 404. All three sandbox ids returned 404, and sbx list showed no classroom sandboxes.

Timings#

Single runs, timed from our client, so they include CLI and network time:

Step Time
Create the builder (small) 3.20 s to ready
Install code-server 6.8 s
Lab files, venv, pytest, service 8.6 s (3 s of it a deliberate wait)
Save the named snapshot 2.13 s
Student sandbox from the snapshot 3.20 s to ready, both times
sbx host 0.30 s
Editor page through the private URL, after host 0.67 s and 0.72 s, first try
SSH with the student's own key 1.78 s
Delete a sandbox 2.13 s

What a 30-student, 2-hour lab costs#

From pricing: small (2 vCPU, 4 GB) costs $0.018/hour and default (4 vCPU, 8 GB) $0.036/hour, billed per second while a sandbox runs.

Scenario Arithmetic Cost
30 students, 2 hours, small, all running the whole time 30 × 2 h × $0.018 $1.08
Same on default 30 × 2 h × $0.036 $2.16
12 weekly labs of that size in a term, small 12 × $1.08 $12.96
Building the image About 40 s of small Under $0.001

--ttl 7200 makes each run stop itself 2 hours after it starts. In the API code, a wake from idle pause sets a new deadline 2 hours from the wake, so a sandbox a student opens again after class runs again. To make $1.08 a real ceiling, delete the sandboxes when the session ends, or use --idle 0 so they don't wake. Idle pause lowers the bill during class, because a paused sandbox bills no compute. The $20 plan includes $20 of machine time a month, which covers the 12-lab term. Check each student's real number with sbx usage <id>: two seconds into its life, our second student's sandbox showed "seconds":2,"dollars":0.00002.

Limits that matter for a class#

Limit Free trial $20 plan $100 plan
Sandboxes running at once 2 100 300
Starts per minute / hour / day 12 / 60 / 200 30 / 210 / 840
Longest TTL 2 hours 30 days 30 days
  • The trial can't run a class. It allows 2 sandboxes at once.
  • Start early. On the $20 plan, 30 creates spread over three minutes (12, 12, then 6). Creates, resumes and forks each count as one start, and in the API code a wake from idle pause is recorded as a start too. If all 30 sandboxes pause over a break, the 30 wakes after it are another 30 starts, which reaches the 60-an-hour limit. For a 2-hour session, either create the sandboxes more than an hour ahead, set --idle longer than your breaks, or use --idle 0 and let the TTL stop them.
  • Storage. Stopped sandboxes and named snapshots count toward your plan's included storage (50 GB on $20). At the limit, new sandboxes get 402 storage_limit_reached. Delete last term's sandboxes and old lab versions.

Honest limits#

  • No per-student dollar cap when you run the sandboxes. Size × TTL bounds each one; the member usage cap applies only to organization members starting their own sandboxes, and only through the API.
  • The private URL is a password. Anyone a student forwards it to gets in. To revoke one, the documented path is switching the port to --public and back to --private, which mints a new token but leaves it open for that moment; replacing the sandbox is the safer fix.
  • Students have root in their VM. That's the point of a VM per student, and the isolation is between VMs. Inside their own sandbox they can install anything and reach the internet.
  • SSH endpoint ports get reused. Our second student's sandbox got the same IPv4 port as the first, deleted one, with a different host key. A student whose old sandbox was replaced sees a host-key warning until they remove the old known_hosts line.
  • Where it runs. The sandbox we checked reported region: "eu-central". Sandboxer has two regions, us-east (Ashburn, Virginia) and eu-central (Helsinki, Finland); see regions. Students far from both will feel the round trip in a terminal more than in a browser editor.
  • No GPU. Sizes are CPU-only, up to xlarge (16 vCPU, 32 GB, $100 plan or higher). A machine-learning class can teach on small models, not train large ones.
  • Moving to a smaller size isn't supported yet. Pick the size in the snapshot you build; a new, smaller sandbox means copying the student's work over.

FAQ#

Do students need a Sandboxer account?#

No, not when you run the sandboxes. A student needs only their private URL, or an SSH client and the public key you authorized. They need an account only if they join your organization or start their own copy from a share code.

What happens to a student's work if their sandbox stops?#

Nothing is lost. Auto-stop and idle pause keep the disk and memory, and an idle-paused sandbox wakes on the next request to its private URL. A sandbox stopped by its TTL stays stopped until you run sbx resume <id>. Copy work you grade out with sbx scp <id>:/home/user/lab01 . before you delete.

How do I update the lab mid-term?#

Change a sandbox built from the current image, save it under a new name such as lab01-v2, and create new students' sandboxes from that. Sandboxes already running keep the version they started from; reusing a name replaces the snapshot.

Can teaching assistants get into a student's sandbox?#

Yes, if they are members of the organization that pays for the sandboxes: members can open and exec into --no-env organization sandboxes. Otherwise you exec into it yourself with sbx exec or sbx ssh.

More guides

Top AI sandbox platforms for code execution in 2026, ranked

For running AI-written code we rank Sandboxer, Freestyle and E2B first: each gives code its own VM with an API or SDK and can pause with memory. Pick Daytona for Windows or GPUs, Modal or Northflank for GPUs at scale, E2B or Northflank to run in your own cloud, and Vercel Sandbox if your app lives on Vercel.

Read the guide
Best sandboxes for running coding agents (Claude Code, Codex, Gemini CLI) in 2026, ranked

For coding agents we rank Sandboxer first: a full Ubuntu VM with Claude Code and Codex preinstalled, Docker, and stop-and-resume that keeps the agent's session in memory. Freestyle and Amp Orbs also keep memory across pauses. Pick E2B for prebuilt agent templates on open source, Docker Sandboxes for free local runs, and Codespaces if you live in GitHub.

Read the guide
Best Daytona sandbox alternatives in 2026, ranked

The best Daytona alternatives depend on why you're switching. For a full Linux microVM on every sandbox with memory-saving stop and Docker, we rank Sandboxer first. E2B suits open source and self-hosting, Modal massive short-job fleets, Northflank bring-your-own-cloud with GPUs, and Vercel Sandbox apps on Vercel.

Read the guide
Best E2B alternatives in 2026, ranked

If you're leaving E2B for longer sessions or a fuller machine, we rank Sandboxer first, then Freestyle: both give each sandbox a full Linux VM that keeps memory across pauses, with Docker and no short session cap. Pick Daytona for Windows or GPUs, Northflank for bring-your-own-cloud, and stay on E2B if you need open source.

Read the guide