Systematically enhance a Docker/Linux environment with tools and capabilities — browser, media, document processing, development tools, and data extraction. Designed for Docker containers on NAS (Synology) with limited initial tooling.
Resources
1Install
npx skillscat add hms091/hermes-skills/environment-enhancement Install via the SkillsCat registry.
Environment Enhancement
Systematic approach to installing tools and capabilities in a Docker container environment (Debian, NAS-hosted, with proxy).
Prerequisites — Network Diagnosis
Before installing anything, diagnose the network:
# Check proxy
curl -s --max-time 3 -w "\nHTTP: %{http_code}\n" "https://www.baidu.com" -o /dev/null
# Check direct access
curl -s --noproxy '*' --max-time 5 "https://httpbin.org/ip"
# Check apt source
curl -sI --max-time 5 "http://deb.debian.org/debian/dists/trixie/Release"Proxy restore pattern: If /etc/environment was modified and user says proxy works, restore from auto-created backup:
# 查找最新备份
ls -t /etc/environment.bak.* | head -1
# 恢复
cp /etc/environment.bak.$(date +%Y%m%d) /etc/environment
source /etc/environmentProxy gone → unexpected restore cycle: In one session, the user first confirmed proxy was dead (connection refused), so it was cleared. Later the user said proxy was actually working but slow. The pattern: don't permanently delete proxy config on first failure — backup it first. The NAS-hosted proxy may be restarted or temporarily unreachable. The safe approach is:
- Save the backup before modification
- If user later says proxy works, restore from backup and re-try installs WITH proxy
- Always
exportthe proxy vars after restoring /etc/environment — they don't auto-apply to the current shell
Installation Batches
Batch 1 — System Packages (apt)
Install all at once for efficiency. Network is the bottleneck, not CPU.
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \
chromium chromium-headless-shell chromium-sandbox \
ffmpeg \
tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng \
sqlite3 \
ripgrep \
libpango1.0-dev libcairo2-devTroubleshooting: Chromium package is ~200MB. First download may timeout on slow connections. Retrying 1-2 times usually works. If persistent failure, swap Debian mirror:
sed -i 's|deb.debian.org|mirrors.ustc.edu.cn|g' /etc/apt/sources.list.d/debian.sources分批策略(当网络慢时): 如果一个长 apt-get install 命令超时,不要直接重试——kill掉后拆成2-3个并行后台进程,每个不超过5个包。用 notify_on_complete=true 监听完成:
# 第一批:浏览器相关
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq chromium chromium-headless-shell chromium-sandbox
# 第二批:媒体+文档(可并行)
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ffmpeg tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng sqlite3 ripgrepEnvironment Detection — Before Installing Anything
Always check these before starting installation:
# 1. Container detection
cat /proc/1/cgroup 2>/dev/null | head -3 # If shows /docker/, we're in a container
cat /proc/1/sched 2>/dev/null | head -2 # PID 1 scheduler
# 2. Docker availability
docker ps 2>&1 # If "Cannot connect to the Docker daemon" → no Docker-in-Docker
ls -la /var/run/docker.sock 2>/dev/null # If missing, cannot run containers
apt list --installed 2>/dev/null | grep docker-cli # CLI-only install
# 3. GPU availability
nvidia-smi 2>&1 # If "command not found" → no GPU, skip GPU-dependent tools
# 4. Python environment type
pip install --dry-run dummy-package 2>&1 | grep "externally-managed"
# If externally-managed → need --break-system-packages or venv
# 5. Available disk space
df -h / | tail -1 # Check if ~5GB+ for model downloadsPEP 668 (externally-managed-environment) 处理: Python 3.13+ on Debian has this protection by default. Three options:
--break-system-packages— fastest, works for simple installs. Acceptable in a disposable container.- Create a venv —
python3 -m venv /opt/venv && /opt/venv/bin/pip install ...— safer, recommended for production. - Use Hermes venv — Hermes already has
/opt/hermes/.venv/—pip installvia that venv's pip:
Check with:/opt/hermes/.venv/bin/pip install --quiet <package>ls /opt/hermes/.venv/bin/pip 2>/dev/null
Docker-in-Docker limitation: When running inside a container (check /proc/1/cgroup for /docker/), Docker CLI is often installed but Docker daemon is NOT running. This means:
docker compose up -dwill fail with "Cannot connect to the Docker daemon"- The compose files and startup scripts can be prepared, but need to be deployed elsewhere
- Workaround: prepare
docker-compose.yml+.env+start.shin a known directory, so they're ready when Docker becomes available
Docker compose fallback pattern: When Docker daemon is unavailable but you want to prepare for future deployment:
- Create a services directory:
mkdir -p /opt/data/docker-services/{ragflow,anythingllm,firecrawl,...} - For each service, download or write the
docker-compose.ymlwith proper port mappings and volumes - Create a unified
start-all.shscript that checks for Docker first, then launches each service - Save
.envfiles with default passwords - Report the limitation clearly: "Docker daemon not available in this container. Configs prepared at /opt/data/docker-services/ — deploy on your Docker host with
bash start-all.sh"
Batch 2 — Python Packages (pip)
Run in parallel with Batch 1 (independent).
Proxy pitfall: If proxy env vars are set, pip will try to reach PyPI through the proxy. If the proxy is down, pip fails with connection refused. Always check env | grep -i proxy before the pip install and either unset or confirm proxy is working.
Externally-managed environment fix: If you get error: externally-managed-environment, add --break-system-packages:
pip install --break-system-packages --quiet <package>This is acceptable in a disposable container environment. For production, use a venv instead.
/opt/hermes/.venv/bin/pip install --quiet --timeout 60 \
pillow \
pymupdf \
openpyxl \
python-docx \
beautifulsoup4 \
lxml \
yt-dlp \
youtube-transcript-api \
pandas \
numpy \
requests \
python-pptx \
pyyamlKey packages and their skills:
pymupdf→ PDF text extraction (ocr-and-documents, nano-pdf)yt-dlp→ YouTube downloading (youtube-content)pillow→ image processing (pixel-art, ocr-and-documents)python-pptx,openpyxl,python-docx→ Office editing (powerpoint)beautifulsoup4,lxml→ HTML scraping (forum-research)
Post-install verification (pip import names differ from package names):
/opt/hermes/.venv/bin/python -c "
import PIL; print('pillow:', PIL.__version__)
import fitz; print('pymupdf:', fitz.__version__)
import openpyxl; print('openpyxl:', openpyxl.__version__)
import docx; print('python-docx:', docx.__version__)
import bs4; print('bs4:', bs4.__version__)
import lxml; print('lxml: ok')
import yt_dlp; print('yt-dlp:', yt_dlp.__version__) # may need try/except
import pandas; print('pandas:', pandas.__version__)
import numpy; print('numpy:', numpy.__version__)
import pptx; print('python-pptx:', pptx.__version__)
import yaml; print('pyyaml:', yaml.__version__)
print('✅ 全部Python包导入成功')
"Note: yt_dlp.__version__ may be missing on some versions — use yt_dlp.version.__version__ as fallback or import in try/except block.
Batch 3 — Node.js Global Tools (npm)
Run in parallel with Batch 1 and 2.
npm install -g --silent typescript ts-nodeCodeGraph (colbymchenry/codegraph) — 代码知识图谱 MCP 工具
CodeGraph 是 Hermes Agent 原生支持的 MCP 代码智能工具,安装后会在当前 Hermes 会话中直接提供 codegraph_explore 工具。它能减少 AI 助手搜索文件的工具调用次数 40-81%。
# 安装(GFW环境:取消代理后直连)
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
npm i -g @colbymchenry/codegraph --noproxy '*'
# 配置 Hermes(自动改 config.yaml,添加 codegraph MCP 服务)
codegraph install --target=hermes --yes
# 在每个项目目录初始化索引(一次,索引后自动文件监听同步)
cd your-project
codegraph init
# 验证状态
codegraph status参考:references/codegraph-setup.md
npm在代理环境下载失败: 容器可能有全局 http_proxy / https_proxy 环境变量指向不可达代理。npm 会走代理连 registry.npmjs.org,连接失败则超时卡住(60s 无响应)。修复:
# 1. 先检查是否被代理阻塞
env | grep -i proxy
# 2. 取消代理环境变量
unset http_proxy https_proxy HTTP_PROXY HTTPS_PROXY
# 3. 安装时加 --noproxy 防止 npm 自己再用代理
npm i -g <package> --noproxy '*'npm镜像解决不了代理问题: 如果直连 npm 慢,用国内镜像(npmmirror.com)也可能超时——在 Docker 内它可能 TLS 握手失败。最佳方案是 unset proxy + --noproxy '*' + 设 --timeout=120 耐心等。直连 30-60s 通常能完成。
npm安装成功但require失败的常见原因: npm全局模块装在/usr/local/lib/node_modules/,但Node默认require不搜这个路径。修复:先检查物理目录确认文件存在,然后用NODE_PATH=/usr/local/lib/node_modules node -e "..."验证,持久化到/etc/environment。
Subsequent Batches (run after the first three finish)
# Browser automation (Playwright)
/opt/hermes/.venv/bin/pip install --quiet playwright
/opt/hermes/.venv/bin/python -m playwright install chromium
# Additional Python tools
/opt/hermes/.venv/bin/pip install --quiet \
selenium
# CSS/image tools for creative skills
apt-get install -y -qq librsvg2-binTool Lifecycle Management (Post-Install)
After installing any tool (pip / npm / git / apt), systematize tracking and updates so tools don't drift.
Step 1: Check Version Control Coverage
cd /path/to/install && git rev-parse --git-dir 2>/dev/null \
&& echo "IN GIT" || echo "NOT IN GIT"Coverage matrix (Hermes Docker environment):
| Location | Auto-synced? | Mechanism |
|---|---|---|
/opt/data/skills/ |
✅ Yes | skills-sync.sh (hourly → GitHub) |
/opt/hermes/ |
❌ Not a git repo | Manual clone from upstream |
~/.understand-anything/repo/ |
❌ Upstream only | git pull on update |
| pip packages | ❌ No | Track via weekly-update.sh |
| npm packages | ❌ No | Track via weekly-update.sh |
Step 2: Update the Tools Manifest
/opt/data/skills/manifests/tools-manifest.json is the single source of truth. It's inside /opt/data/skills/ so it auto-syncs to HMS091/hermes-skills:
{
"last_updated": "YYYY-MM-DD",
"tools": [
{
"name": "Tool Name",
"type": "pip | npm | git | uv | apt",
"version_check": "command to check version",
"update_cmd": "command to update",
"notes": "purpose or special notes"
}
]
}Step 3: Integrate into Update Script
Edit /opt/data/scripts/weekly-update.sh:
- pip packages: Add to the
PACKAGESarray - git repos: Add a dedicated section using git fetch + rev-parse:
# Pattern: git-based tool in weekly-update.sh
echo "--- [N/4] <Tool Name> 检查 ---"
if [ -d "$HOME/.<tool-dir>/repo" ]; then
cd "$HOME/.<tool-dir>/repo"
git fetch origin main 2>/dev/null
LOCAL=$(git rev-parse HEAD)
REMOTE=$(git rev-parse origin/main 2>/dev/null)
if [ "$LOCAL" != "$REMOTE" ] && [ -n "$REMOTE" ]; then
git pull origin main 2>&1 | tail -3
REPORT+="✅ <Tool Name>: 已更新\\n"
else
REPORT+="✅ <Tool Name>: 已是最新\\n"
fi
cd /opt/data
fiUpdate the section counter in all echo lines after adding.
Step 4: Verify Cron Job Chain
cronjob action=listCheck for:
weekly-update.sh— weekly, covers all toolsskills-sync.sh— hourly, syncs skills to GitHub- Hermes self-update — weekly, updates hermes-agent via uv
- Git-based tool sections — integrated into weekly-update.sh
Hermes Plugin / Skill Installation (git-based)
Some tools install as Hermes plugins via curl | bash, cloning a repo and symlinking skills. Example: Understand Anything (Egonex-AI/Understand-Anything, 76K⭐, MIT, codebase→knowledge graph).
Installation
curl -fsSL https://raw.githubusercontent.com/OWNER/REPO/main/install.sh | bash -s hermesWhat Happens
- Clone to
~/.understand-anything/repo/ - Symlink into
~/.hermes/skills/:~/.hermes/skills/understand-anything → ~/.understand-anything/repo/understand-anything-plugin/skills - Exposes sub-skills (understand, understand-chat, understand-dashboard, etc.)
Verification
ls -la ~/.hermes/skills/
ls ~/.understand-anything/repo/
ls ~/.hermes/skills/understand-anything/Post-Install: Restart Required
Run /reset or /reload-skills in the TUI, or restart hermes.
Update
cd ~/.understand-anything/repo && git pull origin main
# Or if installer supports --update:
bash install.sh --updatePitfalls
- Symlink, not copy — deleting the repo breaks skills
- Not in pip/npm — track via tools-manifest.json
- HEAD always moves —
git pullgives latest, no pinning - Re-running install may fail — symlink already exists
Skill-Capability Mapping
| Skill Category | Depends On | Installed Via |
|---|---|---|
| Browser (B站, web scraping, freelancing) | chromium, playwright, selenium | apt + pip (2-step) |
| PDF/Document (ocr-and-documents, nano-pdf) | pymupdf, tesseract | pip + apt |
| Media (youtube-content) | yt-dlp, ffmpeg | pip + apt |
| Office (powerpoint) | python-pptx, openpyxl, python-docx | pip |
| Visual (pixel-art, ascii-video) | pillow, ffmpeg | pip + apt |
| Development | ripgrep, node, typescript | apt + npm |
| Code Intelligence (CodeGraph MCP) | codegraph (npm) | npm |
Hermes Multi-Device Skills Sync
当多台设备运行 Hermes Agent 时,需要在各设备间共享创建的技能。Hermes 支持通过 skills.external_dirs 配置额外技能目录。
原理
Hermes 默认技能目录是 ~/.hermes/skills/(或 /opt/data/skills/),创建的技能存这里。external_dirs 可以额外指定共享技能目录,两个目录的技能都会被加载:
# config.yaml
skills:
external_dirs:
- /mnt/nas/shared-skills # NAS 挂载目录
# 或
- /opt/data/synced-skills # git 同步目录方案 A:NAS Samba/NFS 共享(推荐,实时同步)
前提: NAS(192.168.1.88)上已开启 Samba 或 NFS 共享。
# 1. 安装 cifs-utils
apt-get install -y cifs-utils
# 2. 创建挂载点
mkdir -p /mnt/nas/shared-skills
# 3. 挂载 NAS 共享(替换 username/password/sharename)
mount -t cifs //192.168.1.88/shared-skills /mnt/nas/shared-skills \
-o username=your_user,password=your_pass,uid=$(id -u),gid=$(id -g)
# 4. 验证
ls /mnt/nas/shared-skills/持久挂载(重启不丢): 加到 /etc/fstab:
//192.168.1.88/shared-skills /mnt/nas/shared-skills cifs username=xxx,password=xxx,uid=0,gid=0,noauto 0 0每台设备都这样配,技能文件放 NAS 上,所有设备实时共享。
方案 B:Git 仓库同步(有版本历史)
# 主设备:初始化共享技能目录
mkdir -p /opt/data/synced-skills
# 现有技能复制过去
cp -r /opt/data/skills/* /opt/data/synced-skills/
# 或从 GitHub 克隆已有仓库
git clone https://github.com/HMS091/hermes-skills.git /opt/data/synced-skills
# 设 cron 自动推送(每小时)
crontab -e
0 * * * * cd /opt/data/synced-skills && git add -A && git commit -m "auto sync $(date)" && git pushGFW(中国防火墙)下同步的注意事项:
在 China 网络环境下,SSH 到 GitHub 的端口 22 和 443 均被封锁,无法使用 SSH 方式 clone/push。必须改用 HTTPS + Personal Access Token:
# 1. 生成 GitHub Personal Access Token
# 去 https://github.com/settings/tokens/new
# 勾选 repo 权限,生成以 ghp_ 开头的 token
# 2. 配置 git 缓存 token
git config --global credential.helper store
# 3. 第一次 push/pull 时会提示输入用户名和密码
# 用户名:HMS091
# 密码:粘贴 token(不是 GitHub 登录密码)
# 4. 配置 git 身份(用于 commit 记录)
git config --global user.name "HMS091"
git config --global user.email "your-email" # 填你的 GitHub 注册邮箱
# 5. 验证
git push origin main # 第一次输 token 后永久记住删除 SSH key 避免冲突: 如果已经生成过 SSH key 但 GFW 导致 SSH 连接失败,git 可能自动尝试 SSH 失败后不切换 HTTPS。确保 remote URL 是 HTTPS 格式:
git remote set-url origin https://github.com/HMS091/hermes-skills.git其他设备只拉取(cron 每小时 git pull)。
使用效果
skill_manage(action='create')或action='patch'创建/更新的技能写入默认目录- 手动或 cron 将新技能复制/推送到共享目录
- 其他设备重启 Hermes 后自动加载新技能
- 不需要改
external_dirs也可用共享目录—Hermes 会自动合并所有目录的技能
Pitfalls
- 创建技能仍写入默认目录 —
skill_manage创建技能默认放在~/.hermes/skills/(或/opt/data/skills/),不会自动写入共享目录。需要手动复制或设 cron 同步。 - NAS 挂载断连 — 如果 NAS 重启或网络问题导致挂载丢失,Hermes 启动时找不到目录不会报错,只是那些技能不加载。建议用
noautofstab 选项避免 boot 时卡住。 - git 冲突 — 如果多台设备同时修改同一个技能文件,git push 会失败。简单场景(一人多设备)很少遇到,信号量低的场景足够用。
- 重启才生效 — Hermes 在启动时扫描技能目录。新技能加入共享目录后,需要
/reset或重启 hermes 进程才能加载。/reload-skills斜杠命令也能触发重扫。
Appendix: Container Hardware Inspection
When a user asks about host hardware (CPU, RAM, motherboard model, memory type) from inside a Docker container where /dev/mem is inaccessible, use this approach:
Step 1: Gather what's accessible from /sys
cat /sys/class/dmi/id/product_name
cat /sys/class/dmi/id/board_name
cat /sys/class/dmi/id/board_vendor
cat /sys/class/dmi/id/sys_vendor
lscpu
cat /proc/cpuinfo | grep "model name" | head -1
free -h
cat /proc/meminfo | head -5Step 2: Install tools (limited inside container)
apt-get install -y dmidecode lshwThese fail to read /dev/mem inside the container — confirm they can't work, then move to inference.
Step 3: Infer memory type from known hardware
- Synology DS918+: Intel Celeron J3455 → DDR3L-1866 SO-DIMM. Board has 4GB soldered + 1× slot. Official max: 8GB, community: up to 16GB.
- Intel Celeron/Pentium J/N series: DDR3L or DDR4 depending on generation.
- For unknown hosts, look up
board_namefrom/sys/class/dmi/id/+ CPU model.
Key Constraints
- Docker containers cannot read /dev/mem — dmidecode/lshw fail with "Can't read memory from /dev/mem". This is a container security boundary, not a bug.
- Memory shown as "15GiB" = binary GiB reading. 15GiB ≈ 16GB, 31GiB ≈ 32GB.
- Swap can mislead — some NAS/NUC setups provision large swap files. Don't confuse swap with physical RAM.
- If exact SPD data is needed, recommend SSH to host (
sudo dmidecode -t memory) or physical inspection.
Pitfalls
- Proxy vs direct access mismatch: The container may have proxy env vars set in /etc/environment, but the proxy server (e.g. 192.168.1.88:7890 on the NAS host) might be down. Always check
curl --max-time 3to the proxy first. - Proxy can come back: User may restart the proxy after you've cleared the config. Always backup before deleting. Restore from
/etc/environment.bak.<YYYYMMDD>if needed. - Chromium installation timeout: On 1.5GHz Celeron (NAS CPU) with slow network, chromium takes 2-5 minutes to download. Use background mode with notify_on_complete so you don't block waiting.
- apt安装卡住(包已下载但配置阶段无响应): 用
ps aux | grep apt找到apt进程,如果包确实已缓存(ls /var/cache/apt/archives/chromium*.deb),可以kill apt进程后手动dpkg安装:kill -9 <apt_pid>rm -f /var/lib/dpkg/lock-frontend /var/lib/dpkg/lockdpkg -i /var/cache/apt/archives/chromium*.debapt-get install -f -y -qq(修复依赖)
注意:如果锁文件存在但为空且没有apt/dpkg进程在运行,直接rm -f即可。
- pip can use proxy env vars: If proxy is set but dead, pip will fail. Either restore proxy or unset it. Check
env | grep -i proxybefore pip installs. pip index versionsfor fast version checking: When checking if packages need updates,pip install --upgrade --dry-runis slow on limited networks (downloads full metadata). Usepip index versions <pkg>instead — it only fetches version metadata and is 3-5× faster. Parse output withgrep "^<pkg>" | head -1to get the latest version string.- Don't mix system and pip installs in the same terminal call: They're independent — run them in parallel in separate background terminals.
- Node v20 ships with npm 9: Fine for most tools. If npm install fails, check
npm config get registry— default registry.npmjs.org is usually fine from China-based Docker. - npm全局模块安装后无法require(node -e找不到模块):
- 安装:
npm install -g typescript ts-node报 exit code 0 但node -e "require('typescript')"失败 - 原因:npm 全局模块装在
/usr/local/lib/node_modules/,但 Node.js 的默认 require 路径不包括这个目录(不会自动搜索 $NODE_PATH) - 修复:先检查物理目录
npm root -g && ls $(npm root -g)确认文件存在 - 验证:
NODE_PATH=/usr/local/lib/node_modules node -e "const ts = require('typescript'); console.log('ok:', ts.version)" - 持久化:将
NODE_PATH=/usr/local/lib/node_modules写入/etc/environment
- 安装:
- Don't install playwright browsers via npx: Use
python -m playwright install chromiumafter pip install playwright — more reliable in Docker. - agent-browser (Hermes内置浏览器工具) 安装:agent-browser 已经在
/opt/hermes/node_modules/agent-browser/下,但需要额外安装 Chrome。运行cd /opt/hermes && npx agent-browser install(通过代理下载 ~177MB)。装好后 cache 在/opt/data/home/.agent-browser/browsers/,但 browser 工具会去/root/.agent-browser/browsers/找。修复:mkdir -p /root/.agent-browser && ln -sf /opt/data/home/.agent-browser/browsers /root/.agent-browser/browsers。npx install 下载较慢(受代理速度影响),设 120s 超时足够。 - Playwright验证:不能只验证import成功——要实际启动浏览器验证:
如果Playwright报错"chromium not found",是没执行from playwright.sync_api import sync_playwright p = sync_playwright().start() b = p.chromium.launch(headless=True) page = b.new_page() page.goto('https://httpbin.org/ip') print(page.inner_text('body')) b.close(); p.stop()playwright install chromium。 - Memory pressure: Installing large packages (chromium ~200MB) while other processes run (Hermes, Docker overlay) on 16GB NAS may slow down. Monitor with
htop. - pip import names ≠ package names:
pillow→import PIL,pymupdf→import fitz,python-pptx→import pptx. Verify with the post-install script above. - B站API风控分级: Even after installing all tools, B站 may block automated scraping. The
/x/web-interface/viewendpoint (video details) is most stable;/x/space/wbi/arc/search(profile video list) is most strict. Always try the simpler endpoint first. Headers required:User-Agent(desktop Chrome) +Referer: https://search.bilibili.comorhttps://www.bilibili.com.