# Dev-контейнер для Claude Code: безопасная песочница для автономной работы

Канонический URL: https://zero2claude.ru/blog/claude-code-devcontainer
Дата: 2026-10-17
Теги: claude-code, безопасность, автоматизация

Как запустить Claude Code в dev-контейнере: фича для devcontainer.json, именованный том, чтобы вход переживал пересборку, эталонный контейнер с файрволом и где проходит граница защиты, которую контейнер не даёт.

Рано или поздно хочется отпустить Claude Code поработать без присмотра: пусть сам прогонит рутинную правку по проекту, пока ты занят другим. Флаг, который снимает все вопросы о разрешениях, существует. Но запускать его прямо на своём компьютере — всё равно что дать ключи от квартиры человеку, которого видишь впервые, и уйти гулять.

Правильное место для такого запуска — [dev-контейнер](/glossary/devcontainer). Разберём, как его собрать, почему после пересборки пропадает вход и где проходит граница, которую контейнер не охраняет.

<Cover src="/blog/claude-code-devcontainer.jpg" alt="Помощник работает внутри прозрачного контейнера на причале, огороженного забором" />

## Чем контейнер отличается от песочницы

У Claude Code есть встроенная [песочница](/glossary/sandbox): она ограничивает, что могут делать **команды оболочки**. Dev-контейнер идёт дальше. Это Docker-контейнер, к которому подключается редактор, — на твоём компьютере или в облаке вроде GitHub Codespaces. Внутри живёт **весь процесс** Claude Code: инструменты правки файлов, MCP-серверы, хуки. Команды выполняются в контейнере, а правки попадают в твой репозиторий через монтирование.

Собирается такая среда в три слоя, и каждый опирается на предыдущий.

## Слой 1: фича Claude Code

Самое простое — добавить готовую фичу в `.devcontainer/devcontainer.json`:

```json
{
  "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
  "features": {
    "ghcr.io/devcontainers/features/node:1": { "version": "lts" },
    "ghcr.io/anthropics/devcontainer-features/claude-code:1.0": {}
  },
  "postCreateCommand": "sudo apt-get update && sudo apt-get install -y bubblewrap socat"
}
```

Подвох в теге `:1.0`. Кажется, что он закрепляет версию Claude Code, но нет: он закрепляет **скрипт установки фичи**. Фича ставит последний релиз, и дальше тот обновляется сам. А `postCreateCommand` доставляет `bubblewrap` и `socat` — без них на Linux не работает песочница для Bash.

## Слой 2: вход, который не пропадает

Типичная история: вошёл в Claude Code внутри контейнера, пересобрал его — и `claude auth status` возвращает код 1. Пересборка сохраняет папку с проектом, но выбрасывает домашнюю папку, а с ней и `~/.claude`. В Codespaces то же самое: после **Rebuild Container** `/workspaces` на месте, а входа нет.

Решение — именованный том Docker в `~/.claude` плюс переменная `CLAUDE_CONFIG_DIR` с тем же путём. Том живёт отдельно от контейнера, поэтому вход и настройки переживают пересборку:

```json
"mounts": [
  "source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
],
"containerEnv": {
  "CLAUDE_CONFIG_DIR": "/home/node/.claude"
}
```

Если вход всё равно пропадает, почти всегда причина одна: `target` тома и `CLAUDE_CONFIG_DIR` называют разные пути. Путь зависит от пользователя в образе — в базовом образе Ubuntu это `vscode`, а не `node`.

## Слой 3: эталонный контейнер

В репозитории Claude Code лежит эталонный `.devcontainer/` из трёх файлов: `devcontainer.json`, `Dockerfile` и `init-firewall.sh`. Он добавляет две вещи, ради которых всё и затевается:

- **обычного пользователя** `node` вместо root;
- **файрвол**: исходящий трафик идёт только на адреса из списка в `init-firewall.sh`.

Именно такой контейнер документация поддерживает для прогонов с `--dangerously-skip-permissions`. От root CLI этот флаг вообще не примет.

Перед первой сборкой сделай три вещи:

