SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-13

How to Run sandboxd on Your Own VPS

Run sandboxd for your own VPS with pinned install, model keys, HTTPS preview URLs, 2 vCPU and 4 GB RAM guidance, plus stale sandbox cleanup.

Wetin sandboxd be, and wetin you gain if you run am by yourself

To self-host sandboxd, you need one Linux server wey get Docker and one domain name. You send prompt, coding agent build real application inside isolated container, and that application come up for im own preview URL. Prompt-to-app builders na the loudest hosted category for 2026, and sandboxd na the one wey dey run for your VPS, under MIT licence, with the generated code dey sit for your own disk.

The design small on purpose. One Go control plane dey drive Docker, Traefik v3 dey route every preview hostname, SQLite dey hold the state, and each app dey run inside one container. Kubernetes no dey, and separate database server no dey. Na why 2 vCPU box fit run am at all.

Four objects carry the whole model. An app na the durable project wey hold im name, git metadata, and secrets. A sandbox na the Docker container wey that app dey run inside, and one app fit point to one sandbox at a time. A workspace na the app files wey dey live for the host and survive the container. A task na one prompt wey you hand to the agent inside the sandbox. If you stop sandbox, e free memory and keep the files. If you destroy am, e throw the container away, and the app fit boot a new one.

sandboxd Dey different from Dify and OpenHands how?

People dey mix up these three because all of dem dey run LLM (large language model) for your server, but wetin dem produce different. Dify dey build LLM applications: chat interfaces, retrieval pipelines, and workflows wey dey call model anytime person use dem. Model na part of the finished product. OpenHands dey work on repository wey you already get: you point am to your code, and e go read files, run commands, and suggest changes. sandboxd dey start from nothing. E scaffolds project from preset, builds am inside fresh container, and gives you URL wey you fit check. Wetin come out na normal React or FastAPI application wey no need model to run.

So choose based on wetin you want for the end. sandboxd na for starting from one sentence and keeping the code afterwards. The other two na for when the repository or the model-powered product already dey exist.

The other difference na age, and na this one you suppose weigh before you build anything real on top of am.

ChartGitHub stars and forks, read from the GitHub API on 4 August 2026
The data behind this chart
[
  {
    "tool": "sandboxd",
    "github_stars": "875",
    "forks": "50"
  },
  {
    "tool": "OpenHands",
    "github_stars": "83,091",
    "forks": "10,711"
  },
  {
    "tool": "Dify",
    "github_stars": "151,320",
    "forks": "23,886"
  }
]

sandboxd get 875 stars, compared with 83,091 for OpenHands and 151,320 for Dify. Dem create the repository on 3 June 2026, so e don reach two months old as of August 2026, while OpenHands start from March 2024 and Dify from April 2023. Release v0.1.0 come out on 6 June 2026, and v0.3.6 come out on 1 August 2026. The project call itself beta and talk say 0.x releases fit break compatibility. Read those numbers as dependency risk, no be final judgement on quality: project wey don dey around for two months get only two months of other people finding and reporting bugs.

Wetin the server need, and wetin dey break when e short

The project talk say 2 vCPU and 4 GB of RAM dey enough to start. This correct for the control plane plus one small sandbox, but e no enough for two people wey dey build at the same time. Plan the memory in parts. Traefik and the Go control plane small. Each sandbox wey dey run get complete Node or Python toolchain, and the peak na an npm install followed by production build. Plan 8 GB for a box wey go keep some apps alive. Treat swap as safety net, no be capacity, because build wey dey use swap fit take minutes instead of seconds.

When memory finish, two different failures fit happen, and dem no look alike at all. Inside sandbox, the container go reach the hard --memory ceiling wey sandboxd set, and kernel go kill the biggest process. Because of this, build go die without useful message from the agent. docker ps -a go show exit code 137 for that container, and docker inspect on top am go report "OOMKilled": true. Node build wey die this way often print JavaScript heap out of memory first.

The second failure dey happen for host. sandboxd dey run pressure reaper wey dey stop sandboxes when host memory low. So for small box, sandbox fit disappear while you dey watch the preview. The files dey safe, and the next request to the preview URL go wake am, but task wey dey run when the container stop no go resume.

Disk na the quieter problem. Every app dey keep its own workspace for host, and JavaScript project dey carry node_modules tree wey fit reach hundreds of megabytes. Ten apps na several gigabytes of dependencies before you count the images. Start with 40 GB and monitor am:

