SSD Nodes Learn Hosting plans →
Посібники Matt ConnorВід Matt Connor · Оновлено 2026-08-13

Як розгорнути sandboxd на власному VPS

Запустіть sandboxd на власному VPS: зафіксуйте версію, додайте ключі моделей, налаштуйте HTTPS preview URL, перевірте RAM і диск та очистьте старі sandbox.

Що таке sandboxd і що ви отримуєте, запустивши його самостійно

Щоб самостійно розгорнути sandboxd, потрібен один Linux-сервер із Docker і доменним ім’ям. Ви надсилаєте prompt, coding agent створює повноцінний застосунок в ізольованому контейнері, і цей застосунок стає доступним за власною preview URL. Prompt-to-app builders — найпомітніша категорія hosted-сервісів у 2026 році, а sandboxd працює на вашому VPS за ліцензією MIT, і згенерований код зберігається на вашому диску.

Архітектуру навмисно зроблено невеликою. Control plane на Go керує Docker, Traefik v3 маршрутизує кожне preview hostname, SQLite зберігає стан, а кожен застосунок працює в окремому контейнері. Kubernetes і окремий database server не потрібні, тому sandboxd взагалі може працювати на сервері з 2 vCPU.

Усю модель описують чотири об’єкти. app — це постійний проєкт, у якому зберігаються його назва, метадані git і секрети. sandbox — це Docker-контейнер, у якому працює app; одночасно app посилається лише на один sandbox. workspace — це файли app, які зберігаються на хості й не втрачаються разом із контейнером. task — це один prompt, переданий agent усередині sandbox. Зупинка sandbox звільняє пам’ять і зберігає файли. Знищення sandbox видаляє контейнер, після чого app можна запустити в новому контейнері.

Чим sandboxd відрізняється від Dify та OpenHands?

Ці три продукти часто плутають, оскільки всі вони запускають LLM (велику мовну модель) на вашому сервері, але результат їхньої роботи різний. Dify створює застосунки на основі LLM: інтерфейси чатів, конвеєри пошуку та workflows, які щоразу звертаються до моделі, коли ними хтось користується. Модель є частиною готового продукту. OpenHands працює з наявним репозиторієм: ви вказуєте йому свій код, а він читає файли, виконує команди та пропонує зміни. sandboxd починає роботу з порожнього середовища. Він створює структуру проєкту на основі preset, збирає його в новому контейнері та надає URL для перегляду. У результаті ви отримуєте звичайний застосунок React або FastAPI, якому не потрібна модель для роботи.

Тому обирайте інструмент залежно від кінцевого результату. sandboxd призначений для запуску роботи з одного речення та подальшого використання коду. Два інші інструменти призначені для випадків, коли репозиторій або продукт на основі моделі вже існує.

Інша відмінність — вік проєкту. Її потрібно врахувати, перш ніж будувати на ньому щось важливе.

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 875 зірок, у OpenHands — 83,091, а в Dify — 151,320. Репозиторій створено 3 June 2026, тому станом на August 2026 йому два місяці, тоді як OpenHands з’явився в March 2024, а Dify — в April 2023. Реліз v0.1.0 вийшов 6 June 2026, а v0.3.6 — 1 August 2026. Проєкт має статус beta і зазначає, що релізи 0.x можуть порушувати сумісність. Сприймайте ці числа як показник ризику залежностей, а не як оцінку якості: дворічний проєкт мав більше часу, щоб інші користувачі знайшли його помилки.

Потреби сервера та проблеми через нестачу ресурсів

У проєкті зазначено, що для початку достатньо 2 vCPU і 4 GB RAM. Це справедливо для control plane і одного невеликого sandbox, але недостатньо для одночасної роботи двох користувачів над збірками. Розподіляйте пам’ять між компонентами. Traefik і Go control plane споживають небагато. Кожен запущений sandbox містить повний toolchain для Node або Python, а пікове споживання виникає під час npm install, після якого запускається production build. Для сервера, на якому працюватиме кілька застосунків, плануйте 8 GB. Розглядайте swap як запас безпеки, а не як додатковий обсяг пам’яті: збірка, яка працює через swap, триває хвилини, а не секунди.