1. **Прочитай все три файла.** `init-firewall.sh` выполняется от root при каждом старте контейнера.
2. **Добавь домены входа.** Рядом с `api.anthropic.com` в списке должны быть `claude.ai` и `platform.claude.com`, иначе вход упрётся в файрвол. Нужен PyPI или приватный реестр пакетов — тоже допиши.
3. **Закрепи версию.** В аргументе сборки `CLAUDE_CODE_VERSION` замени `latest` на конкретную версию, а в `containerEnv` добавь `"DISABLE_AUTOUPDATER": "1"`.

Проверить файрвол просто: изнутри контейнера запрос к `example.com` должен падать, а к `api.github.com` — проходить.

## Запуск без вопросов — но с рамками

Контейнер не отменяет рамок, о которых мы говорили в уроке [про автономный запуск](/learn/claude-code-pro/3-2-avtonomnyy-zapusk). Он добавляет внешнюю стену, а лимиты по-прежнему нужны внутри:

```bash
claude -p "Добавь в начало README.md однострочное описание репозитория" \
  --dangerously-skip-permissions --model sonnet --max-turns 10
```

Логика та же, что и без контейнера: `--max-turns` ограничивает число ходов, `--max-budget-usd` — примерную стоимость, а коммитишь результат ты сам, после того как прочитал дифф. Когда лимит срабатывает, в JSON-выводе `subtype` объясняет причину — `error_max_turns` или `error_max_budget_usd`. И помни, что `total_cost_usd` в этом выводе — оценка на стороне клиента, а не твой счёт.

<Callout type="warning" title="Что контейнер НЕ защищает">
Контейнер не мешает прогону унести всё, что доступно внутри: в том числе учётные данные Claude Code в ~/.claude и сам проект с его .git. Расширение Dev Containers в VS Code ещё и пробрасывает в контейнер твои Git-учётки и SSH-агент, а эталонный файрвол пропускает GitHub — так что прогон может запушить от твоего имени. Используй контейнер только с репозиториями, которым доверяешь, никогда не монтируй туда ~/.ssh или файлы с облачными ключами, а после прогона прочитай .git/config, список .git/hooks и дифф .devcontainer — до пересборки и до git commit.
</Callout>

## Уборка после работы

Том переживает контейнер, а значит, и твой вход в нём. Закончил — выполни `claude auth logout` внутри контейнера, затем удали контейнер и тома, чьи имена начинаются с `claude-code-config-` и `claude-code-bashhistory-` (`docker volume ls`, потом `docker volume rm`). Codespace удаляй отдельно: даже остановленный, он занимает место.

## Частые вопросы

<Faq>
<FaqItem q="Закрепляет ли тег :1.0 версию Claude Code в контейнере?">

Нет. Тег закрепляет скрипт установки фичи, а она ставит последний релиз Claude Code, который потом обновляется сам. Чтобы версия не менялась, задай конкретную версию в CLAUDE_CODE_VERSION при сборке и добавь DISABLE_AUTOUPDATER=1.

</FaqItem>
<FaqItem q="Почему после пересборки контейнера Claude Code просит войти заново?">

Пересборка выбрасывает домашнюю папку вместе с ~/.claude. Смонтируй именованный том в эту папку и задай CLAUDE_CONFIG_DIR с тем же путём — тогда вход переживёт пересборку.

</FaqItem>
<FaqItem q="Можно ли запускать --dangerously-skip-permissions от root?">

Нет, от root CLI этот флаг не примет. Документация поддерживает такие прогоны в эталонном контейнере с обычным пользователем и файрволом.

</FaqItem>
</Faq>

## Где разобраться глубже

Настройке контейнера посвящён урок [«Claude Code в dev-контейнере»](/learn/claude-code-pro/6-1-dev-konteyner), а рамкам для запуска без присмотра — урок [«Автономный запуск с лимитами»](/learn/claude-code-pro/3-2-avtonomnyy-zapusk). Оба — в бесплатном курсе «Claude Code: уровень профи», с тренажёром в браузере. Флаги из этого поста собраны в [шпаргалке по Claude Code](/claude-code-shpargalka).