docker system df
sudo du -sh /var/lib/sandboxed/workspaces

The default data directory na /var/lib/sandboxed, and e get the extra e. If you type /var/lib/sandboxd, you go get empty directory and five confusing minutes.

Install one pinned sandboxd release

Docker Engine with the Compose plugin, plus git, must dey for the box first. Install Docker for one VPS cover that side.

docker compose version
git --version

Both must print version. docker: 'compose' is not a docker command mean say you get the old standalone docker-compose binary, and the installer dey expect the v2 plugin.

The installer na shell script wey dem fetch through network, so read am before you run am, and pin the version.

curl -fsSL https://raw.githubusercontent.com/tastyeffectco/sandboxd/v0.3.6/install.sh -o install-sandboxd.sh
less install-sandboxd.sh
SANDBOXD_REF=v0.3.6 bash install-sandboxd.sh

SANDBOXD_REF na the git ref wey the installer check out into $HOME/.sandboxd/src, and e dey default to main. If you leave am unset, your install go be anything wey dem merge that morning. This matter for project wey release six versions for July 2026 alone. Pin am, then upgrade deliberately after you read the changelog.

The script clones the source, builds the images, starts the stack with docker compose up -d, and prints the console URL plus API token for the end. Save that token for safe place. Na the credential for API wey dey drive Docker as root.

curl http://127.0.0.1:9090/healthz

That go print ok when the control plane don come up. If e print nothing, the stack no start: run docker compose ps from ~/.sandboxd/src to see which service dey down, then docker compose logs sandboxd to see why.

Console reach di remote box

Traefik dey serve the console through HTTP_PORT, wey be 80 by default, for hostname http://console.localhost. Traefik dey route based on hostname, so if you put your server IP address for browser, e no match any rule and e return 404. Until you set real domain, forward the port and keep the hostname:

ssh -L 8080:127.0.0.1:80 you@your-vps

Then open http://console.localhost:8080 for your laptop. For Linux and macOS, any name wey end with .localhost dey resolve to 127.0.0.1, so the request go pass through the tunnel with the correct Host header. Set the console password when you visit am for the first time.

Give agent model

Two coding agents dey inside base image: OpenCode and Claude Code. SANDBOXD_DEFAULT_AGENT dey decide which one go run task wey no specify one, and e dey default to opencode. If no key connect at all, tasks go run with OpenCode Zen keyless free models. So your first build no go cost anything, and you fit test the whole loop before you spend money.

Connect your own key when you want stronger model. Keys dey go control plane, never sandbox: dem dey stored encrypted under data directory and credential proxy dey inject dem on the wire. So neither agent nor code wey e write fit read dem.

export API=http://127.0.0.1:9090
export SANDBOXD_TOKEN=sk_...                       # printed by the installer
export AUTH="Authorization: Bearer $SANDBOXD_TOKEN"

curl -s -XPOST $API/v1/agents/claude-code/api-key -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"api_key":"sk-ant-..."}'

Console dey do the same thing under Settings, AI Agents. E include guided OAuth flow if you want use Claude subscription instead of API key. Default model for each agent dey for the same panel, and one task fit override am.

Build one small app end to end

Make the app, boot its sandbox, then send one prompt. The ids go come back as JSON, and the quickstart uses sed to pull dem out, so you no need install jq.

APP=$(curl -s -XPOST $API/v1/apps -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"name":"todo","runtime_preset":"react-vite"}' \
  | sed -E 's/.*"id":"([^"]+)".*/\1/')

SB=$(curl -s -XPOST $API/v1/apps/$APP/sandbox -H "$AUTH" \
  -H 'content-type: application/json' -d '{"ports":[3000]}' \
  | sed -E 's/.*"id":"([^"]+)".*/\1/')

echo "app=$APP sandbox=$SB"

Both variables must contain an id. Empty $SB mean say the sandbox never boot. The common cause na say the base image still dey build, or the host no get enough memory. If you see 401 instead of an id, the bearer token no correct.

curl -s -XPOST $API/v1/sandboxes/$SB/tasks -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"prompt":"Add a todo list with a text input, an add button, and a delete button on each row. Keep the list in localStorage.","agent":"opencode"}'

The response get one task id. GET /v1/sandboxes/$SB/tasks/<task id> go return the result, while the /events path for the same task na live SSE (server sent events) stream of wetin the agent dey do. The console show the same stream like chat.