Коли пам’яті не вистачає, виникають дві різні помилки, які мають зовсім різний вигляд. Усередині sandbox контейнер досягає жорсткого обмеження --memory, установленого sandboxd, і kernel завершує найбільший процес. Через це збірка завершується без корисного повідомлення від agent. docker ps -a показує для цього контейнера код завершення 137, а команда docker inspect для нього повідомляє "OOMKilled": true. Якщо так завершується Node build, перед цим часто виводиться JavaScript heap out of memory.

Друга помилка виникає на host. sandboxd запускає pressure reaper, який зупиняє sandbox, коли пам’яті на host стає мало. Тому на невеликому сервері sandbox може зникнути під час перегляду його preview. Файли залишаються в безпеці, а наступний запит до preview URL запускає його знову. Однак завдання, яке виконувалося під час зупинки контейнера, не відновлюється.

Проблеми з диском виникають непомітніше. Кожен застосунок зберігає власний workspace на host, а JavaScript-проєкт містить дерево node_modules обсягом у сотні мегабайтів. Для десяти застосунків потрібно кілька гігабайтів лише на dependencies, без урахування images. Почніть із 40 GB і стежте за використанням:

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

Каталог даних за замовчуванням — /var/lib/sandboxed, причому в назві є додатковий e. Якщо ввести /var/lib/sandboxd, ви отримаєте порожній каталог і витратите п’ять хвилин на пошук причини.

Встановлення зафіксованого релізу sandboxd

Спочатку на сервері мають бути встановлені Docker Engine із плагіном Compose, а також git. У розділі Встановлення Docker на VPS описано цю частину.

docker compose version
git --version

Обидві команди мають вивести версію. docker: 'compose' is not a docker command означає, що у вас встановлено старий окремий бінарний файл docker-compose, а інсталятор очікує плагін версії v2.

Інсталятор є shell-скриптом, який завантажується через мережу. Перед запуском перегляньте його вміст і зафіксуйте версію.

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 — це git-посилання, яке інсталятор checkout-ить у $HOME/.sandboxd/src. За замовчуванням використовується main. Якщо залишити змінну невизначеною, буде встановлено стан коду, який було злито того ранку. Це важливо для проєкту, у якому лише за липень 2026 року вийшло шість релізів. Зафіксуйте версію, а потім оновлюйте її навмисно після ознайомлення зі списком змін.

Скрипт клонує вихідний код, збирає образи, запускає стек за допомогою docker compose up -d і наприкінці виводить URL консолі та API-токен. Збережіть цей токен у безпечному місці. Це облікові дані для API, який керує Docker із правами root.

curl http://127.0.0.1:9090/healthz

Якщо control plane запущено, команда виведе ok. Якщо нічого не виведено, стек не запустився: виконайте docker compose ps з каталогу ~/.sandboxd/src, щоб визначити, який сервіс не працює, а потім docker compose logs sandboxd, щоб з’ясувати причину.

Підключення до консолі на віддаленому сервері

Консоль доступна через Traefik на порту HTTP_PORT, який типово має значення 80, за іменем хоста http://console.localhost. Traefik маршрутизує запити за іменем хоста, тому введення IP-адреси сервера у браузері не відповідає жодному правилу та повертає помилку 404. Поки ви не налаштували справжній домен, перенаправте порт і збережіть ім’я хоста:

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

Потім відкрийте http://console.localhost:8080 на своєму ноутбуці. У Linux і macOS будь-яке ім’я, що закінчується на .localhost, резолвиться в 127.0.0.1, тому запит проходить через тунель із правильним заголовком Host. Під час першого відвідування встановіть пароль консолі.

Надайте агенту модель

