| Name | Last commit date | |
|---|---|---|
| .. | ||
| scripts | ||
| src | ||
| web | ||
| .env.example | ||
| CHANGELOG.md | ||
| docker-compose.yml | ||
| Dockerfile | ||
| executor.config.ts | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| tsr.routes.ts | ||
| vite.config.ts | ||
| vitest.config.ts | ||
Self-hosted Executor
The single-container, self-hostable Executor server: the typed API, the MCP server, Better Auth (cookie / bearer / API-key + MCP OAuth), QuickJS code execution, and the web UI — all in one process over a libSQL (SQLite) file. No external database, worker, or proxy.
Run it
Using the published image:
docker run -d \
--name executor-selfhost \
-p 4788:4788 \
-v executor-data:/data \
ghcr.io/usefulsoftwareco/executor-selfhost:latest
Or build from a repository clone:
# From this directory:
docker compose up -d --build
# then open http://localhost:4788 and create the admin account
No configuration is required. A fresh instance shows a setup screen; the first person to create an account becomes the owner. After that, people join via single-use invite links you mint from the Admin page, and self-service signup is closed.
See .env.example for optional settings (most importantly
EXECUTOR_WEB_BASE_URL behind a domain / TLS) and the full
Self-Hosting guide for first-run, inviting
people, backups, reverse-proxy setup, and upgrades.
Develop
bun run build # build the SPA (regenerates the route tree)
bun run src/serve.ts # serve the built app
bun run --filter @executor-js/host-selfhost test # the test suite
Layout
src/
app.ts the ExecutorApp.make composition root
serve.ts the Bun server entry
config.ts env + zero-config secret/key persistence
auth/ Better Auth wiring, the signup gate, invite codes, seed
account/ the AccountProvider seam (members/roles via the org plugin)
admin/ the invite-code admin HttpApi
system/ public /api/health + /api/setup-status
db/ · mcp/ · execution.ts · plugins.ts · observability.ts
web/ the TanStack Router SPA (setup, login, join, admin, …)
Container permissions when upgrading
The container runs as UID/GID 65532:65532. New Docker named volumes inherit
that ownership from the image. Existing volumes created by a root-running
release must be backed up and assigned to this UID/GID before the first start
of the new image. Bind-mounted data directories need the same write access.
Stop the old container before changing data-directory ownership. Verify the exact volume or bind-mount path, make a backup, and change ownership only within that data directory. The application does not change existing volume ownership automatically. Keep the previous image and backup available for rollback.
The Docker build and runtime base images are pinned by digest. Update the tag and digest together when applying upstream security updates.