---
date: 2026-05-08
type: error
tags: [temporal, docker, ops, dev-env, auto-setup, latent-bomb]
---

# Temporal 容器三重坑：auto-setup 时序、CLI 地址、custom namespace 不自动建

## 现场

FFAI UAT 这台机器上 `ffws-dev-backend-temporal-worker` PM2 进程 ↺=2965 在持续 crash loop 9 天，没人管。错误显示：

```
TransportError: Namespace dev was not found
GetNamespace operation failed. Error pq: database "temporal" does not exist
```

挖到底发现是三重坑叠加：

## 坑 1：auto-setup 镜像首次启动时序

`temporalio/auto-setup:1.25.2` 的设计意图：**容器启动时自动跑 schema 创建 + 注册 default namespace**，开箱即用。

但 `auto-setup.sh` 只检测一次 PG 可用，失败就直接进入 server 启动循环（不再重试 schema setup）。**如果首次 docker compose up 时 PG 慢起 1-3 秒，auto-setup 就漏过了创建 schema 的窗口**。

更隐蔽的是 — 漏过后容器仍标 `Up (healthy)`（healthcheck 只看 server 端口活着，不检查 schema），运维以为正常。

实际 server 跑起来一直在报：
```
ListNamespaces operation failed. 
Error: no usable database connection found
```

但只有 worker 真去连的时候才会暴露出 worker 端的 crash。如果项目没有 worker（只是 server 待用），9 天都不会有人发现。

### 修复路径

```bash
# 在 PG 里手动建两个空 db
docker exec <pg-container> psql -U <user> -d postgres \
  -c "CREATE DATABASE temporal; CREATE DATABASE temporal_visibility;"

# 重启 temporal 容器（auto-setup 检测到 db 存在但没 schema，会建表）
docker restart <temporal-container>

# 等 30 秒让 auto-setup 跑完 + register default namespace
sleep 30
docker exec <pg-container> psql -U <user> -d temporal -c "\dt" | wc -l
# 应该有 36+ 行（14+ 张 Temporal 表）
```

## 坑 2：tctl / temporal CLI 在容器内默认地址不通

进 temporal 容器跑命令：

```bash
docker exec ffws-dev-temporal tctl namespace list
# Error: connection refused dial tcp 127.0.0.1:7233

docker exec ffws-dev-temporal temporal --address localhost:7233 ...
# 同样 connection refused
```

**根因**：Temporal frontend 服务 bind 到容器 hostname（即容器名），**不监听 `127.0.0.1`**。容器内 `localhost` / `127.0.0.1` 解析的 loopback 不是 frontend 的监听地址。

### 修复

用容器名（也是 docker-compose 服务名）作为地址：

```bash
docker exec ffws-dev-temporal temporal \
  --address temporal:7233 \
  operator namespace list
```

`temporal:7233` 走 docker network 的 DNS，能解到 frontend 真实绑定的 IP。

## 坑 3：业务用的 custom namespace 不在 auto-setup 范围

`auto-setup` 只建一个 namespace：**`default`**。

但本项目业务代码（`backend/src/core/workflow/temporal/worker.ts` 等）配的是 namespace = `dev`（dev 环境）/ `uat` 等 custom 名。auto-setup 不建这些，worker 启动时报 "Namespace X is not found" 直接挂。

### 修复

```bash
docker exec ffws-dev-temporal temporal \
  --address temporal:7233 \
  operator namespace create \
  --namespace dev \
  --retention 7d
# Output: Namespace dev successfully registered.
```

retention 是工作流历史保留天数，dev 用 7 天足够（生产建议 30+ 天）。

## 完整修复脚本（任何环境通用）

```bash
PG=ffws-dev-postgres
TEMPORAL=ffws-dev-temporal
PG_USER=ffws_dev
APP_NAMESPACE=dev   # 业务代码里写的 namespace 名

# 1. 建空 db
docker exec $PG psql -U $PG_USER -d postgres \
  -c "CREATE DATABASE temporal; CREATE DATABASE temporal_visibility;"

# 2. 重启 temporal 让 auto-setup 跑 schema
docker restart $TEMPORAL
sleep 40

# 3. 验证 schema (>= 14 张表)
docker exec $PG psql -U $PG_USER -d temporal -c "\dt" | wc -l

# 4. 注册业务 namespace
docker exec $TEMPORAL temporal --address temporal:7233 \
  operator namespace create --namespace $APP_NAMESPACE --retention 7d

# 5. 重启 worker 验证
pm2 restart <worker-pm2-name>
sleep 30
pm2 list --no-color | grep <worker-pm2-name>  # ↺ 应不再增长
```

## 治理建议

1. **`scripts/dev/dev.sh up` 类启动脚本**应该在 `docker compose up` 之后加一段：
   - 等 temporal server ready（轮询 namespace list）
   - 检测 schema 是否存在（查 `temporal` db 表数）
   - 不存在则手动 CREATE db + restart container
   - 检测业务 namespace 是否存在，不存在则注册
2. **healthcheck 加严**：当前 temporal 容器的 healthcheck 只看端口活着，应改成"能 list namespace + 业务 namespace 存在"
3. **PM2 进程长 crash loop 监控**：worker 9 天 crash 没人管的根因是没有报警。↺ 超过某阈值（如 100）应该 trigger 报警

## 适用范围

- 任何用 `temporalio/auto-setup` 镜像 + Postgres backend 的项目
- 特别是首次部署 / 用 setup-worktree 重建 dev 环境的场景
- 项目用 custom namespace（不只是 default）的所有部署

## 关联

- 工单 #242（顺手发现的副问题）
- ffws-dev-backend-temporal-worker（修复目标）