У базовому образі доступні два агенти для роботи з кодом: OpenCode і Claude Code. SANDBOXD_DEFAULT_AGENT визначає, який із них виконуватиме завдання, якщо в ньому не вказано конкретного агента, і за замовчуванням використовує opencode. Якщо не підключено жодного ключа, завдання виконуються на безкоштовних моделях OpenCode Zen без ключа. Тому перша збірка нічого не коштує, і ви можете перевірити весь процес перед будь-якими витратами.

Підключіть власний ключ, якщо потрібна потужніша модель. Ключі передаються до control plane, але не потрапляють у sandbox: вони зберігаються в зашифрованому вигляді в data directory, а credential proxy передає їх через мережу. Тому ні агент, ні написаний ним код не можуть прочитати ці ключі.

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-..."}'

Консоль виконує ті самі дії в розділі Settings, AI Agents, зокрема пропонує керований OAuth flow, якщо ви хочете використовувати підписку Claude замість API key. Модель за замовчуванням для кожного агента налаштовується на цій самій панелі, а окреме завдання може її перевизначити.

Створіть невеликий застосунок від початку до кінця

Створіть застосунок, запустіть його sandbox, а потім надішліть prompt. Ідентифікатори повертаються у форматі JSON, а quickstart видобуває їх за допомогою sed, тому 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"

Обидві змінні мають містити ідентифікатор. Порожній $SB означає, що sandbox не запустився. Зазвичай причина полягає в тому, що базовий образ ще збирається або на хості недостатньо пам’яті. Значення 401 замість ідентифікатора означає, що bearer token неправильний.

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"}'

Відповідь містить ідентифікатор завдання. GET /v1/sandboxes/$SB/tasks/<task id> повертає його результат, а шлях /events для того самого завдання — це активний потік SSE (server sent events) із даними про дії агента. У консолі цей самий потік відображається як чат.

Після цього застосунок доступний за адресою http://s-<sandbox id>-3000.preview.localhost, де 3000 — порт, який ви вказали. Якщо sandbox перебував у стані сну, перший запит надходить до catch-all у Traefik. sandboxd запускає контейнер, очікує на відповідь порту й показує коротку сторінку запуску, яка після оновлення відкриває ваш застосунок. Якщо preview постійно залишається на цій сторінці, процес усередині sandbox не прослуховує порт, зазначений у sandbox.yaml застосунку.

Розмістіть preview-середовища на реальному домені з HTTPS

Кожне sandbox-середовище отримує власне hostname, тому один wildcard DNS-запис охоплює їх усі. Спрямуйте *.preview.yourdomain.com на IP-адресу сервера за допомогою A-запису. Потім задайте змінні preview-середовища в .env у ~/.sandboxd/src:

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

Traefik потребує відповідної частини конфігурації: увімкніть entrypoint websecure у traefik/traefik.yml і додайте certificate resolver. Використовуйте challenge DNS-01, оскільки один wildcard-сертифікат охопить усі hostname preview-середовищ. У разі HTTP-01 для кожного нового sandbox-середовища потрібно було б окремо отримувати сертифікат, а активна збірка швидко призвела б до досягнення лімітів Let's Encrypt. У Wildcard-сертифікати через challenge DNS-01 описано налаштування DNS.

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

URL preview-середовищ матимуть вигляд https://s-<id>-3000.preview.yourdomain.com. Відкрийте порти 80 і 443 у firewall, а порт 9090 залиште недоступним із зовнішньої мережі: див. базові правила firewall для ufw. Пам’ятайте, що будь-хто, хто вгадає hostname preview-середовища, зможе відкрити застосунок, тому вважайте preview-середовища публічними.

Куди потрапляє згенерований код і чи можна його експортувати?

На хості — у каталозі даних. Кожен workspace є звичайним каталогом за шляхом /var/lib/sandboxed/workspaces/<id>/, змонтованим у контейнер через bind mount, а файли застосунку розташовані за шляхом /home/sandbox/workspace/app усередині sandbox. Стан control plane зберігається в одному файлі SQLite за шляхом state/sandboxd.db, а зашифровані облікові дані agent — у agent-auth/. Нічого не приховано в шарі контейнера, тому резервна копія складається з копії каталогу та цього файлу бази даних. Резервні копії restic на VPS охоплюють обидва компоненти.

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

