OpenCode в Docker: проблемы на настоящих задачах и что делать вместо этого
Введение
Эта статья — часть большого руководства
OpenCode в Docker на Ubuntu 24.04.
Руководство показывает, как запускать
OpenCode с флагом
--auto внутри контейнера
Docker.
С --auto агент не спрашивает вас перед каждой
командой, поэтому контейнер ограничивает то, до чего он может достать:
агент работает только в одном git worktree, отдельной
копии проекта.
Первыми настоящими задачами с этой настройкой были новый курс по
Perl и план по ошибкам во вчерашнем логе
доступа.
Ниже — что пошло не так, почему и что делать вместо этого.
Использованные здесь инструменты: git,
Docker, awk.
Новый запуск не знает о прошлом
Каждый run начинает новую сессию. Когда агента
второго запуска попросили повторить отчёт по-английски, он ответил, что
разговор только что начался.
opencode run принимает
--continue для последней сессии и
--session для заданной, и лаунчер передаёт оба:
container/tools/ai/opencode-docker.sh ../site-agent run --session <id> "Repeat your report in English."
Называйте сессию, а не --continue, когда работают
два агента.
Все их контейнеры держат сессии в одном томе, поэтому
--continue берёт самую новую сессию любого из них.
Запущенный с worktree другой задачи, возобновлённый агент искал
записанные им файлы и свои заметки в /tmp, не нашёл
ни того, ни другого и стал ждать:
ls: cannot access '/work/container/src/dev/perl': No such file or directory ls: cannot access '/tmp/agentwork': No such file or directory
Файлы были в другом worktree, а /tmp исчез вместе
с контейнером прошлого запуска.
Id — в оболочке шага 6 (проверка,
что видит контейнер), сначала новые, со временем в UTC, по часам
контейнера:
opencode session list
ses_e8351f6a7ffehA4yRD0x6NlBtH Untitled session 10/27/2026, 10:09:11 AM ses_e83357c41ffeT3ReWox83y3CJF Untitled session 10/27/2026, 10:07:43 AM
Запуск, начатый с --title doc-test, виден в списке по своему заголовку, и задачи можно различить:
ses_e8296785dffeIxdSRidoUwdNLJ doc-test 10/27/2026, 12:50:34 PM
Агент видит только worktree
В задаче про логи назывался файл из игнорируемой папки основной копии.
В контейнере его нет, поэтому агент ничего не нашёл.
Запуск, который ничего не нашёл, всё равно может написать план — по старым
заметкам в репозитории.
Не монтируйте вместо этого лог: в нём IP-адреса и браузеры посетителей, а
бесплатная модель может хранить то, что ей отправляют.
Вырежьте на хосте то, что нужно задаче, в папку, которая смонтирована и не
игнорируется:
awk '$9 == 404 || $9 >= 500 {print $9, $4, $7}' access.log > ../site-agent/container/docs/errors.txt
cat ../site-agent/container/docs/errors.txt
404 [26/Oct/2026:10:15:11 /old/page.php 500 [26/Oct/2026:10:16:40 /ru/x/
- Только статус, время и путь. В комбинированном формате лога поле 9 — статус, 4 — время, 7 — путь. Прочитайте вырезку до запуска: в строке запроса могут быть адрес почты или токен.
- Монтируется только на чтение. container/docs — одна из папок только для чтения, и лаунчер принимает новый неотслеживаемый файл; отказывается он только от игнорируемых.
- Удалите его после задачи. Он не для коммита.
«Все проверки проходят» охватывает то, что агент проверил
Отчёт по курсу перечислял php -l, рендеры
страниц, баланс тегов и карту сайта: проверки
HTML.
Ни одна не запускала Perl, хотя Perl есть в образе, а в одном уроке было
сказано, что my $last = (1..5); даёт 5.
Запустите такой пример в образе сами, без сети и без ничего лишнего
смонтированного:
vi demo.pl
my $last = (1..5); print "[$last]\n";
docker run --rm --network none --cap-drop ALL -v "$PWD/demo.pl:/tmp/demo.pl:ro" --entrypoint perl opencode-agent -w /tmp/demo.pl
Use of uninitialized value $. in range (or flip) at /tmp/demo.pl line 1. []
В скалярном контексте .. — это оператор
переключателя, а не диапазон, и значение пустое.
Второй запуск исправил урок и добавил настоящий вывод на 51 страницу
курса.
Из 126 примеров с выводом 116 печатали ровно то, что показывает страница.
Остальные 10 зависят от файлов, которые урок создаёт первым, от модуля,
которого нет в образе, от машины или от порядка обычного вывода и вывода
ошибок.
Тот же запуск сломал страницу уже после своего
php -l: команда оболочки с одинарными кавычками
внутри строки PHP страницы, тоже в одинарных кавычках.
Хук коммита в worktree поймал это — как только хуки там заработали.
Хуки коммита в worktree
Worktree использует хуки репозитория, но git
запускает их там иначе: он передаёт хуку
GIT_DIR, путь git-папки worktree, и не передаёт
GIT_WORK_TREE.
Тогда git -C <folder> принимает эту папку за
верх рабочего дерева.
Скрипт хука, который ищет свои файлы так, падает с ошибкой или не находит
файлов и пропускает коммит. В одноразовом репозитории:
mkdir -p ~/hooktest/main
cd ~/hooktest/main
git init -q
mkdir -p a/b
touch a/b/f
git add .
git commit -qm init
vi .git/hooks/pre-commit
#!/bin/sh echo "GIT_DIR=$GIT_DIR" git -C a/b rev-parse --show-toplevel exit 1
chmod +x .git/hooks/pre-commit
git commit --allow-empty -m test
GIT_DIR= /home/me/hooktest/main
exit 1 останавливает каждый тестовый коммит, поэтому ничего не коммитится. Тот же коммит в worktree:
git worktree add -q ../wt -b side
cd ../wt
git commit --allow-empty -m test
GIT_DIR=/home/me/hooktest/main/.git/worktrees/wt /home/me/hooktest/wt/a/b
С unset GIT_DIR GIT_WORK_TREE вместо строки echo git снова находит worktree по папке, и тот же коммит печатает правильный верх:
/home/me/hooktest/wt
- Исправление идёт в каждый скрипт хука, который вызывает git -C, первой строкой после set -euo pipefail. В основной копии git не задаёт GIT_DIR, поэтому там ничего не меняется.
- Проверка внутри сервисного контейнера, например php -l в контейнере локального сайта, проверяет файлы, которые смонтированы в этот контейнер: основную копию, а не worktree. Запускайте её в одноразовом контейнере на worktree.
- Хук, который принимает новый include за страницу. Проверка, считавшая каждый новый файл .php страницей, падала на файле в .php/, папке с include. Теперь она пропускает папки, имена которых начинаются с точки.
main ушёл вперёд, пока агент работал
Когда курс был готов, его worktree отставал от main
на десять коммитов, среди них обновления лент.
Закоммитьте на старую основу — и эти файлы дадут конфликт при слиянии.
Сначала подтяните worktree до актуального состояния; подготовленные файлы
остаются подготовленными, проверено в одноразовом worktree:
git -C ../site-agent merge --ff-only main
Хук, который работает после коммита и готовит файлы, например ленту новых
страниц, оставляет их для второго коммита.
Сделайте его тоже в worktree, чтобы git merge
--ff-only принёс оба.
Другой агент в вашей папке, пока вы коммитите
pre-commit откладывает ваши неподготовленные изменения, пока работают
хуки, и возвращает их потом.
Коммит в основной копии упал с files were
modified by this hook на хуке, который ничего не меняет.
Другой агент, работавший в той же папке, отредактировал файл в эту минуту.
Правка, сделанная в это окно, может и потеряться, когда pre-commit
возвращает ваши изменения.
Перечислите агентов и их папки до коммита (имя пользователя заменено):
for p in $(pgrep -x 'claude|opencode'); do echo "$p $(readlink /proc/$p/cwd)"; done
13509 /home/me/github/site 24610 /home/me/github/site 40276 /home/me
В списке есть и тот агент, которого вы, возможно, и просите его выполнить.
Коммитьте, когда в этой папке не работает другой агент, или дайте каждому
из них свой worktree, как Claude Code в
разделе два агента: по одной
ветке каждому.
У бесплатной модели есть лимит использования
После дня запусков новый остановился на первом запросе:
Error: Free usage limit reached. Connect a provider or subscribe to OpenCode Go to keep going, or switch models.
Подождите или продолжите ту же задачу на модели OpenCode Go либо на модели
через OpenRouter.
Как это сделать, рассказано в разделе
Когда бесплатная модель
упирается в лимит использования статьи про ежедневную работу.
Автор статьи: Клавдия Кондакова