testcontainers-go 连不上 PostgreSQL 容器的主因是端口映射未生效、容器网络监听地址错误(需设为 0.0.0.0)、或未等 PostgreSQL 就绪即 dial;应动态获取 host 和 mapped port,配置 sslmode=disable,并用 ForListeningPort 等就绪检查。

testcontainers-go 能跑通,但连不上 PostgreSQL 容器?不是代码写错了,大概率是端口、网络或就绪时机三处没对上。
PostgreSQL 容器启动后 dial 失败的常见原因
本地 docker ps 看到容器在跑,net.Dial("tcp", "localhost:5432", ...) 却报 dial tcp [::1]:5432: connect: connection refused,问题通常不在 Go 代码本身:
- PostgreSQL 默认监听
127.0.0.1,而 testcontainers 启动的容器里这个地址只对容器自己有效;宿主机必须通过桥接网络访问,所以容器得监听0.0.0.0 -
ExposedPorts: []string{"5432/tcp"}只是声明要暴露,不等于端口已映射成功;硬写localhost:5432必然失败 - 容器创建返回后,PostgreSQL 还在初始化——日志还没打出
database system is ready to accept connections,此时 dial 就是徒劳
正确获取可连接地址的步骤
别拼字符串,用 API 动态拿:
- 调用
container.Host(ctx)得到宿主机可访问的 IP(通常是localhost,但 WSL2 下可能是host.docker.internal) - 调用
container.MappedPort(ctx, "5432/tcp")得到实际绑定的 host 端口(比如32789) - 组合成
host:port,再塞进sqlx.Open或pgx.Connect - 连接字符串里必须加
sslmode=disable,否则默认走 SSL,而 testcontainer 启的 PG 不带证书
必须配置的就绪检查(WaitingFor)
不加就绪检查,等于让测试“赌运气”。PostgreSQL 启动慢,尤其首次初始化时:
立即学习“go语言免费学习笔记(深入)”;
- 推荐用
wait.ForListeningPort("5432/tcp"),比ForLogOutput更稳——日志输出时机不可靠,且不同镜像日志格式可能变 - 设超时:用
WithStartupTimeout(60 * time.Second),CI 拉镜像慢,60 秒是底线 - 加轮询间隔:
WithPollInterval(2 * time.Second),避免高频探测干扰 Docker daemon - 完整示例:
WaitingFor: wait.ForListeningPort("5432/tcp"). WithStartupTimeout(60 * time.Second). WithPollInterval(2 * time.Second),
TestMain 中容器生命周期管理的关键点
单个测试函数里 defer container.Terminate(ctx) 看似简单,但容易漏、不可靠、不兼容并行:
- 测试 panic 时,
defer可能不执行,残留容器越积越多 - 多个测试共用一个数据库容器时,
Terminate放在单个TestXxx里会互相干扰 - 正确做法:在
TestMain中启动全局testcontainers.NewReaper,并在os.Exit(m.Run())前调用reaper.Cleanup() - Reaper 能兜底清理所有未终止容器,哪怕测试中途 crash 也能回收
- 别忘了给
GenericContainer加Started: true,否则容器不会真正运行
最易被忽略的是:PostgreSQL 初始化 SQL 只在空数据目录首次启动时执行。反复跑测试却没清 volume,init 脚本就静默跳过——这不是 testcontainers 的 bug,是它的设计前提。


