Git export вбудовано в систему, а не додано окремо. API надає status і diff для читання, а також commit і 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"}'

Для приватного remote потрібен personal access token. Один раз задайте його в консолі: Settings, Git credentials. Токен зберігається в зашифрованому вигляді поза sandbox, тому agent не може прочитати його або непомітно виконати push із його використанням. Виконуйте push рано й регулярно. Поки цього не зроблено, каталог workspace є єдиною копією коду, а DELETE /v1/apps/<id> видаляє його без можливості відновлення.

Скільки токенів моделі коштує збірка?

sandboxd не відстежує ваші витрати, тому важливе число потрібно дивитися в консолі вашого провайдера. Безкоштовні моделі OpenCode Zen нічого не коштують, але вони повільніші та слабші за платну модель. Для всього, що складніше за тестовий застосунок, це проявляється у більшій кількості раундів виправлень.

Сума залежить від роботи агентного циклу. Під час кожного ходу агент повторно надсилає потрібний контекст, тому вартість залежить від кількості ходів, а не від кількості застосунків. Один вдалий запит коштує недорого. П’ятнадцять раундів із проханнями «тепер виправте відступи» для проєкту з п’ятдесятьма файлами коштують значно дорожче, оскільки вміст файлів щоразу передається повторно. Вхідні та вихідні токени тарифікуються по-різному, а в матеріалі скільки коштує coding agent за сеанс наведено реалістичний діапазон. Перш ніж передати керування циклу, який працює без нагляду, установіть жорсткий ліміт витрат у провайдера.

Очищення застарілих sandbox

Idle reaper зупиняє кожен sandbox, який не використовувався довше ніж SANDBOXD_IDLE_THRESHOLD_SECONDS. Типове значення — 2100 секунд, або 35 хвилин. Це звільняє RAM, але зберігає файли. Наступний запит до preview URL запускає контейнер знову. На невеликому сервері зменште це значення, оскільки 35 хвилин простою контейнерів — це 35 хвилин недоступної для використання пам’яті.

Зупинка не означає видалення. Саме тому диски можуть непомітно заповнюватися. Зупинений sandbox і далі володіє своїм workspace та контейнером. Видалення sandbox зі збереженням застосунку — це DELETE для sandbox. Воно видаляє контейнер і workspace. Видалення застосунку видаляє все остаточно.

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

Після кількох тижнів експериментів docker system df покаже більше простору образів, доступного для звільнення, ніж ви очікуєте, оскільки кожен застосунок, який завантажував власний toolchain, залишив після себе шари. docker image prune очищає невикористовувані шари. Спочатку перевірте GET /v1/apps, оскільки образ, на який усе ще посилається sandbox у режимі сну, не є сміттям.

Що дає і чого не дає межа контейнера

Кожен sandbox працює від імені непривілейованого користувача з файловою системою root лише для читання, без усіх Linux capabilities, із заданим no-new-privileges, обмеженням пам’яті та лімітом процесів. Проєкт прямо вказує на це обмеження: контейнер Linux зі спільним ядром є надійною межею ізоляції, але слабкою межею безпеки. Уразливість ядра призводить до компрометації хоста.

Два факти потребують уваги. Вихідний мережевий трафік із sandbox у self-hosted build відкритий, тому згенерований код може звертатися до інтернету, локальної мережі та endpoint-ів метаданих хмарної інфраструктури. Підсистема вихідного трафіку nftables є у вихідному коді, але вимкнена під час компіляції у portable Docker Compose build. Тому обмеження має забезпечувати firewall хоста. API control plane фактично має права root на хості, оскільки керує Docker socket. За замовчуванням він прослуховує 127.0.0.1:9090, параметр SANDBOXD_API_AUTH_DISABLED має залишатися false, і цей API не можна публікувати в інтернеті.

