一键导入
dockerfile
Write Dockerfiles for sandbox environments. Triggers on "Dockerfile", "docker image", "FROM", "ENTRYPOINT", "docker build", "sandbox image".
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Write Dockerfiles for sandbox environments. Triggers on "Dockerfile", "docker image", "FROM", "ENTRYPOINT", "docker build", "sandbox image".
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Use the ReinforceNow CLI for RLHF training. Use when running rnow commands, initializing projects, submitting training runs, testing rollouts, running evals, or downloading models. Triggers on "rnow", "rnow init", "rnow run", "rnow test", "rnow eval", "rnow download", "rnow deploy", "rnow login", "training run".
Configure ReinforceNow training runs with config.yml and train.jsonl. Also covers converting HuggingFace datasets to ReinforceNow format. Triggers on "config.yml", "train.jsonl", "training config", "batch_size", "group_size", "max_turns", "qlora", "HuggingFace", "dataset", "convert dataset".
Write reward functions for ReinforceNow RL training. Use when creating @reward decorated functions, writing rewards.py, using precondition rewards, sandbox rewards, llm_judge, or math-verify. Triggers on "reward function", "@reward", "RewardArgs", "precondition", "llm_judge", "math-verify", "math reward", "latex".
Write tool functions for ReinforceNow agent training. Use when creating @tool decorated functions, writing tools.py, or sandbox tools. Triggers on "@tool", "tools.py", "tool function", "function calling", "agent tools", "sandbox".
Format train.jsonl training data for ReinforceNow. Use when creating train.jsonl, formatting training entries, using tools/rewards per entry, or setting up sandbox/docker. Triggers on "train.jsonl", "training data", "docker", "sandbox", "entry format".
Configure ReinforceNow training runs with config.yml and train.jsonl. Also covers converting HuggingFace datasets to ReinforceNow format. Triggers on "config.yml", "train.jsonl", "training config", "batch_size", "group_size", "max_turns", "qlora", "HuggingFace", "dataset", "convert dataset".
| name | dockerfile |
| description | Write Dockerfiles for sandbox environments. Triggers on "Dockerfile", "docker image", "FROM", "ENTRYPOINT", "docker build", "sandbox image". |
"docker": "local/foo" → looks for Dockerfile.foo in project root. No registry push needed.
ReinforceNow runs /start.sh sleep infinity for all sandbox images.
Modal ignores Docker ENTRYPOINT/CMD, so we explicitly run /start.sh sleep infinity. Your Dockerfile must define /start.sh to initialize the container.
At termination, ReinforceNow automatically runs /terminate.sh (if it exists) before killing the sandbox.
| Script | When Called | Purpose |
|---|---|---|
/start.sh | Container startup | Initialize services, acquire resources |
/terminate.sh | Before termination | Release resources, cleanup |
Create /start.sh that initializes your service then runs the passed command:
FROM some-image:latest
EXPOSE 8931
USER root
# /start.sh: Called at container startup with "sleep infinity" as args
# 1. Initialize your service (acquire resources, start servers)
# 2. exec "$@" runs "sleep infinity" to keep container alive
RUN echo '#!/bin/bash' > /start.sh && \
echo 'set -e' >> /start.sh && \
echo 'your-server-command --port 8931 &' >> /start.sh && \
echo 'sleep 3' >> /start.sh && \
echo 'exec "$@"' >> /start.sh && \
chmod +x /start.sh
# /terminate.sh: Called automatically before sandbox termination
RUN echo '#!/bin/bash' > /terminate.sh && \
echo 'echo "Cleaning up..."' >> /terminate.sh && \
echo '# YOUR CLEANUP CODE HERE' >> /terminate.sh && \
chmod +x /terminate.sh
ENTRYPOINT ["/start.sh"]
CMD ["sleep", "infinity"]
When ReinforceNow runs /start.sh sleep infinity:
/start.sh initializes your serviceexec "$@" runs sleep infinity to keep container alive/terminate.sh runs automatically, then container killedRUN echo instead of COPY <<'EOF'USER root to create files, then USER <original> after--platform linux/amd64 for cloudFROM mcp/playwright
EXPOSE 8931
USER root
# Base image entrypoint: node cli.js --headless --browser chromium --no-sandbox
# --port 8931 --host 0.0.0.0: expose as HTTP/SSE server
# --allowed-hosts '*': allow any hostname (required for cloud tunnels, otherwise 403 Forbidden)
RUN echo '#!/bin/bash' > /start.sh && \
echo 'node cli.js --headless --browser chromium --no-sandbox --port 8931 --host 0.0.0.0 --allowed-hosts "*" &' >> /start.sh && \
echo 'sleep 5' >> /start.sh && \
echo 'exec "$@"' >> /start.sh && \
chmod +x /start.sh
ENTRYPOINT ["/start.sh"]
FROM some-image:latest
USER root
RUN echo '#!/bin/bash' > /start.sh && \
echo 'some-service &' >> /start.sh && \
echo 'for i in {1..30}; do curl -s http://localhost:PORT/health && break; sleep 1; done' >> /start.sh && \
echo 'exec "$@"' >> /start.sh && \
chmod +x /start.sh
USER original-user # switch back if needed
ENTRYPOINT ["/start.sh"]
# 1. Inspect the base image
docker pull IMAGE:TAG
docker inspect IMAGE:TAG --format='{{json .Config.Cmd}}'
docker run --rm IMAGE:TAG id # check user
docker run --rm IMAGE:TAG cat supervisord.conf 2>/dev/null # find startup commands
# 2. Build and test container starts
docker build --platform linux/amd64 -t test:latest -f Dockerfile.foo .
docker run --rm test:latest echo "SUCCESS"
# 3. Test with Modal's behavior (simulates how Modal runs it)
docker run --rm test:latest sleep 5 # Should start your service + sleep
# 4. TEST YOUR ACTUAL TOOL (critical!)
docker run --rm test:latest python -c "from tools import browse; print(browse('https://example.com'))"
The most common mistake: Testing that the container starts but not testing the actual tool function with real inputs.
CRITICAL: Many MCP servers (especially Playwright MCP) restrict connections to localhost by default, even when bound to 0.0.0.0. When Modal's tunnel connects, the server sees a non-localhost hostname and returns 403 Forbidden.
Fix: Add --allowed-hosts '*' (or --allowed-hosts "*" in shell scripts) to allow external connections.
FROM python:3.11-slim
RUN apt-get update && apt-get install -y curl jq && rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir requests rnow
# Creates Kernel browser session and starts replay recording
RUN echo '#!/bin/bash' > /start.sh && \
echo 'set -e' >> /start.sh && \
echo 'RESP=$(curl -sS --max-time 60 -X POST "https://api.onkernel.com/browsers" -H "Authorization: Bearer $KERNEL_API_KEY" -H "Content-Type: application/json" -d "{\"timeout_seconds\": 300, \"headless\": false, \"viewport\": {\"width\": 1024, \"height\": 768}}")' >> /start.sh && \
echo 'SESSION_ID=$(echo "$RESP" | jq -r ".session_id")' >> /start.sh && \
echo 'echo "$SESSION_ID" > /tmp/kernel_session_id' >> /start.sh && \
echo 'curl -sS -X POST "https://api.onkernel.com/browsers/$SESSION_ID/replays" -H "Authorization: Bearer $KERNEL_API_KEY" -H "Content-Type: application/json" -d "{}" || true' >> /start.sh && \
echo 'exec "$@"' >> /start.sh && \
chmod +x /start.sh
# Cleanup: delete browser session
RUN echo '#!/bin/bash' > /terminate.sh && \
echo 'SESSION_ID=$(cat /tmp/kernel_session_id 2>/dev/null)' >> /terminate.sh && \
echo '[ -n "$SESSION_ID" ] && curl -sS --max-time 10 -X DELETE "https://api.onkernel.com/browsers/$SESSION_ID" -H "Authorization: Bearer $KERNEL_API_KEY" || true' >> /terminate.sh && \
chmod +x /terminate.sh
ENTRYPOINT ["/start.sh"]
CMD ["sleep", "infinity"]
When a rollout completes, ReinforceNow automatically runs /terminate.sh before terminating the sandbox. Use this to release external resources (sessions, connections, etc.).
The CLI shows cleanup status:
Rollout 1: ✓ reward=2.000 [cleanup ✓] # terminate.sh ran successfully
Rollout 1: ✓ reward=2.000 [cleanup ✗] # terminate.sh not found or failed
Rollout completes:
1. ReinforceNow calls: sandbox.exec("/terminate.sh") ← Your cleanup runs
2. ReinforceNow calls: sandbox.terminate() ← Container killed
No SIGTERM traps needed - we explicitly call your script before killing the container.
Add your cleanup logic to /terminate.sh:
# Option 1: Inline in Dockerfile
RUN echo '#!/bin/bash' > /terminate.sh && \
echo '# YOUR CLEANUP CODE HERE' >> /terminate.sh && \
echo 'echo "Cleaning up..."' >> /terminate.sh && \
echo 'curl --max-time 5 -X POST "https://api.example.com/release" || true' >> /terminate.sh && \
chmod +x /terminate.sh
# Option 2: Copy from file
COPY terminate.sh /terminate.sh
RUN chmod +x /terminate.sh
/terminate.sh (or /app/terminate.sh)chmod +x)--max-time 5) to avoid hanging|| true) - don't fail the cleanup#!/bin/bash
# /terminate.sh - deletes Kernel browser session
if [ -f /tmp/kernel_session_id ]; then
SESSION_ID=$(cat /tmp/kernel_session_id)
echo "Deleting Kernel browser $SESSION_ID..."
curl -sS --max-time 10 -X DELETE \
"https://api.onkernel.com/browsers/$SESSION_ID" \
-H "Authorization: Bearer $KERNEL_API_KEY" || true
echo "Done"
fi
#!/bin/bash
# /terminate.sh - close DB connection
if [ -n "$DB_CONNECTION_ID" ]; then
echo "Closing database connection..."
curl --max-time 5 -X DELETE \
"https://db.example.com/connections/$DB_CONNECTION_ID" || true
fi
#!/bin/bash
# /terminate.sh - generic cleanup
echo "Running cleanup..."
# Kill any background processes
pkill -f "my-server" || true
# Remove temp files
rm -rf /tmp/session_* || true
# Notify external service
curl --max-time 5 -X POST "https://api.example.com/cleanup" \
-d '{"container_id": "'$HOSTNAME'"}' || true
echo "Cleanup complete"
| /terminate.sh | SIGTERM trap | |
|---|---|---|
| Complexity | Simple script | Complex bash (traps, PID handling, wait loops) |
| Guaranteed to run | Yes (we call it explicitly) | No (race with SIGKILL) |
| PID 1 issues | None | Yes (signals may not reach your process) |
| User code | Standalone file | Buried in entrypoint |
For ReinforceNow/Modal, /terminate.sh is simpler and more reliable
| Issue | Fix |
|---|---|
Server not starting | Modal ignores ENTRYPOINT - use wrapper script pattern above |
Permission denied | Add USER root before creating files |
Heredoc not supported | Use RUN echo '...' > file |
Platform mismatch | Build with --platform linux/amd64 |
Server not responding | Check supervisord.conf or similar for actual startup commands |
403 Forbidden | Add --allowed-hosts '*' to MCP server command (localhost restriction) |
Cleanup not running | Create /terminate.sh instead of using trap |