The app dey available for http://s-<sandbox id>-3000.preview.localhost, where 3000 na the port wey you request. If the sandbox dey sleep, the first request go reach Traefik catch-all. sandboxd go start the container, wait until the port answer, then serve one short warming page wey go refresh into your app. If preview no ever comot from that page, the process inside no dey listen on the port wey the app sandbox.yaml declare.

Put the previews for real domain with HTTPS

Every sandbox get im own hostname, so one wildcard DNS record fit cover all of dem. Point *.preview.yourdomain.com to the server IP address with an A record. Then set the preview variables for .env inside ~/.sandboxd/src:

PREVIEW_DOMAIN=yourdomain.com
PREVIEW_ENTRYPOINT=websecure
PREVIEW_TLS=true
SANDBOXD_API_AUTH_DISABLED=false

Traefik need the matching side: enable the websecure entrypoint for traefik/traefik.yml and add a certificate resolver. Use the DNS-01 challenge, because one wildcard certificate go cover every preview hostname. With HTTP-01, every new sandbox go need im own issuance, and busy afternoon of building fit quickly reach Let's Encrypt rate limits. Wildcard certificates through the DNS-01 challenge explain the DNS side of this.

cd ~/.sandboxd/src
docker compose up -d

Preview URLs go become https://s-<id>-3000.preview.yourdomain.com. Open 80 and 443 for the firewall, and leave 9090 closed to the public: see basic ufw firewall rules. Remember say anybody wey fit guess a preview hostname fit load the app, so treat previews as public.

Where generated code dey stay, and fit you export am?

For the host, e dey under the data directory. Each workspace na ordinary directory for /var/lib/sandboxed/workspaces/<id>/, wey dem bind mount enter the container, and app files dey for /home/sandbox/workspace/app inside the sandbox. Control plane state na one SQLite file for state/sandboxd.db, while encrypted agent credentials dey for agent-auth/. Nothing dey hidden inside container layer, so backup na to copy the directory plus that database file. restic backups for VPS go handle both.

