# sandboxd: a self-hosted AI app builder that runs coding agents in Docker sandboxes

> sandboxd is an MIT-licensed Go control plane that turns a prompt into a running app inside an isolated container on your own server, with Traefik routing each sandbox to a preview URL. It is for engineers who want the Lovable-style build loop without handing over the infrastructure.

**tastyeffectco/sandboxd** — Open-source, self-hosted AI app builder, an agent builds real apps in isolated sandboxes on your own server, each live at a preview URL. Self-host in one command. MIT.

- Repository: https://github.com/tastyeffectco/sandboxd
- Website: https://sandboxd.io/
- Stars: 952 · Forks: 62
- Language: Go
- License: MIT
- Published: 2026-08-08 · Updated: 2026-08-18 · Language: en
- Canonical page: https://hysenlabs.com/projects/tastyeffectco-sandboxd

## The gap sandboxd fills: prompt-to-app without giving up the box

Hosted builders such as Lovable, Bolt, v0 and Replit all answer the same request: type a description, get a working site at a link. The README frames sandboxd as the open-source engine behind that pattern, with the qualifier that it runs on your own server. The audience is therefore narrow and specific. It is not aimed at someone who wants to describe an app and never think about a machine again. It is aimed at the engineer who wants that loop, but wants the containers, the code and the data to stay on infrastructure they own.

The README also positions the project against the usual orchestration stack. Under the hood it is described as one Go program driving Docker, with Traefik for URLs and SQLite for state, and explicitly no Kubernetes, no separate database and no queue. That is a deliberate reduction in moving parts, and it is the most interesting design decision in the repository. It means the operational surface is a single compose file and a host Docker daemon, not a cluster. It also means the scaling ceiling is one machine, which the README acknowledges indirectly by noting that idle sandboxes sleep and wake on demand so that one ordinary box holds many apps instead of a VM each.

## How the control plane, Traefik and per-sandbox containers fit together

The docker-compose.yml in the repository is unusually well commented, and it is the clearest description of the architecture available. Two long-running services are defined. traefik is the edge router; the compose comments state that it turns each sandbox's labels into an HTTP or HTTPS preview route and wakes stopped sandboxes on demand. sandboxd is the control plane; it creates, stops and destroys sandbox containers by shelling out to the host Docker daemon over the mounted socket, stores state in SQLite, and runs the idle and pressure reapers plus the wake path.

The important detail is what is not in the compose file. Per-sandbox containers are not services. They are launched at runtime from a prebuilt image referenced as ${SANDBOXD_IMAGE}, and they join the same network, ${SANDBOXD_NETWORK}, so Traefik can route to them. The network is given an explicit name in the compose file precisely so that the literal string also works as the --network value sandboxd passes to docker run. That is the whole data flow: an HTTP call arrives at the control plane, the control plane starts a container with the right labels, Traefik reads those labels and publishes a route, and the container's port becomes reachable at a hostname derived from the sandbox id.

The compose file also documents a namespace choice worth reading before deployment. Both traefik and sandboxd run with userns_mode set to host, because they need host-level access to the Docker socket and, for sandboxd, to the data directory that sandboxes bind-mount. Per-sandbox containers are deliberately not listed, so they keep the daemon's default isolation. On a daemon without userns-remap this is a no-op; on one with it enabled it keeps the two infrastructure containers working. Whether that trade-off is acceptable is a judgement about your host, and the file states the reasoning rather than hiding it.

## Installing sandboxd and building a first app from the API

The README states the prerequisites plainly: Docker plus the Compose plugin, and git, on Linux. macOS through Docker Desktop is described as best-effort. The project runs natively on amd64 and arm64, including Apple Silicon Macs and arm64 Linux hosts such as AWS Graviton, and the README says every image builds from multi-arch bases with no cross-compilation. Installation is a single line:

```bash
curl -fsSL https://raw.githubusercontent.com/tastyeffectco/sandboxd/main/install.sh | bash
```

According to the README, that script builds the images, starts the stack with the web console, and prints a console URL plus a generated login, with no password step. The console is served at http://console.localhost and the installer prints the credentials. If you lose them, the README points at ./console-login.sh to display the login again. The API listens on http://127.0.0.1:9090, and the README gives a health check for it:

```bash
curl http://127.0.0.1:9090/healthz
```

The README says that returns ok. If you do not want the console at all, the stack can run headless with SANDBOXD_CONSOLE=0 or the --no-console flag. Upgrades go through ./upgrade.sh, which the README says backs up your database first, health-checks the new version, and rolls back automatically if the check fails; ./upgrade.sh --check reports your current version.

For a scripted first use, the README gives this sequence. It connects an agent, creates a sandbox that exposes port 3000, and hands that sandbox a prompt:

```bash
API=http://127.0.0.1:9090
curl -s -XPOST $API/v1/agents/claude-code/api-key -d '{"api_key":"sk-ant-..."}'
ID=$(curl -s -XPOST $API/sandbox -d '{"ports":[3000]}' | sed -E 's/.*"id":"([^"]+)".*/\1/')
curl -s -XPOST $API/v1/sandboxes/$ID/tasks -d '{"prompt":"build a todo app on port 3000","agent":"opencode"}'
```

The README says the result appears at http://s-$ID-3000.preview.localhost. Note the two different agent names in that example: the key is registered for claude-code, while the task is dispatched to opencode. That is what the README shows, and it is worth reading the agent configuration docs before assuming they are interchangeable. The preview hostname works without DNS because browsers resolve *.localhost to 127.0.0.1, which is also why the default PREVIEW_DOMAIN is localhost.

## Preview routing, hostname styles and the TLS boundary

.env.example is the place where deployment decisions actually get made, and it is more opinionated than the README. PREVIEW_DOMAIN defaults to localhost, PREVIEW_TLS to false, and PREVIEW_ENTRYPOINT to web. The file explains that a public deployment needs a real wildcard domain and TLS.

The subtle part is the hostname layout, controlled by PREVIEW_HOST_STYLE. In nested mode, the default, previews look like s-<id>-<port>.preview.<domain> and the console sits at console.<domain>. In flat mode they become s-<id>-<port>[--tag].<domain>. The reason to choose flat is certificates: the file notes that one wildcard certificate *.<domain> covers every host in flat mode, while Cloudflare Universal SSL and similar cover only one level, which nested hostnames exceed. The tag lets several sandboxd instances share one domain, is only used with flat, and must match [a-z0-9][a-z0-9-]{0,30}. There is also a security consequence stated in the file: in flat mode each preview host authorises private previews separately, with no shared session cookie.

There is a second scenario the file handles carefully. If something else terminates TLS in front of sandboxd, such as Cloudflare Tunnel, Caddy, Nginx or a reverse tunnel, you keep PREVIEW_TLS=false so Traefik stays on plain HTTP, and set PREVIEW_URL_SCHEME=https so the console hands out https:// preview URLs it can iframe. The proxy in front must then listen on that scheme's default port, because with an explicit scheme HTTP_PORT is not part of the URL. These are the kinds of details that usually surface as a broken iframe after an hour of debugging; here they are written down next to the variables that cause them.

## Where sandboxd is the wrong tool

The strongest limitation is structural rather than a missing feature. The control plane needs the host Docker socket, and the compose file says so directly. Anything that can reach the control plane is, in effect, asking a process with socket-level access to start containers on your host. The project isolates the sandboxes from each other, but it does not isolate the control plane from the host. On a shared machine or one running other workloads, that is a real boundary to think about before installing.

The second limitation is scale. The README presents the no-Kubernetes, no-queue, SQLite design as a virtue, and for one server it is. It also means there is no path described for spreading sandboxes across several machines. If your requirement is horizontal scaling with a scheduler deciding placement, this is the wrong shape of system, and the README does not claim otherwise.

The third is platform support. Linux is the stated target; macOS via Docker Desktop is best-effort. If your team develops on Macs and expects the same experience as on the server, the README's own wording sets the expectation lower than you might assume. Finally, the console is described as a pure /v1 client and the engine as running perfectly headless without it, so a bug in the console is not a bug in the engine. That separation is good design, but it also means console issues and engine issues should be reported and debugged as different things.

## How it compares with running your own containers by hand

