我第一次认真看 NocoDB,是因为一个朋友把公司客户表、项目进度、采购记录全塞在 Excel 里。文件名从 客户表_final.xlsx 变成 客户表_final_真的最终版.xlsx,最后谁也不知道哪个才是最新。
Airtable 能解决这个问题,但团队人数一多,价格就不便宜。更麻烦的是,很多小团队的数据本来就在 MySQL、PostgreSQL 里,只是没有一个给运营、销售、客服能直接用的表格界面。
NocoDB 的定位很直接:把数据库变成类似 Airtable 的可视化表格。你可以在浏览器里筛选、排序、建视图、导入 CSV、生成 API,还能让不会写 SQL 的同事直接维护数据。
这篇文章讲的是怎么把 NocoDB 跑在自己的 VPS 上。我们不用黑盒安装脚本,而是用 Docker Compose 手写一套能看懂、能备份、能迁移的配置。
适合这些场景:
- 小团队想要一个开源 Airtable 替代品。
- 你有一堆 Excel / CSV,想搬到数据库里统一管理。
- 运营、客服、销售需要改数据,但不应该直接进数据库。
- 你想给 PostgreSQL / MySQL 套一个低代码管理界面。
- 你已经有 VPS,不想再为表格协作工具按人头付费。
不太适合这些场景:
- 你只是一个人记几条待办,用 Notion 或飞书表格更省事。
- 你完全不想维护服务器、数据库和备份。
- 你需要极复杂的企业权限、审计、审批流。
- 你打算让几百人同时在线编辑,单台小 VPS 可能扛不住。
我的建议是:个人项目、小团队、内部工具、轻量 CRM、库存表、内容排期,这些都很适合。真要做大型业务后台,NocoDB 可以作为辅助界面,但不要把它当成万能 ERP。
NocoDB 官方 quickstart 会跑四个容器:
| 容器 | 作用 |
|---|---|
nocodb | Web 界面和 API |
worker | 导入、导出、自动化等后台任务 |
db | PostgreSQL,保存元数据和业务数据 |
redis | 缓存和任务队列 |
最低可以从 1 核 1GB 起步,但我不建议生产这么抠。
| 用法 | 推荐配置 |
|---|---|
| 个人测试、少量表格 | 1 vCPU / 1GB RAM / 20GB SSD |
| 小团队日常使用 | 2 vCPU / 2GB RAM / 40GB SSD |
| 文件附件较多、多人协作 | 2-4 vCPU / 4GB RAM / 80GB+ SSD |
真正要注意的是磁盘和备份。NocoDB 本身不算重,麻烦的是你一旦把客户资料、订单、附件都放进去,它就变成业务数据中心了。便宜 VPS 可以用,但备份不能省。
本文默认:
- Ubuntu 22.04 / 24.04 或 Debian 12。
- 已安装 Docker 和 Docker Compose V2。
- 有一个域名,例如
nocodb.example.com。 - 域名 A 记录指向 VPS 公网 IP。
- 80、443 端口可开放给 Caddy 自动申请 HTTPS。
确认 Docker:
docker version
docker compose version
如果还没装 Docker,可以先看站内 Docker Compose 生产环境那篇文章。这里不重复讲基础安装。
NocoDB 官方提供 quickstart 和 single-server install 脚本,确实很方便:
curl -fsSL https://install.nocodb.com/noco.sh | bash
它会帮你生成 Compose 栈,甚至 single-server 模式还能处理 Traefik 和 Let's Encrypt。
但我不太建议新手第一次就直接跑脚本。原因很简单:出了问题你不知道它生成了什么,数据在哪,怎么备份,怎么迁移。
这篇文章采用手写 Compose 的方式。配置稍微长一点,但每个容器、每个 volume、每个密码你都看得见。后面维护起来也更踏实。
创建部署目录:
sudo mkdir -p /opt/nocodb
sudo chown -R "$USER":"$USER" /opt/nocodb
cd /opt/nocodb
mkdir -p postgres-data redis-data nocodb-data backups caddy-data caddy-config
生成几个随机密钥:
openssl rand -base64 32
openssl rand -base64 32
openssl rand -hex 32
分别用来做:
- PostgreSQL 密码
- NocoDB JWT Secret
- 数据库连接加密 Key
不要用 password123,也不要照抄教程里的密码。NocoDB 后面可能会保存外部数据库连接信息,密码太弱就是给自己挖坑。
创建 .env:
nano .env
写入下面内容,把域名和密码换成你自己的:
NOCODB_DOMAIN=nocodb.example.com
POSTGRES_PASSWORD=replace_with_a_long_postgres_password
NC_AUTH_JWT_SECRET=replace_with_a_long_random_secret
NC_CONNECTION_ENCRYPT_KEY=replace_with_64_hex_chars
几个变量解释一下:
POSTGRES_PASSWORD:PostgreSQL 数据库密码。NC_AUTH_JWT_SECRET:登录会话相关的密钥,生产环境必须固定。NC_CONNECTION_ENCRYPT_KEY:用于加密外部数据库连接信息,建议一开始就设置。NOCODB_DOMAIN:Caddy 反代使用的域名。
.env 文件不要上传到 Git 仓库。它就是生产密钥。
创建 docker-compose.yml:
nano docker-compose.yml
写入:
services:
nocodb:
image: nocodb/nocodb:latest
container_name: nocodb
restart: unless-stopped
env_file:
- .env
environment:
NC_DB: "pg://db:5432?u=nocodb&p=${POSTGRES_PASSWORD}&d=nocodb"
NC_REDIS_URL: "redis://redis:6379"
NC_SITE_URL: "https://${NOCODB_DOMAIN}"
NC_AUTH_JWT_SECRET: "${NC_AUTH_JWT_SECRET}"
NC_CONNECTION_ENCRYPT_KEY: "${NC_CONNECTION_ENCRYPT_KEY}"
NC_DISABLE_MUX: "true"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
volumes:
- ./nocodb-data:/usr/app/data
ports:
- "127.0.0.1:8080:8080"
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
worker:
image: nocodb/nocodb:latest
container_name: nocodb-worker
restart: unless-stopped
env_file:
- .env
environment:
NC_DB: "pg://db:5432?u=nocodb&p=${POSTGRES_PASSWORD}&d=nocodb"
NC_REDIS_URL: "redis://redis:6379"
NC_SITE_URL: "https://${NOCODB_DOMAIN}"
NC_AUTH_JWT_SECRET: "${NC_AUTH_JWT_SECRET}"
NC_CONNECTION_ENCRYPT_KEY: "${NC_CONNECTION_ENCRYPT_KEY}"
NC_WORKER_CONTAINER: "true"
depends_on:
nocodb:
condition: service_healthy
volumes:
- ./nocodb-data:/usr/app/data
db:
image: postgres:17-alpine
container_name: nocodb-db
restart: unless-stopped
environment:
POSTGRES_USER: nocodb
POSTGRES_PASSWORD: "${POSTGRES_PASSWORD}"
POSTGRES_DB: nocodb
volumes:
- ./postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U nocodb -d nocodb"]
interval: 10s
timeout: 5s
retries: 5
redis:
image: redis:7-alpine
container_name: nocodb-redis
restart: unless-stopped
volumes:
- ./redis-data:/data
command: redis-server --appendonly yes
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
caddy:
image: caddy:2-alpine
container_name: nocodb-caddy
restart: unless-stopped
env_file:
- .env
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy-data:/data
- ./caddy-config:/config
depends_on:
- nocodb
这里有几个故意这么写的地方:
nocodb只绑定127.0.0.1:8080,外网不能直接访问 8080。- 真正对外的是 Caddy 的 80/443。
- PostgreSQL 没有映射 5432,外网扫不到数据库。
- Redis 也没有映射 6379。
nocodb-data、postgres-data、redis-data都是宿主机目录,备份时一眼能找到。
生产环境更稳的做法是把 nocodb/nocodb:latest 固定成当前稳定版本标签。第一次部署可以用 latest 跑通,正式上线前建议去 NocoDB GitHub Release 或 Docker Hub 查当前版本,再手动固定。
创建 Caddyfile:
nano Caddyfile
写入:
{$NOCODB_DOMAIN} {
encode zstd gzip
reverse_proxy 127.0.0.1:8080
}
确保域名已经解析到 VPS,然后开放防火墙:
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
如果你的云厂商还有安全组,也要放行 80 和 443。很多人系统里放行了,云后台没放,最后 Caddy 申请证书失败。
执行:
cd /opt/nocodb
docker compose up -d
看容器状态:
docker compose ps
看日志:
docker compose logs -f --tail=100 nocodb
第一次启动会拉镜像、初始化数据库,等几分钟很正常。等 nocodb 变成 healthy 后,浏览器打开:
https://nocodb.example.com
第一次注册的用户会成为超级管理员。注册完马上去设置里检查邮箱、站点 URL、语言和权限,不要直接把链接丢给全公司用。
NocoDB 有几种用法:
- 新建空项目,然后在界面里建表。
- 导入 CSV / Excel。
- 连接已有 PostgreSQL / MySQL 数据库。
- 给表生成 REST API。
如果你是从 Excel 迁移,我建议先导入一份副本,不要上来就把生产数据接进去。
比如客户表可以先整理成:
| 字段 | 示例 |
|---|---|
| 客户名称 | Acme Inc |
| 联系人 | Zhang San |
| 邮箱 | [email protected] |
| 状态 | 跟进中 / 已成交 / 暂停 |
| 负责人 | Alice |
| 下次跟进 | 2026-07-15 |
先让团队试用 1-2 周,再决定要不要接真实业务数据库。低代码工具最大的坑不是部署,而是大家一开始就把字段设计得乱七八糟。
NocoDB 可以连接外部数据库,这也是它最有价值的地方。但这里要谨慎。
建议:
- 先建只读账号给 NocoDB。
- 不要直接用数据库 root 用户。
- 不要把生产库 5432 / 3306 暴露公网。
- 最好让 NocoDB 和数据库在同一内网或 Docker 网络里通信。
- 先在测试库验证字段、关系、权限。
比如 PostgreSQL 只读用户大概是这样:
CREATE USER nocodb_readonly WITH PASSWORD 'change_this_password';
GRANT CONNECT ON DATABASE appdb TO nocodb_readonly;
GRANT USAGE ON SCHEMA public TO nocodb_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO nocodb_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO nocodb_readonly;
如果你确实要让运营在 NocoDB 里编辑数据,那就单独建一个权限受控的账号,只开放需要编辑的表。不要为了省事把整个数据库权限全给出去。
NocoDB 要备份两类东西:
- PostgreSQL 数据库:项目、表结构、NocoDB 元数据、业务数据。
/usr/app/data:附件、本地文件等数据。
先创建一个备份脚本:
nano backup-nocodb.sh
写入:
#!/usr/bin/env bash
set -euo pipefail
APP_DIR="/opt/nocodb"
BACKUP_DIR="$APP_DIR/backups"
DATE="$(date +%F-%H%M)"
mkdir -p "$BACKUP_DIR/$DATE"
cd "$APP_DIR"
docker exec nocodb-db pg_dump -U nocodb nocodb | gzip > "$BACKUP_DIR/$DATE/nocodb.sql.gz"
tar czf "$BACKUP_DIR/$DATE/nocodb-data.tar.gz" nocodb-data Caddyfile docker-compose.yml .env
find "$BACKUP_DIR" -mindepth 1 -maxdepth 1 -type d -mtime +14 -exec rm -rf {} \;
echo "Backup saved to $BACKUP_DIR/$DATE"
加执行权限并测试:
chmod +x backup-nocodb.sh
./backup-nocodb.sh
ls -lh backups
确认备份文件能生成以后,再加定时任务:
crontab -e
每天凌晨 3 点备份:
0 3 * * * /opt/nocodb/backup-nocodb.sh >> /opt/nocodb/backups/backup.log 2>&1
这只是本地备份。更稳妥的做法是再用 rclone 同步到对象存储、另一台 VPS 或 NAS。只备份在同一台机器上,硬盘坏了还是一起没。
备份没恢复过,就不能算真正可用。
找一台测试 VPS,拷贝备份文件过去:
mkdir -p /opt/nocodb-restore
cd /opt/nocodb-restore
解压配置和附件:
tar xzf nocodb-data.tar.gz
启动数据库后恢复 SQL:
docker compose up -d db redis
sleep 20
gunzip -c nocodb.sql.gz | docker exec -i nocodb-db psql -U nocodb nocodb
docker compose up -d
实际恢复时,域名、证书、.env 里的域名可能要改。别等生产事故当天才第一次练这个流程。
升级前先备份:
cd /opt/nocodb
./backup-nocodb.sh
如果你还在用 latest:
docker compose pull
docker compose up -d
如果你已经固定了版本标签,就先改 docker-compose.yml 里的镜像版本,再执行同样命令。
升级后检查:
docker compose ps
docker compose logs --tail=100 nocodb
然后打开网页,至少检查三件事:
- 能不能登录。
- 表格能不能打开。
- 导入、导出、附件是否正常。
NocoDB 这种工具一旦接入团队工作流,升级不要太随意。建议放在低峰期,升级前通知团队,保留回滚备份。
先看容器:
docker compose ps
再看 Caddy 日志:
docker compose logs --tail=100 caddy
如果 Caddy 证书申请失败,重点查:
- 域名 A 记录是否指向当前 VPS。
- 80/443 是否被 UFW 或安全组拦住。
- Cloudflare 代理模式是否干扰验证。
刚开始建议 Cloudflare DNS 记录先用“仅 DNS”,跑通后再按需要开启代理。
看应用日志:
docker compose logs -f nocodb
常见原因:
- PostgreSQL 密码不一致。
.env改了但没重启容器。- 数据库还没 ready,应用提前启动失败。
NC_DB连接串写错。
可以单独检查数据库:
docker compose logs --tail=100 db
docker exec -it nocodb-db pg_isready -U nocodb -d nocodb
如果只是忘记密码,优先用 NocoDB 的找回功能。如果邮件没配好,处理会麻烦很多。
所以刚上线时就建议:
- 管理员账号用长期可用邮箱。
- 配好 SMTP。
- 至少保留两个管理员账号。
- 把初始部署信息写进内部文档。
不要全公司只有一个管理员,还用个人邮箱注册。
中文 CSV 最常见问题是编码。Excel 导出的文件经常不是 UTF-8。
解决办法:
- 尽量导出为 UTF-8 CSV。
- 如果是 GBK,可以先转换:
iconv -f gbk -t utf-8 old.csv > new.csv
另外,日期字段、手机号、订单号这类数据最好先当文本导入。很多表格工具会自作聪明,把前导 0 吃掉。
重点查:
- 数据库监听地址是否只绑了
127.0.0.1。 - 防火墙是否放行。
- 用户权限是否允许远程连接。
- NocoDB 容器能不能解析数据库主机名。
- 数据库 TLS 要求是否匹配。
但我还是那句话:能走内网就走内网,不要把生产数据库直接暴露公网。
NocoDB 是内部工具,但一旦暴露公网,就要按正式系统对待。
建议至少做到:
- 只开放 80/443,不开放 8080、5432、6379。
- 管理员使用强密码。
- 关闭不需要的公开注册。
- 给不同团队分配最小权限。
- 外部数据库连接使用低权限账号。
.env、备份文件不要放到 Web 可访问目录。- 定期更新镜像和系统安全补丁。
- 备份同步到异地。
- 重要表格开启变更记录或另做审计。
如果你要给客户或外部协作者用,最好再加一层 Cloudflare Access、Tailscale、VPN 或至少 HTTP Basic Auth。不要把内部数据管理界面裸露给全网。
正式使用前检查:
- 域名已经解析到 VPS。
- 80/443 已在系统防火墙和云安全组放行。
- 8080 只绑定
127.0.0.1。 - PostgreSQL 和 Redis 没有暴露公网端口。
-
.env里的密码和密钥都已替换。 -
NC_SITE_URL是真实 HTTPS 域名。 - 第一位管理员账号已创建。
- 本地备份脚本跑通过。
- 至少做过一次测试恢复。
- 团队权限和表格字段已经整理好。
如果你只是一个人记账、写待办,NocoDB 可能有点重。Notion、飞书表格、Google Sheets 都更省事。
但如果你已经有 VPS,又想把 Excel、CSV、数据库变成一个可协作、可权限控制、可生成 API 的内部工具,NocoDB 很值得试。它不是要替代所有后台系统,而是填上“开发太慢、Excel 太乱、Airtable 太贵”中间那块空白。
我建议先用一份非核心数据试跑,比如内容排期、客户线索、库存记录。团队用顺了,再慢慢接入更重要的数据。低代码工具最怕一上来就承载核心业务,先小范围验证,再扩展,出问题也好收场。