sudo ls /var/lib/sandboxed/workspaces
sudo du -sh /var/lib/sandboxed/workspaces/*

Git export dey built in; dem no bolt am on later. The API get status and diff for reading, then commit and push:

curl -s $API/v1/apps/$APP/git/status -H "$AUTH"

curl -s -XPOST $API/v1/apps/$APP/git/commit -H "$AUTH" \
  -H 'content-type: application/json' \
  -d '{"message":"todo list, first pass"}'

curl -s -XPOST $API/v1/apps/$APP/git/push -H "$AUTH" \
  -H 'content-type: application/json' -d '{"branch":"main"}'

Private remote need personal access token. Set am once for the console under Settings, Git credentials. The system store am encrypted and e stay outside the sandbox, so the agent no fit read am or push with am behind your back. Push early and often. Until you do am, workspace directory na the only copy of the code, and DELETE /v1/apps/<id> go remove am with no second chance.

Wetin one build cost for model tokens?

sandboxd no dey measure how much you spend, so na the number for your provider console matter. The free OpenCode Zen models no cost anything, but dem slower and weaker pass paid model. You go notice am as more correction rounds for anything wey pass small test app.

The bill pattern follow how agent loop dey work. Every turn dey resend the context wey e need, so cost dey follow number of turns, no be number of apps. One prompt wey work correct na cheap. Fifteen rounds of “now fix the spacing” for project wey get fifty files no cheap, because file contents dey go along every time. Input and output tokens get different prices, and how much coding agent cost per session show the realistic range. Set hard spend limit for provider before you give control to loop wey go run unattended.

Stale sandbox dem cleanup

Idle reaper dey stop any sandbox wey don idle pass SANDBOXD_IDLE_THRESHOLD_SECONDS. Default na 2100 seconds, or 35 minutes. E dey return the RAM but keep the files, and the next request to the preview URL go wake the container. Reduce this value for small box, because 35 minutes of idle containers mean 35 minutes of memory wey you no fit use.

Stopping no be deleting, and na here disks fit quietly full. Stopped sandbox still get im workspace and container. To remove sandbox but keep the app na DELETE for the sandbox. This one go remove the container and workspace together with am. To remove the app go remove everything permanently.

curl -s -XPOST $API/v1/sandboxes/$SB/stop -H "$AUTH"     # frees RAM, keeps files
curl -s -XDELETE $API/v1/sandboxes/$SB -H "$AUTH"        # container and workspace gone
curl -s -XDELETE $API/v1/apps/$APP -H "$AUTH"            # app and everything under it

After some weeks of experiments, docker system df go show more reclaimable image space than you expect, because every app wey pull im own toolchain leave layers behind. docker image prune go clear the dangling ones. Check GET /v1/apps first, because image wey sleeping sandbox still dey reference no be garbage.

Wetin the container boundary dey give you and wetin e no dey give you

Each sandbox dey run as an unprivileged user with read only root filesystem, all Linux capabilities dropped, no-new-privileges set, memory ceiling and process limit. The project dey clear about this limit: shared kernel Linux container na strong isolation boundary but weak security boundary. Kernel bug fit compromise the host.

Two facts need action. Network egress from sandbox dey open for the self-hosted build, so generated code fit reach internet, your local network and cloud metadata endpoints. An nftables egress subsystem dey for the source but dem compile am off for the portable Docker Compose build. This mean say your host firewall must provide the limits. And the control plane API practically be host root because e dey drive Docker socket. E bind to 127.0.0.1:9090 by default, SANDBOXD_API_AUTH_DISABLED must remain false, and you must never publish am to internet.

If you plan make other people send prompts to your box, this model no strong enough by itself. The project point to gVisor with SANDBOXD_RUNTIME=runsc. This one put userspace kernel between the sandbox and host, but syscall-heavy work fit slow down roughly 1.7 to 4 times. The stronger answer na one machine for each tenant. Na the same reason behind running coding agents inside disposable VM.

You suppose build on project wey don old two months?

For personal build box, yes, with the obvious precautions: pin SANDBOXD_REF, back up /var/lib/sandboxed, and push every app wey matter to you go git remote. For anything wey customer go touch, wait for 1.0 or budget for breakage, because the maintainers talk am plainly say 0.x fit change under you. The maintainers still dey sell managed install for 79 dollars every month as of August 2026. This one dey useful to know when you dey judge whether the project get reason to continue existing.

The reason the risk dey tolerable na the output. sandboxd dey produce ordinary application inside ordinary git repository. So if the project stop moving, you still keep the code and na only the wrapper you lose. This position better well-well pass hosted builder wey own your project. For wider view of wetin deserve place for your server this year, see wetin worth self-hosting for 2026.

FAQ

Wetin be the minimum server specs for sandboxd?

The project talk say 2 vCPU and 4 GB of RAM dey enough to start. This one cover the control plane, Traefik, and one small sandbox. Use 8 GB and 40 GB of disk if you wan keep several apps alive at once, because every sandbox wey dey run get complete Node or Python toolchain, and every workspace keep its own dependency tree for disk. When the host memory no dey enough, sandboxd's pressure reaper go stop sandboxes to free memory. If build pass the memory ceiling for its container, kernel go kill am: docker ps -a go show exit code 137 for am.

How sandboxd different from Dify or OpenHands?

Dem dey produce different artifacts. Dify dey build applications wey call model when dem dey run, like chat interfaces and retrieval pipelines. OpenHands dey edit repository wey you already get. E dey run commands and suggest changes to existing code. sandboxd dey create completely new project from prompt, build am inside its own container, and serve am for preview URL. The result na normal web application wey no need model to run.

Where code wey the agent write dey actually live?

E dey for host filesystem, no be inside container image. Every app get directory for /var/lib/sandboxed/workspaces/<id>/. The directory dey bind mounted into its sandbox, and the files dey show for /home/sandbox/workspace/app inside. Control plane state na one SQLite file under state/ for the same data directory. You fit commit and push go git remote from the console's Git tab, or through /v1/apps/<id>/git/commit and /git/push endpoints. Token for private remotes dey stored encrypted by the control plane, instead of handing am to the sandbox.

E safe to expose sandboxd to the internet?

Expose the preview URLs and the console, but never expose the control plane API. That API dey control Docker for the host, so e get the same power as root. Na why e dey bind to 127.0.0.1:9090 by default. Sandboxes still get open network egress for the self-hosted build. This mean code wey the agent write fit reach your local network and cloud metadata endpoints. Add host firewall rules if the box get other systems wey you need protect. For prompts from people wey you no trust, use one host for each tenant instead of relying on the container boundary.

#sandboxd#ai-agents#self-hosted#app-builder#docker