Як розгорнути 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: чат-інтерфейси, конвеєри пошуку та отримання даних, робочі процеси, які щоразу викликають модель, коли хтось ними користується. Модель є частиною готового продукту. OpenHands працює з наявним репозиторієм: ви вказуєте йому свій код, а він читає файли, виконує команди та пропонує зміни. sandboxd починає з нуля. Він створює структуру проєкту з пресету, збирає його в новому контейнері й надає URL для перегляду. Результатом є звичайний застосунок React або FastAPI, якому для роботи не потрібна модель.
Тому обирайте за кінцевим результатом. sandboxd призначений для запуску від речення й подальшого збереження коду. Два інші варіанти призначені для випадків, коли репозиторій або продукт на основі моделі вже існує.
Інша відмінність — вік проєкту. На неї слід зважити, перш ніж будувати на його основі щось справді важливе.
Дані, на яких побудовано графік
[
{
"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 і control plane на Go споживають мало ресурсів. Кожен запущений sandbox містить повний toolchain для Node або Python, а пікове навантаження виникає під час npm install і наступної production build. Для сервера, на якому постійно працюватиме кілька застосунків, плануйте 8 GB. Swap використовуйте як аварійний резерв, а не як заміну оперативній пам’яті: build, що працює через swap, триває хвилини замість секунд.
Коли пам’ять закінчується, виникають дві різні помилки, які зовсім не схожі одна на одну. Усередині sandbox контейнер досягає жорсткого обмеження --memory, установленого sandboxd, і kernel завершує найбільший процес. У результаті build завершується без корисного повідомлення від 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 розміром у сотні мегабайтів. Для 10 застосунків це кілька гігабайт залежностей без урахування images. Почніть із 40 GB і стежте за використанням:
docker system df
sudo du -sh /var/lib/sandboxed/workspacesУ v0.3.6, release, до якого прив’язано цей посібник, каталог даних за замовчуванням — /var/lib/sandboxed, написаний із додатковим e. Якщо ввести там /var/lib/sandboxd, команда нічого не знайде, а пошук причини може забрати п’ять хвилин. Це змінилося у v0.3.9 (25 August 2026). Новіші installers розміщують нові інсталяції в /var/lib/sandboxd і використовують /var/lib/sandboxed лише для інсталяції, яка вже розташована там. На сервері, уперше налаштованому з пізнішим release, у всіх наведених нижче командах використовуйте шлях без e.
Встановлення зафіксованого релізу sandboxd
Спочатку на сервері мають бути встановлені Docker Engine із плагіном Compose та git. У розділі Встановлення Docker на VPS описано цю частину.
docker compose version
git --versionОбидві команди мають вивести версію. docker: 'compose' is not a docker command означає, що плагін Compose v2 відсутній. Інсталятор надає перевагу плагіну, але використовує окремий бінарний файл docker-compose, якщо його встановлено, і завершує роботу з Docker Compose not found лише тоді, коли немає жодного з них. У командах цього посібника використовується docker compose, тому найпростіше рішення — встановити плагін.
Інсталятор є 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.shSANDBOXD_REF — це git ref, який інсталятор checkout-ить у $HOME/.sandboxd/src. За замовчуванням використовується main. Якщо не задати це значення, буде встановлено стан проєкту, який було злито того ранку. Це важливо для проєкту, що лише в липні 2026 року випустив шість релізів. Зафіксуйте версію, а потім оновлюйте її навмисно, попередньо прочитавши changelog. Станом на 12 жовтня 2026 року найновішим релізом є v0.3.22 — це на шістнадцять релізів більше, ніж зафіксований тут v0.3.6.
Скрипт клонує вихідний код, збирає образи, запускає стек за допомогою 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 застосунку.
Розмістіть попередні перегляди на реальному домені з HTTPS
Кожне sandbox має власне ім’я хоста, тому один wildcard DNS-запис охоплює їх усі. Спрямуйте *.preview.yourdomain.com на IP-адресу сервера за допомогою A-запису. Потім задайте змінні попереднього перегляду в .env у ~/.sandboxd/src:
PREVIEW_DOMAIN=yourdomain.com
PREVIEW_ENTRYPOINT=websecure
PREVIEW_TLS=true
SANDBOXD_API_AUTH_DISABLED=falseTraefik потребує відповідної частини конфігурації: увімкніть entrypoint websecure у traefik/traefik.yml і додайте certificate resolver. Використовуйте challenge DNS-01, оскільки один wildcard-сертифікат охоплює всі імена хостів попереднього перегляду. У разі HTTP-01 кожному новому sandbox потрібен був би окремий випуск сертифіката, а активна збірка швидко впирається в ліміти Let's Encrypt. У матеріалі Wildcard-сертифікати через challenge DNS-01 описано налаштування DNS.
cd ~/.sandboxd/src
docker compose up -dURL попередніх переглядів матимуть вигляд https://s-<id>-3000.preview.yourdomain.com. Відкрийте порти 80 і 443 у firewall, а порт 9090 залиште закритим для зовнішнього доступу: див. базові правила firewall ufw. Пам’ятайте, що будь-хто, хто вгадає ім’я хоста попереднього перегляду, зможе відкрити застосунок, тому вважайте попередні перегляди публічними.
Якщо ви хочете повністю відмовитися від домену, DNS і сертифікатів, розробники тепер пропонують sandboxd Cloud. Він надає серверу публічну HTTPS-адресу в домені sandboxd.io за 9.99 долара на сервер на місяць або за 4.99 долара зі знижкою на запуск, станом на October 2026. Перед використанням перевірте, яку версію release він потребує, зафіксувавши v0.3.6.
Де зберігається згенерований код і чи можна його експортувати?
На хості — у каталозі даних. Кожен workspace — це звичайний каталог за адресою /var/lib/sandboxed/workspaces/<id>/ в інсталяції v0.3.6, змонтований у контейнер через 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 вбудований у систему, а не підключений окремо. 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":"todo-first-pass"}'Push завжди виконується в нову гілку. Якщо не вказати branch, за замовчуванням використовується sandboxd/<slug>-<shortsha>. main, master і гілка, з якої виконано імпорт, відхиляються, тому злиття потрібно виконувати на віддаленій стороні.
Для приватного 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, встановленим no-new-privileges, обмеженням пам’яті та лімітом процесів. Проєкт відкрито описує це обмеження: контейнер Linux зі спільним kernel є сильною межею ізоляції, але слабкою межею безпеки. Помилка в kernel може призвести до компрометації хоста.
Два факти потребують практичних дій. Вихідний мережевий трафік із sandbox у self-hosted збірці відкритий, тому згенерований код може звертатися до інтернету, локальної мережі та кінцевих точок метаданих хмарної інфраструктури. У вихідному коді є підсистема вихідного трафіку nftables, але в portable Docker Compose збірці вона вимкнена під час компіляції. Це означає, що обмеження має забезпечувати ваш host firewall. API control plane фактично працює з правами root на хості, оскільки керує Docker socket. За замовчуванням він прив’язується до 127.0.0.1:9090, параметр SANDBOXD_API_AUTH_DISABLED має залишатися false, і цей API ніколи не слід публікувати в інтернеті.
Якщо ви плануєте дозволити іншим людям надсилати prompts на ваш сервер, цієї моделі самої по собі недостатньо. Проєкт вказує на gVisor із SANDBOXD_RUNTIME=runsc, який розміщує kernel у просторі користувача між sandbox і хостом. За власними вимірюваннями проєкту це робить встановлення пакетів приблизно у 1.7 раза повільнішим, а роботу з великою кількістю системних викликів — приблизно у 4 рази повільнішою. Надійніший варіант — окрема машина для кожного tenant. Аргумент такий самий, як у випадку запуску coding agents у одноразовій VM.
Чи варто використовувати проєкт, якому два місяці?
Для персонального build-сервера — так, але з очевидними запобіжними заходами: зафіксуйте SANDBOXD_REF, створіть резервну копію каталогу даних (/var/lib/sandboxed у v0.3.6, /var/lib/sandboxd у разі нового встановлення v0.3.9 або новішої версії) і передавайте кожен важливий для вас застосунок до віддаленого git-репозиторію. Для будь-чого, з чим працюють клієнти, дочекайтеся версії 1.0 або закладіть бюджет на виправлення поломок, оскільки супроводжувачі прямо попереджають, що в 0.x можуть змінювати поведінку без зворотної сумісності. Супроводжувачі також пропонують кероване встановлення: 199 доларів за налаштування і 79 доларів на місяць за експлуатацію станом на October 2026. Це варто враховувати, оцінюючи, чи має проєкт підстави продовжувати існування.
Ризик прийнятний завдяки результату. 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, щоб її звільнити. Якщо збірка перевищує memory ceiling свого контейнера, kernel завершує її: docker ps -a показує для неї exit code 137.
Чим sandboxd відрізняється від Dify або OpenHands?
Вони створюють різні результати. Dify створює застосунки, які під час роботи викликають модель, наприклад chat interface і retrieval pipeline. OpenHands редагує наявний repository, виконує команди та пропонує зміни до наявного коду. sandboxd створює каркас нового проєкту за prompt, збирає його у власному контейнері та публікує за preview URL. У результаті виходить звичайний web application, якому для роботи не потрібна модель.
Де фактично зберігається код, який пише agent?
У файловій системі хоста, а не всередині container image. Для кожного застосунку в інсталяції v0.3.6 створюється каталог у /var/lib/sandboxed/workspaces/<id>/ (у новій інсталяції v0.3.9 або новішої версії — у /var/lib/sandboxd/workspaces/<id>/). Цей каталог монтується через bind mount у його sandbox, а всередині вона доступна за шляхом /home/sandbox/workspace/app. Стан control plane зберігається в одному файлі SQLite у state/ у тому самому data directory. Ви можете виконати commit і push у нову branch на git remote з Git tab консолі або через endpoints /v1/apps/<id>/git/commit і /git/push. Токен для private remote зберігається control plane у зашифрованому вигляді, а не передається sandbox.
Чи безпечно відкривати sandboxd в інтернет?
Відкривайте preview URL і консоль, але не control plane API. Цей API керує Docker на хості, тому фактично має права root. Саме тому за замовчуванням він прив’язаний до 127.0.0.1:9090. У self-hosted build sandbox також мають відкритий network egress. Це означає, що код, написаний agent, може підключатися до локальної мережі та cloud metadata endpoints. Якщо на хості є сусідні системи, які потрібно захистити, додайте правила host firewall. Для prompt від користувачів, яким ви не довіряєте, використовуйте окремий хост для кожного tenant і не покладайтеся лише на ізоляцію контейнера.