Nocodb 对接私网 PostgreSQL:Private CA 证书 + Traefik 生产部署实战
在私有云或本地机房环境中,PostgreSQL 往往只信任自签或私有 CA 签发的证书。Nocodb 官方提供了一个可直接落地的部署模板 docker-compose/examples/postgres-private-ca,演示如何让 Nocodb 通过私有 CA 证书(ssl.ca 内联 PEM 内容)安全连接外部 PostgreSQL,同时用外部 Redis 承载缓存、用 Traefik 自动签发 Let's Encrypt 证书暴露 HTTPS 入口。读完本文,你将掌握该模板的完整配置方法(db.json、docker.env、docker-compose.yml 逐项说明)、CA 证书转单行字符串的实操命令,以及 Nocodb 源码中 NC_DB_JSON_FILE 的解析链路与 SSL 文件路径的安全边界,从而能将其迁移到自己的内网部署场景。
一、适用场景与组件架构
该模板面向的核心场景是:数据库使用私有或自签 CA 证书(本地机房数据库、私有云环境)。官方 README(docker-compose/examples/postgres-private-ca/README.md)将组件职责概括为三点:
- PostgreSQL:外部数据库,通过自定义 CA 证书建立 SSL 连接;
- Redis:外部部署,Nocodb 仅通过 URL 连接;
- 反向代理:Traefik,基于 Let's Encrypt 自动签发 HTTPS 证书。
对应的 docker-compose.yml 中定义了三个服务:
| 服务 | 镜像 | 职责 |
|---|---|---|
nocodb |
nocodb/nocodb:latest |
主应用,监听 8080,承载 API 与前端,挂载 db.json |
worker |
nocodb/nocodb:latest |
独立 worker 容器,通过 NC_WORKER_CONTAINER: 'true' 标记,仅处理异步任务 |
traefik |
traefik:v3.6 |
80/443 入口,HTTP 强制跳转 HTTPS,ACME 自动证书 |
关键设计点:
- App 与 Worker 共享数据卷:两者都挂载了
nocodb_data:/usr/app/data和./nocodb/db.json:/usr/app/data/db.json,保证元数据库连接配置一致。 - Worker 依赖 App 健康检查通过才启动:
depends_on.nocodb.condition: service_healthy,避免在元数据库尚未就绪时 worker 先启动报错。 - worker 不挂 Traefik 标签:
exposedbydefault=false且 worker 没有任何traefik.http.*标签,意味着 worker 只在内网nocodb-network中工作,不对外暴露端口。 - Traefik 仅挂载只读的 docker.sock:
/var/run/docker.sock:ro用于发现容器标签,ACME 状态持久化在./letsencrypt/acme.json。
从源码结构看,NC_WORKER_CONTAINER 这个开关在主进程入口 packages/nocodb/src/Noco.ts 中被读取:为 true 时跳过 Web 服务启动,仅注册任务监听;Redis 任务模块 packages/nocodb/src/modules/jobs/redis/jobs-redis.ts 也按同一变量决定 worker 模式行为,这与 compose 文件中 app/worker 双容器的拆分是配套的。
二、核心配置:nocodb/db.json(私有 CA 的关键)
模板中的 nocodb/db.json 是整篇部署的灵魂——Nocodb 的元数据库连接以 JSON 文件形式给出,而不是 URL:
{
"client": "pg",
"connection": {
"host": "your-private-db-host.internal",
"port": "5432",
"user": "nocodb",
"password": "CHANGE_ME_db_password",
"database": "nocodb",
"ssl": {
"rejectUnauthorized": true,
"ca": "-----BEGIN CERTIFICATE-----\nPASTE_YOUR_CA_PEM_HERE_AS_ONE_LINE\n-----END CERTIFICATE-----"
}
}
}
逐项说明:
client: "pg":指定使用 PostgreSQL 驱动(对应源码中DriverClient枚举);connection.host/port/user/password/database:标准连接五要素,port以字符串形式给出,示例使用内网域名your-private-db-host.internal,部署时替换为你的私有数据库地址;ssl.rejectUnauthorized: true:这是私有 CA 场景与"裸自签证书"场景的本质区别——开启严格校验后,Node.js TLS 握手会校验证书链,此时必须提供ca,否则握手失败;ssl.ca:内联的单行 CA 证书 PEM 内容,换行符已转义为\n。
为什么用内联内容而不是 caFilePath
源码中其实同时支持两种写法。在 packages/nocodb/src/utils/nc-config/helpers.ts 的 xcUrlToDbConfig 与 同文件 的 metaUrlToDbConfig 中,URL 查询参数形式的 caFilePath / certFilePath / keyFilePath 会在启动时被读取为文件内容并回填到 ssl.ca 等字段。此外,packages/nocodb/src/helpers/resolveSslFileConfig.ts 是所有外部数据库连接(CE 与 EE 的 SqlClientFactory,见 SqlClientFactory.ts)共用的"文件路径 → 内联内容"解析入口。
但 resolveSslFileConfig 带有明确的安全约束:它先执行 validateDbConnectionSslPaths 策略守卫(云环境默认拦截文件路径 SSL,自托管可用 NC_DISABLE_DB_SSL_FILE_PATHS 打开),并且读取失败时只抛出统一文案 Failed to load SSL certificate configuration,避免通过错误差异探测宿主机文件是否存在。也就是说,文件路径方式存在策略与可用性门槛,而本模板采用的内联 ca 字符串写法在容器场景下更直接:证书内容随 db.json 一起以只读方式挂载进容器,不依赖容器内文件系统路径,也不触及上述策略守卫。
CA 证书转单行字符串
README 给出的转换命令如下,将多行 PEM 压缩为一行、换行替换为 \n(同时去除 \r):
awk 'NF {sub(/\r/, ""); printf "%s\\n",$0;}' your-ca.pem
将输出粘贴为 db.json 中 ca 字段的值即可。注意两点:
NF过滤会丢弃空行,因此证书中间的空行不会保留——PEM 主体是连续 base64 行,通常没有空行,一般 PEM 证书可直接使用;- 粘贴后
db.json必须仍是合法 JSON:ca的值是一个 JSON 字符串,其中的\n是 JSON 转义序列,最终解析回真实换行的 PEM 文本。
启动链路:NC_DB_JSON_FILE 如何被消费
docker.env 中声明 NC_DB_JSON_FILE=/usr/app/data/db.json,而容器内该路径正是 ./nocodb/db.json 的挂载点。解析链路可以在源码中完整印证:
- 环境入口 packages/nocodb/src/utils/nc-config/NcConfig.ts 的
createByEnv()读取process.env.NC_DB_JSON_FILE(优先级:NC_DBURL >NC_DB_JSON内联 JSON >NC_DB_JSON_FILE文件); - 文件不存在时直接抛出
NC_DB_JSON_FILE not found: <path>(NcConfig.ts#L110),容器启动即失败,这是部署后最快的排错信号; - 文件内容被
JSON.parse后作为元数据库DbConfig,随后metaDbCreateIfNotExist()(NcConfig.ts#L155-L180)会通过SqlClientFactory.create真正建立一次连接并尝试createDatabaseIfNotExists——这意味着带私有 CA 的 SSL 握手在此刻就会发生:如果ca内容错误或rejectUnauthorized严格校验失败,app 容器会在健康检查窗口内反复报错,不会"静默降级"。
三、docker.env:环境变量逐项说明
docker.env 全部内容及说明:
# Database
NC_DB_JSON_FILE=/usr/app/data/db.json
# Redis
NC_REDIS_URL=redis://your-redis-host:6379
# Public URL (email links, webhooks, OAuth redirects). Set to your public-facing URL.
NC_SITE_URL=https://nocodb.example.com
# Settings
NC_SECURE_ATTACHMENTS=true
NC_DISABLE_MUX=true
| 变量 | 示例值 | 说明 |
|---|---|---|
NC_DB_JSON_FILE |
/usr/app/data/db.json |
元数据库连接 JSON 的容器内路径,指向 compose 挂载的 ./nocodb/db.json |
NC_REDIS_URL |
redis://your-redis-host:6379 |
外部 Redis 地址。模板要求你替换为真实地址;Redis 承担缓存与任务队列职责 |
NC_SITE_URL |
https://nocodb.example.com |
对外公开 URL,用于邮件链接、Webhook 回调、OAuth 重定向。应设置为 Traefik 路由的域名,且必须是最终对外可达的 HTTPS 地址 |
NC_SECURE_ATTACHMENTS |
true |
附件安全模式。源码 packages/nocodb/src/modules/noco.module.ts 中该值为 true 时改变附件模块的注册方式,适合对外暴露的部署 |
NC_DISABLE_MUX |
true |
关闭多路复用/内嵌通道,配合外部 PostgreSQL + 外部 Redis 的完全外部化部署 |
修改要点(来自 README 的 Usage 清单):
docker.env:设置NC_REDIS_URL;docker-compose.yml:把nocodb.example.com替换为你的域名(注意traefik.http.routers.nocodb.rule中的 Host 规则与NC_SITE_URL保持一致),把admin@example.com(ACME 邮箱)替换为真实邮箱;nocodb/db.json:设置数据库主机、凭据、端口,并把ca替换为你的 CA 证书单行内容。
四、docker-compose.yml:完整编排解析
App 服务(nocodb)
nocodb:
image: nocodb/nocodb:latest
env_file: docker.env
deploy:
mode: replicated
replicas: 1
restart: unless-stopped
volumes:
- nocodb_data:/usr/app/data
- ./nocodb/db.json:/usr/app/data/db.json
networks:
- nocodb-network
labels:
- 'traefik.enable=true'
- 'traefik.http.routers.nocodb.rule=Host(`nocodb.example.com`)'
- 'traefik.http.routers.nocodb.entrypoints=websecure'
- 'traefik.http.routers.nocodb.tls.certresolver=letsencrypt'
healthcheck:
test: ['CMD-SHELL', 'wget -q --tries=1 --spider http://localhost:8080/api/v1/health || exit 1']
interval: 30s
timeout: 5s
retries: 5
start_period: 30s
- 健康检查请求
http://localhost:8080/api/v1/health,30 秒探测一次、5 秒超时、5 次重试、30 秒启动宽限——这与 worker 的service_healthy依赖联动:只有 app 真正通过健康检查(即元数据库连接已建立、私有 CA 握手成功),worker 才会启动; deploy.mode: replicated与replicas: 1是 swarm 风格声明,在普通docker compose下等价于单副本,可保留原样。
Worker 服务(worker)
worker:
image: nocodb/nocodb:latest
env_file: docker.env
environment:
NC_WORKER_CONTAINER: 'true'
depends_on:
nocodb:
condition: service_healthy
restart: unless-stopped
volumes:
- nocodb_data:/usr/app/data
- ./nocodb/db.json:/usr/app/data/db.json
networks:
- nocodb-network
worker 与 app 共用 env_file 与数据卷,唯一差异是 NC_WORKER_CONTAINER: 'true'(该变量在 packages/nocodb/src/Noco.ts 处被主流程读取,worker 模式下不对外提供 Web 端口,也不注册面向用户的 Controller,参见 packages/nocodb/src/modules/auth/auth.module.ts)。拆分 worker 的收益是把导入导出、报表类异步任务与 API 请求隔离,互不抢占资源。
Traefik 服务(traefik)
traefik:
image: traefik:v3.6
command:
- '--providers.docker=true'
- '--providers.docker.exposedbydefault=false'
- '--entryPoints.web.address=:80'
- '--entryPoints.websecure.address=:443'
- '--entryPoints.web.http.redirections.entryPoint.to=websecure'
- '--entryPoints.web.http.redirections.entryPoint.scheme=https'
- '--certificatesresolvers.letsencrypt.acme.httpchallenge.entrypoint=web'
- '--certificatesresolvers.letsencrypt.acme.email=admin@example.com'
- '--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json'
ports:
- '80:80'
- '443:443'
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- ./letsencrypt:/letsencrypt
restart: unless-stopped
networks:
- nocodb-network
exposedbydefault=false:只有显式打了traefik.enable=true标签的服务才暴露,worker 因此天然隐身;- 80 端口仅用于 HTTP→HTTPS 301 重定向与 ACME HTTP-01 挑战,业务流量全部走 443 的
websecure; - ACME 状态落在宿主机
./letsencrypt/acme.json,容器重建不丢证书; - 前提条件:域名 A 记录必须指向本服务器公网 IP,否则 HTTP 挑战无法通过、证书签发失败。
五、部署步骤与验证
README 给出的标准流程(在仓库 docker-compose/ 目录下执行;cp 的目标可以放到任意位置,但需保持 ./nocodb/db.json、./letsencrypt 等相对路径结构):
cp -r examples/postgres-private-ca ./my-deployment
cd my-deployment
# 1. 编辑 docker.env: 设置 NC_REDIS_URL
# 2. 编辑 docker-compose.yml:
# - 将 nocodb.example.com 替换为你的域名
# - 将 admin@example.com 替换为你的邮箱
# 3. 编辑 nocodb/db.json:
# - 填写数据库 host、凭据、port
# - 将 ca 值替换为你的 CA 证书内容(单行,\n 表示换行)
docker compose up -d
部署后的建议验证顺序:
docker compose ps观察三个服务状态,重点确认nocodb的healthy——它意味着带私有 CA 的元数据库连接已经建立成功(NcConfig.metaDbCreateIfNotExist在启动时完成了真实握手);- 查看 app 日志:若 CA 内容有误,会看到元数据库连接/建库失败相关报错,而不是前端 502;
- 访问
https://<你的域名>,确认证书由 Let's Encrypt 签发、页面可登录; - 确认
NC_SITE_URL与域名一致后,测试一次邀请邮件或 Webhook,验证出站链接指向正确。
六、迁移到私有环境的检查清单
- [ ] CA 证书 PEM 已用
awk命令转为单行,db.json整体可被JSON.parse(可用python3 -m json.tool nocodb/db.json或jq快速校验); - [ ]
ssl.rejectUnauthorized保持true,ca为签发数据库证书的 CA(或完整证书链中缺的上级 CA),而不是数据库服务器证书本身; - [ ]
docker.env中NC_REDIS_URL指向真实 Redis,网络策略允许容器访问 Redis 6379 与私有数据库 5432; - [ ]
docker-compose.yml的 Host 规则、ACME 邮箱、NC_SITE_URL三处域名保持一致; - [ ] 80/443 端口对公网开放(ACME 挑战与 HTTPS 入口需要),而 8080 端口不对外暴露(模板未发布该端口,仅容器网络内可达)。
参考文件
- 模板文档:docker-compose/examples/postgres-private-ca/README.md
- 编排文件:docker-compose/examples/postgres-private-ca/docker-compose.yml、docker.env、nocodb/db.json
- 配置解析:NcConfig.ts(
NC_DB_JSON_FILE读取与元数据库初始化)、helpers.ts(caFilePath等 URL 形式 SSL 参数解析) - SSL 解析入口:resolveSslFileConfig.ts、SqlClientFactory.ts
- worker 模式开关:Noco.ts、noco.module.ts
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00