name: imgbot services: imgbot-daemon: build: . image: imgbot-daemon:local container_name: imgbot-daemon restart: unless-stopped # uid:gid пользователя tgclaude: .env лежит с mode 600, данные принадлежат # ему же. Под другим uid контейнер не прочитает ключи и не запишет картинки. user: "1001:1001" volumes: # Исходники read-only: правки агента применяются `docker restart`. - /home/tgclaude/imgbot:/app:ro # Данные: images/, manifest.db, куки google_state/reddit_state, логи. # ВАЖНО: путь внутри контейнера обязан совпадать с путём на хосте. # И проверять надо ВСЕ префиксы file_path в манифесте, а не только этот # (был второй корень — легаси-архив ~tgclaude/images, снесён 2026-09-08): # select substr(file_path,1,28), count(*) from images group by 1; # В SQLite `images.file_path` лежит абсолютным путём хоста, и его читают # как этот контейнер, так и оставшийся на хосте imgbot-file-server. # Любое другое место монтирования ломает обе стороны: analyze падает с # FileNotFoundError на старых строках, а новые строки становятся # нечитаемыми для хостовых процессов. - /home/tgclaude/imgbot-data:/home/tgclaude/imgbot-data # Таймзона хоста: без этого контейнер пишет в общий daemon.log в UTC, # а строки от прежнего хостового запуска — в CEST, и лог мешает два пояса. - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro environment: IMGBOT_DATA_DIR: /home/tgclaude/imgbot-data PYTHONPATH: /app/src HOME: /tmp # .env подхватывается самим приложением: config.py ищет его как # parents[2]/.env относительно пакета, то есть /app/.env через монтирование. # Через env_file не пробрасываем, чтобы ключи не размножать. # chromium падает на дефолтных 64 МБ /dev/shm shm_size: 1gb # Демон ровно жрёт ~15% ядра постоянно (analyze каждые 5 мин, 3 vision-запроса # на картинку). Потолок — чтобы refresh с playwright не разогнался на все 8. cpus: "2.0" mem_limit: 3g # Лог остаётся там, где его читает агент, и дублируется в `docker logs`. command: > sh -c 'python3 -c "from imgbot.daemon import main; main()" 2>&1 | tee -a /home/tgclaude/imgbot-data/daemon.log' logging: driver: json-file options: max-size: "10m" max-file: "3" # Объединённый HTTP-сервис: MCP (/mcp) и раздача картинок (/photo/) в # одном процессе. Раньше это были два процесса на двух портах, оба # захардкоженные на 172.22.0.1 — адрес шлюза docker-сети openclaw_default. imgbot-http: build: . image: imgbot-daemon:local container_name: imgbot-http restart: unless-stopped user: "1001:1001" volumes: - /home/tgclaude/imgbot:/app:ro - /home/tgclaude/imgbot-data:/home/tgclaude/imgbot-data - /etc/localtime:/etc/localtime:ro - /etc/timezone:/etc/timezone:ro environment: IMGBOT_DATA_DIR: /home/tgclaude/imgbot-data PYTHONPATH: /app/src HOME: /tmp # URL, который сервис выдаёт в image_url: имя контейнера в общей # docker-сети с claude-channels, откуда его и качает агент. IMGBOT_PUBLIC_BASE: http://imgbot-http:8765 # Порт НЕ публикуется на хост: сервис доступен только из docker-сетей, # к которым подключён контейнер (и с самой машины). Авторизации у него # нет сознательно — см. README проекта. expose: - "8765" networks: - default - claude-channels # request_photos гоняет playwright внутри этого же процесса, поэтому # лимиты и shm такие же, как у демона. shm_size: 1gb cpus: "2.0" mem_limit: 3g command: python3 -c "from imgbot.app import main; main()" logging: driver: json-file options: max-size: "10m" max-file: "3" networks: # Сеть проекта claude-channels: агент внутри того контейнера обращается к # imgbot по имени imgbot-http. Тот же приём, что у gitea с monitoring. claude-channels: external: true name: claude-channels_default