I Run 85 Docker Containers as a Solo Founder. Here's the Bash That Keeps It Alive.
I Run 85 Docker Containers as a Solo Founder. Here’s the Bash That Keeps It Alive.
作为一名独立创始人,我运行着 85 个 Docker 容器。以下是维持其运行的 Bash 脚本。
85 containers. 24 PostgreSQL databases. 67 domains. 232 cron jobs. One developer. 120 EUR/month in Hetzner bills. This is not a startup fantasy pitch. This is my production infrastructure for a SaaS ecosystem serving German golf clubs, a golf school management platform, a community platform, a CRM, and an auth service. Every customer gets their own database. Physical tenant isolation, not software filters. People tell me this cannot work. The containers disagree. 85 个容器,24 个 PostgreSQL 数据库,67 个域名,232 个定时任务,只有一名开发者,每月 120 欧元的 Hetzner 账单。这不是创业公司的幻想推介,而是我为 SaaS 生态系统构建的生产环境,服务对象包括德国高尔夫俱乐部、高尔夫学校管理平台、社区平台、CRM 和身份验证服务。每个客户都有自己的独立数据库,这是物理层面的租户隔离,而非软件过滤。人们说这行不通,但容器们证明他们错了。
The Stack: Next.js for all frontends. Single-tenant PostgreSQL per customer (Supabase stacks). Docker on bare metal. Coolify for deployment orchestration. Traefik as the reverse proxy handling 67 domains. Two Hetzner servers in Germany. Total infrastructure cost: 120 EUR/month. 技术栈:所有前端均使用 Next.js;每个客户拥有独立的 PostgreSQL 数据库(Supabase 架构);在裸机上运行 Docker;使用 Coolify 进行部署编排;Traefik 作为反向代理处理 67 个域名。两台位于德国的 Hetzner 服务器,总基础设施成本为每月 120 欧元。
The single-tenant architecture is a deliberate trade-off. Multi-tenant saves infrastructure cost, but one RLS bug exposes every customer’s data. One compromised tenant enables lateral movement to all others. GDPR Article 17 deletion in multi-tenant requires complex cross-tenant queries. In single-tenant, deletion is DROP DATABASE. No residual risk. The cost is more operational complexity. Which is exactly why automation is not optional.
这种单租户架构是我深思熟虑后的权衡。多租户虽然节省基础设施成本,但一个行级安全(RLS)漏洞就可能导致所有客户数据泄露,一个租户被攻破就会导致横向攻击蔓延至其他所有租户。在多租户环境下,GDPR 第 17 条规定的删除操作需要复杂的跨租户查询;而在单租户架构中,删除只需执行 DROP DATABASE,没有任何残留风险。代价是更高的运维复杂度,这正是自动化成为“必选项”的原因。
176 Guard Rules: The Immune System
176 条防护规则:免疫系统
My AI agents (Claude Code with custom hooks) execute roughly 80% of daily development and operations work. That is dangerous without constraints. So I built a guard system: 176 shell scripts that fire on every command, every file edit, every session end. The architecture is simple. Four dispatchers route to context-specific guards: 我的 AI 智能体(带有自定义钩子的 Claude Code)执行了大约 80% 的日常开发和运维工作。如果没有约束,这非常危险。因此,我构建了一个防护系统:176 个 Shell 脚本,会在每次命令执行、文件编辑和会话结束时触发。架构很简单:四个调度器将任务路由到特定上下文的防护脚本中:
#!/bin/bash
# Pre-Bash-Dispatcher: Loads guards based on command profile.
# Not all 176 guards fire on every command. Profiling classifies
# each command (git, docker, npm, database, deploy, comms) and
# loads only relevant guards.
set -uo pipefail
GUARDS_DIR="$(dirname "$0")/guards"
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
# 8 security gates fire ALWAYS, non-negotiable:
# tabu-gate, pii-gate, api-key-guard, secret-output-guard,
# pre-exec-file-scanner, gate-file-guard (guards protect themselves),
# agent-control-policy, main-push-guard
PROFILE=$(classify_command "$CMD") # git|docker|npm|database|deploy|...
for guard in "$GUARDS_DIR/$PROFILE"/*.sh; do
result=$("$guard" "$CMD" "$SESSION_ID")
if echo "$result" | jq -e '.permissionDecision == "deny"' > /dev/null 2>&1; then
echo "$result"
exit 0
fi
done
The guards protect against real problems I have encountered: agents pushing directly to main, leaking PII into logs, deleting production containers, skipping pre-mortem checks before destructive operations, or committing API keys. 96% of all rules (83 out of 86) are enforced automatically. The remaining 3 require human judgment. No governance document that nobody reads. Executable rules that block before damage happens. 这些防护脚本保护我免受实际遇到过的问题困扰:智能体直接向主分支推送代码、将个人隐私信息(PII)泄露到日志中、删除生产环境容器、在执行破坏性操作前跳过预检查,或提交 API 密钥。96% 的规则(86 条中的 83 条)是自动执行的,剩下的 3 条需要人工判断。这不是没人看的治理文档,而是能在损害发生前进行拦截的可执行规则。
The Self-Healing Watchdog
自愈看门狗
Containers disappear. Coolify deployments fail silently. Traefik loses backend connections. At 85 containers, something breaks every week. The watchdog runs every 5 minutes via cron and restores service from the last known good state: 容器会消失,Coolify 部署会静默失败,Traefik 会丢失后端连接。在 85 个容器的规模下,每周总会出点问题。看门狗程序通过 Cron 每 5 分钟运行一次,并从最后已知的良好状态恢复服务:
#!/bin/bash
# live-app-watchdog.sh
# Detects missing Coolify containers, restores from last local image.
set -uo pipefail
LOG="/var/log/live-app-watchdog.log"
STATE_DIR="/var/run/live-app-watchdog"
APPS=( "golf-club-community|golfclub-app.de" "golfschul-app|golfschul-app.de" ... )
for entry in "${APPS[@]}"; do
IFS='|' read -r name domain <<< "$entry"
container=$(docker ps -q --filter "name=$name" 2>/dev/null)
if [ -z "$container" ]; then
log "MISSING: $name ($domain)"
http_code=$(curl -sS -o /dev/null -w "%{http_code}" "https://$domain/api/health" --max-time 5 2>/dev/null)
if [ "$http_code" != "200" ]; then
last_image=$(docker images --format '{{.Repository}}:{{.Tag}}' | grep "$name" | head -1)
if [ -n "$last_image" ]; then
docker run -d --name "${name}-emergency" --network coolify "$last_image"
notify_once "critical" "Emergency container started" "$name restored"
fi
fi
fi
done
Emergency containers are temporary. The watchdog notifies me via ntfy.sh push notification, and the next Coolify deployment replaces the emergency container with a proper one. The point is: the customer never notices. 紧急容器只是临时的。看门狗会通过 ntfy.sh 推送通知我,下一次 Coolify 部署会自动用正式容器替换掉紧急容器。重点是:客户完全察觉不到。
The Crystallization Loop: Mistakes Become Permanent Rules
结晶循环:错误转化为永久规则
This is the mechanism that makes the system improve without me writing new rules. When an AI agent makes a mistake, the learning gets captured. When that learning proves useful across 3+ sessions with a quality score of 4 or higher, it crystallizes into a permanent rule. 这是让系统无需我手动编写新规则就能自我进化的机制。当 AI 智能体犯错时,经验会被捕获。当这些经验在 3 次以上的会话中被证明有效且质量评分达到 4 分或以上时,它就会“结晶”成为一条永久规则。
The numbers after 18 months: 211 rules crystallized from agent experience. Not written by a human. Distilled from 1,448 autonomous tasks. 1,087 completed successfully. 88% success rate. The crystallization loop is the core of what I call the GRIP framework (Guards, Resilient, Isolated, Public). Guards prevent mistakes. When mistakes happen anyway, the resilience loop turns them into new guards. 18 个月后的数据:从智能体经验中结晶出了 211 条规则。它们不是由人类编写的,而是从 1,448 个自主任务中提炼出来的。其中 1,087 个任务成功完成,成功率达 88%。这个结晶循环是我所称的 GRIP 框架(防护、弹性、隔离、公开)的核心。防护机制防止错误发生;当错误不可避免地发生时,弹性循环会将它们转化为新的防护规则。