Якщо ви плануєте дозволити іншим людям надсилати prompts на ваш сервер, цієї моделі ізоляції недостатньо. Проєкт рекомендує gVisor із SANDBOXD_RUNTIME=runsc. Він розміщує kernel у userspace між sandbox і хостом, але робота з великою кількістю системних викликів стає приблизно у 1.7–4 рази повільнішою. Надійніший варіант — окрема машина для кожного tenant. Аргумент той самий, що й для запуску coding agents у disposable VM.

Чи варто будувати на проєкті, якому два місяці?

Для особистого сервера збірки — так, із очевидними запобіжними заходами: зафіксуйте SANDBOXD_REF, створюйте резервні копії /var/lib/sandboxed і надсилайте кожен важливий для вас застосунок до віддаленого git-репозиторію. Якщо застосунком користується клієнт, дочекайтеся версії 1.0 або закладіть час і кошти на виправлення проблем, оскільки мейнтейнери прямо зазначають, що у версіях 0.x зміни можуть відбуватися без зворотної сумісності. Станом на August 2026 мейнтейнери також пропонують кероване встановлення за 79 доларів на місяць. Це варто враховувати, оцінюючи, чи має проєкт підстави продовжувати існування.

Ризик прийнятний завдяки результату. sandboxd створює звичайний застосунок у звичайному git-репозиторії, тому в разі зупинки розробки проєкту ви зберігаєте код і втрачаєте лише оболонку. Це значно краще, ніж у випадку з hosted builder, якому належить ваш проєкт. Щоб ширше оцінити, що цього року варто розміщувати на власному сервері, див. що варто розміщувати на власному сервері у 2026 році.

FAQ

Які мінімальні характеристики сервера потрібні для sandboxd?

У проєкті зазначено, що для початку достатньо 2 vCPU і 4 GB RAM. Цього вистачає для control plane, Traefik і одного невеликого sandbox. Якщо потрібно одночасно запускати кілька застосунків, використовуйте 8 GB RAM і 40 GB дискового простору. Кожен запущений sandbox містить повний toolchain для Node або Python, а кожен workspace зберігає власне дерево залежностей на диску. Коли на хості бракує ресурсів, pressure reaper у sandboxd зупиняє sandbox, щоб звільнити пам’ять. Якщо збірка перевищує ліміт пам’яті контейнера, kernel завершує її: docker ps -a показує для неї код завершення 137.

Чим sandboxd відрізняється від Dify або OpenHands?

Вони створюють різні результати. Dify створює застосунки, які під час роботи викликають model, наприклад chat interfaces і retrieval pipelines. OpenHands редагує наявний repository, виконує команди та пропонує зміни до наявного коду. sandboxd створює новий проєкт за prompt, збирає його у власному контейнері та публікує за preview URL. Результатом є звичайний web application, якому для роботи не потрібна model.

Де фактично зберігається код, який пише agent?

На файловій системі хоста, а не в image контейнера. Для кожного застосунку створюється directory у /var/lib/sandboxed/workspaces/<id>/, яку монтують у його sandbox через bind mount. Усередині sandbox файли доступні за шляхом /home/sandbox/workspace/app. Стан control plane зберігається в одному файлі SQLite у state/ в тому самому data directory. Ви можете виконати commit і push до git remote з вкладки Git у console або через endpoints /v1/apps/<id>/git/commit і /git/push. Токен для приватних remote зберігається control plane у зашифрованому вигляді, а не передається sandbox.

Чи безпечно відкривати sandboxd в інтернет?

Відкривайте preview URLs і console, але не API control plane. Цей API керує Docker на хості, тому фактично має права root. Саме тому за замовчуванням він прослуховує 127.0.0.1:9090. У self-hosted build sandbox також мають відкритий вихідний мережевий доступ. Це означає, що код, написаний agent, може звертатися до локальної мережі та endpoints cloud metadata. Якщо на хості є системи, які потрібно захистити, додайте правила host firewall. Для prompt від людей, яким ви не довіряєте, використовуйте окремий хост для кожного tenant, а не покладайтеся лише на ізоляцію контейнера.

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