The obvious alternative is not another AI app builder. It is doing this yourself: a Docker host, a reverse proxy, and a script that starts a container per project. That approach gives you total control and no new dependency, and for a handful of apps it is genuinely less work than learning another control plane.

The difference is in what happens after the container starts. With hand-rolled containers, the preview URL, the lifecycle and the resource accounting are your problem. sandboxd's contribution is that Traefik derives routes from container labels, the control plane tracks state in SQLite, and idle sandboxes sleep and wake on demand, which is the mechanism that lets one box hold many apps. If you have ever written a cron job to stop idle development containers, that cron job is what this project replaces. If you have not, you may not need it yet.

The second alternative is the hosted builders the README names. Their advantage is that there is no server to operate and no upgrade script to run. The trade-off is exactly what the README is selling against: the infrastructure, the code and the data live with the vendor. Choosing between them is not a technical question about features. It is a question about whether you want to be the operator.

## Licence, upgrade cost and what the repository actually commits to

The project is MIT licensed, and the README states that self-hosting is MIT and free, forever, with the self-hosted engine described as always the full product. A managed cloud is mentioned as optional and there is a waitlist link, but nothing in the README suggests the self-hosted build is feature-limited. For a commercial deployment, MIT is permissive, and the usual obligations around retaining the licence text apply. That is a description of the licence, not legal advice; have your own counsel review anything you ship.

Upgrade cost is unusually well specified for a project at this stage. ./upgrade.sh is documented as backing up the database before upgrading, health-checking the new version, and rolling back automatically if the health check fails, and ./upgrade.sh --check reports the installed version. A rollback path that is described in the README is more than many self-hosted tools offer. The catch is that the backup covers the database, and the README does not say what happens to data inside sandbox containers during an upgrade, so treat container-local state as something to verify on your own deployment rather than assume.

On maintenance: the repository is not archived, and the most recent push recorded is 2026-08-28, which is recent enough that the project cannot be described as abandoned on that basis alone. The release list shows v0.3.20, v0.3.19 and v0.3.18 all dated 2026-08-28, which looks like a burst of patch releases on a single day rather than a steady cadence. The version number itself is the honest signal here: this is a 0.3.x project, and the README's own newsletter copy refers to a 1.0 release as still ahead.

## Conclusion

Adopt sandboxd if you want the prompt-to-preview loop running on hardware you control and you are comfortable giving a container the host Docker socket. Do not adopt it if you need a hosted service with no server to operate, or if your threat model forbids socket-level access. Before committing, verify two things on your own box: that 2 vCPU / 4 GB actually holds the number of concurrent sandboxes you expect once idle sleeping is configured, and that ./upgrade.sh --check reports the version you intend to run.

## FAQ

### What is sandboxd on a Mac?

sandboxd is an open-source, self-hosted AI app builder. The README states that macOS via Docker Desktop is best-effort, while Linux with Docker and the Compose plugin is the supported target, and that the project runs natively on amd64 and arm64 including Apple Silicon Macs.

### What does being sandboxed mean in the context of sandboxd?

In this project, a sandbox is a private, isolated container with its own filesystem and limits, in which a coding agent runs against your prompt. The README describes each sandbox as live at its own preview URL, with idle sandboxes sleeping and waking on demand.

### How do I install sandboxd?

The README gives a one-line install using curl against install.sh on the main branch, which builds the images, starts the stack with the web console, and prints a console URL plus a generated login. The prerequisites are Docker with the Compose plugin and git on Linux.

### Does sandboxd need Kubernetes or a separate database?

No. The README states that under the hood it is one Go program driving Docker, with Traefik for URLs and SQLite for state, and explicitly no Kubernetes, no separate database and no queue.

### Can I run sandboxd without the web console?

Yes. The README states the engine runs headless with SANDBOXD_CONSOLE=0 or the --no-console flag, and describes the console as a pure /v1 client that the engine does not depend on.

## Sources

- [Official documentation](https://sandboxd.io/)
- [Official README](https://github.com/tastyeffectco/sandboxd#readme)
- [Project repository](https://github.com/tastyeffectco/sandboxd)
- [Release notes](https://github.com/tastyeffectco/sandboxd/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/tastyeffectco-sandboxd
