Nocodb 对接私网 PostgreSQL:Private CA 证书 + Traefik 生产部署实战
【免费下载链接】nocodb🔥 🔥 🔥 A Free & Self-hostable Airtable Alternative项目地址: https://gitcode.com/GitHub_Trending/no/nocodb
在私有云或本地机房环境中,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-networkworker 与 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-networkexposedbydefault=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
【免费下载链接】nocodb🔥 🔥 🔥 A Free & Self-hostable Airtable Alternative项目地址: https://gitcode.com/GitHub_Trending/no/nocodb
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考