Docker一键部署Twenty CRM
一、项目简介
项目简介
Twenty CRM 是一款面向现代化团队的开源客户关系管理(CRM)系统。它采用主流的 TypeScript/React技术栈,拥有类似Notion的高颜值交互界面。作为Salesforce的现代化开源替代方案,Twenty完美融合了AI时代的自动化需求(原生支持MCP协议与AI Agents集成)。项目支持完全的自托管(Self-Hosted)部署,旨在帮助企业彻底摆脱云端SaaS的厂商锁死,实现100% 的客户数据自主权。
技术生态优势
现代化全栈架构:前端基于React + Tailwind CSS确保极致的响应体验,后端采用NestJS + Node.js提供稳健的API服务,底层数据由PostgreSQL与Redis驱动,技术栈成熟且对开发者极其友好。
AI-Ready(AI 原生就绪):区别于传统CRM,Twenty从底层即为AI智能体(Agents)和现代大模型编码工具留出了标准接口,支持通过自然语言直接进行数据检索与流转自动化。
代码即配置(Code-as-Config):系统的对象、字段和业务逻辑允许直接通过TypeScript 代码或SDK进行定义,这意味着可以将CRM的配置纳入Git版本管理,无缝对接企业现有的CI/CD 自动化运维流水线。
原生容器化支持:官方提供完善的Docker部署生态,支持一键实现微服务架构的容器化编排,极大地降低了私有化部署、数据备份与平滑升级的运维门槛。
二、环境准备
- VPS一台 最低配置2核4G 带宽1M即可
- 服务器系统:本文演示的系统是Ubuntu-24.04.1(LTS)
- 是否备案:如果服务器在国外,站点无须备案;如果服务器在国内,网站必须备案。
- 域名一个
- 图床一个(又拍云、阿里云oss、腾讯云cos、七牛云等)
- 为了便于后期的升级维护以及卸载,利用docker+Nginx Proxy Manager部署
- 安装好Docker、Docker-compose、Nginx Proxy Manager(安装教程)
- SSH工具:FinalShell官网
- 项目地址:Github官网
- 官方软硬件配置要求请参考以下链接:Twenty官网
三、部署过程
系统初始化,更新索引
#更新索引库
sudo apt-get update
创建部署目录
# 创建Twenty部署目录
mkdir -p /root/data/docker_workspace/twenty
# 进入目录
cd /root/data/docker_workspace/twenty
获取 docker-compose.yml和env环境配置文件
# 获取compose文件
curl -o docker-compose.yml \
https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml
# 获取.env环境配置文件
curl -o .env \
https://raw.githubusercontent.com/twentyhq/twenty/refs/heads/main/packages/twenty-docker/.env.example
编辑环境变量文件
# 编辑环境变量文件
nano .env
修改成以下内容
TAG=latest
PG_DATABASE_USER=postgres
PG_DATABASE_PASSWORD=INSERT_GENERATED_SECRET_HERE
PG_DATABASE_HOST=db
PG_DATABASE_PORT=5432
REDIS_URL=redis://redis:6379
SERVER_URL=http://localhost:3000
# Use openssl rand -base64 32 for each secret
ENCRYPTION_KEY=INSERT_GENERATED_SECRET_HERE
FALLBACK_ENCRYPTION_KEY=INSERT_GENERATED_SECRET_HERE
APP_SECRET=INSERT_GENERATED_SECRET_HERE
STORAGE_TYPE=local
# STORAGE_S3_REGION=eu-west3
# STORAGE_S3_NAME=my-bucket
# STORAGE_S3_ENDPOINT=
# STORAGE_S3_ACCESS_KEY_ID=
# STORAGE_S3_SECRET_ACCESS_KEY=
其中四个密码:PG_DATABASE_PASSWORD、ENCRYPTION_KEY、FALLBACK_ENCRYPTION_KEY、APP_SECRET均使用openssl rand -base64 32命令随机生成,首次部署的时候,FALLBACK_ENCRYPTION_KEY密码为空。其他三个密码必须是独立的。
效果如下
TAG=latest
PG_DATABASE_USER=postgres
PG_DATABASE_PASSWORD=hLR5qvru8VgX2m+5rw2mpEcb2y1iB09lXqOfSdO0jAI=
PG_DATABASE_HOST=db
PG_DATABASE_PORT=5432
REDIS_URL=redis://redis:6379
SERVER_URL=http://47.168.92.48:3000 # 改为你的服务器IP
DISABLE_DB_MIGRATIONS=false
DISABLE_CRON_JOBS_REGISTRATION=false
# Use openssl rand -base64 32 for each secret
ENCRYPTION_KEY=r8SOZy9wu2P1k6jwkOhlNDXf11/dW7cHvLTmTGQgBH0=
FALLBACK_ENCRYPTION_KEY=
APP_SECRET=+JWPNq8lmvxo45hMsHbtZ9GJBQltOmp6qSHr1DaeYbE=
STORAGE_TYPE=local
# STORAGE_S3_REGION=eu-west3
# STORAGE_S3_NAME=my-bucket
# STORAGE_S3_ENDPOINT=
# STORAGE_S3_ACCESS_KEY_ID=
# STORAGE_S3_SECRET_ACCESS_KEY=
请在服务器内部和云平台的安全组中开启3000端口。如果是生产环境中,.env中的SERVER_URL值请改为自己已经解析好的域名
官网的docker-compose.yml文件里面关于healthy的检测太过严格,启动容器的时候经常会导致失败

放宽 healthcheck,修改 docker-compose.yml如下,可复制直接替换
name: twenty
services:
server:
image: twentycrm/twenty:${TAG:-latest}
volumes:
- server-local-data:/app/packages/twenty-server/.local-storage
ports:
- "3000:3000"
environment:
NODE_PORT: 3000
PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-postgres}:${PG_DATABASE_PASSWORD:-postgres}@${PG_DATABASE_HOST:-db}:${PG_DATABASE_PORT:-5432}/default
SERVER_URL: ${SERVER_URL}
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
DISABLE_DB_MIGRATIONS: ${DISABLE_DB_MIGRATIONS}
DISABLE_CRON_JOBS_REGISTRATION: ${DISABLE_CRON_JOBS_REGISTRATION}
STORAGE_TYPE: ${STORAGE_TYPE}
STORAGE_S3_REGION: ${STORAGE_S3_REGION}
STORAGE_S3_NAME: ${STORAGE_S3_NAME}
STORAGE_S3_ENDPOINT: ${STORAGE_S3_ENDPOINT}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
FALLBACK_ENCRYPTION_KEY: ${FALLBACK_ENCRYPTION_KEY}
APP_SECRET: ${APP_SECRET:-}
# MESSAGING_PROVIDER_GMAIL_ENABLED: ${MESSAGING_PROVIDER_GMAIL_ENABLED}
# CALENDAR_PROVIDER_GOOGLE_ENABLED: ${CALENDAR_PROVIDER_GOOGLE_ENABLED}
# AUTH_GOOGLE_CLIENT_ID: ${AUTH_GOOGLE_CLIENT_ID}
# AUTH_GOOGLE_CLIENT_SECRET: ${AUTH_GOOGLE_CLIENT_SECRET}
# AUTH_GOOGLE_CALLBACK_URL: ${AUTH_GOOGLE_CALLBACK_URL}
# AUTH_GOOGLE_APIS_CALLBACK_URL: ${AUTH_GOOGLE_APIS_CALLBACK_URL}
# CALENDAR_PROVIDER_MICROSOFT_ENABLED: ${CALENDAR_PROVIDER_MICROSOFT_ENABLED}
# MESSAGING_PROVIDER_MICROSOFT_ENABLED: ${MESSAGING_PROVIDER_MICROSOFT_ENABLED}
# AUTH_MICROSOFT_ENABLED: ${AUTH_MICROSOFT_ENABLED}
# AUTH_MICROSOFT_CLIENT_ID: ${AUTH_MICROSOFT_CLIENT_ID}
# AUTH_MICROSOFT_CLIENT_SECRET: ${AUTH_MICROSOFT_CLIENT_SECRET}
# AUTH_MICROSOFT_CALLBACK_URL: ${AUTH_MICROSOFT_CALLBACK_URL}
# AUTH_MICROSOFT_APIS_CALLBACK_URL: ${AUTH_MICROSOFT_APIS_CALLBACK_URL}
# EMAIL_FROM_ADDRESS: ${EMAIL_FROM_ADDRESS:-contact@yourdomain.com}
# EMAIL_FROM_NAME: ${EMAIL_FROM_NAME:-"John from YourDomain"}
# EMAIL_DRIVER: ${EMAIL_DRIVER:-smtp}
# EMAIL_SMTP_HOST: ${EMAIL_SMTP_HOST:-smtp.gmail.com}
# EMAIL_SMTP_PORT: ${EMAIL_SMTP_PORT:-465}
# EMAIL_SMTP_USER: ${EMAIL_SMTP_USER:-}
# EMAIL_SMTP_PASSWORD: ${EMAIL_SMTP_PASSWORD:-}
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: curl -f http://localhost:3000/healthz || exit 1
interval: 20s
timeout: 10s
retries: 10
start_period: 120s
restart: always
worker:
image: twentycrm/twenty:${TAG:-latest}
volumes:
- server-local-data:/app/packages/twenty-server/.local-storage
command: ["yarn", "worker:prod"]
environment:
PG_DATABASE_URL: postgres://${PG_DATABASE_USER:-postgres}:${PG_DATABASE_PASSWORD:-postgres}@${PG_DATABASE_HOST:-db}:${PG_DATABASE_PORT:-5432}/default
SERVER_URL: ${SERVER_URL}
REDIS_URL: ${REDIS_URL:-redis://redis:6379}
DISABLE_DB_MIGRATIONS: "true" # it already runs on the server
DISABLE_CRON_JOBS_REGISTRATION: "true" # it already runs on the server
STORAGE_TYPE: ${STORAGE_TYPE}
STORAGE_S3_REGION: ${STORAGE_S3_REGION}
STORAGE_S3_NAME: ${STORAGE_S3_NAME}
STORAGE_S3_ENDPOINT: ${STORAGE_S3_ENDPOINT}
ENCRYPTION_KEY: ${ENCRYPTION_KEY}
FALLBACK_ENCRYPTION_KEY: ${FALLBACK_ENCRYPTION_KEY}
APP_SECRET: ${APP_SECRET:-}
# MESSAGING_PROVIDER_GMAIL_ENABLED: ${MESSAGING_PROVIDER_GMAIL_ENABLED}
# CALENDAR_PROVIDER_GOOGLE_ENABLED: ${CALENDAR_PROVIDER_GOOGLE_ENABLED}
# AUTH_GOOGLE_CLIENT_ID: ${AUTH_GOOGLE_CLIENT_ID}
# AUTH_GOOGLE_CLIENT_SECRET: ${AUTH_GOOGLE_CLIENT_SECRET}
# AUTH_GOOGLE_CALLBACK_URL: ${AUTH_GOOGLE_CALLBACK_URL}
# AUTH_GOOGLE_APIS_CALLBACK_URL: ${AUTH_GOOGLE_APIS_CALLBACK_URL}
# CALENDAR_PROVIDER_MICROSOFT_ENABLED: ${CALENDAR_PROVIDER_MICROSOFT_ENABLED}
# MESSAGING_PROVIDER_MICROSOFT_ENABLED: ${MESSAGING_PROVIDER_MICROSOFT_ENABLED}
# AUTH_MICROSOFT_ENABLED: ${AUTH_MICROSOFT_ENABLED}
# AUTH_MICROSOFT_CLIENT_ID: ${AUTH_MICROSOFT_CLIENT_ID}
# AUTH_MICROSOFT_CLIENT_SECRET: ${AUTH_MICROSOFT_CLIENT_SECRET}
# AUTH_MICROSOFT_CALLBACK_URL: ${AUTH_MICROSOFT_CALLBACK_URL}
# AUTH_MICROSOFT_APIS_CALLBACK_URL: ${AUTH_MICROSOFT_APIS_CALLBACK_URL}
# EMAIL_FROM_ADDRESS: ${EMAIL_FROM_ADDRESS:-contact@yourdomain.com}
# EMAIL_FROM_NAME: ${EMAIL_FROM_NAME:-"John from YourDomain"}
# EMAIL_DRIVER: ${EMAIL_DRIVER:-smtp}
# EMAIL_SMTP_HOST: ${EMAIL_SMTP_HOST:-smtp.gmail.com}
# EMAIL_SMTP_PORT: ${EMAIL_SMTP_PORT:-465}
# EMAIL_SMTP_USER: ${EMAIL_SMTP_USER:-}
# EMAIL_SMTP_PASSWORD: ${EMAIL_SMTP_PASSWORD:-}
depends_on:
db:
condition: service_healthy
server:
condition: service_healthy
restart: always
db:
image: postgres:16
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_DB: ${PG_DATABASE_NAME:-default}
POSTGRES_PASSWORD: ${PG_DATABASE_PASSWORD:-postgres}
POSTGRES_USER: ${PG_DATABASE_USER:-postgres}
healthcheck:
test: pg_isready -U ${PG_DATABASE_USER:-postgres} -h localhost -d postgres
interval: 5s
timeout: 5s
retries: 10
restart: always
redis:
image: redis
restart: always
command: ["--maxmemory-policy", "noeviction"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 10
volumes:
db-data:
server-local-data:
启动 Docker 容器
docker compose up -d

使用你的服务器地址:3000端口访问

使用电子邮件注册登录后初始化数据库




域名访问
可参考下面的文章去Nginx Proxy Manager里设置域名访问

卸载
进入目录
# 进入自己的项目路径
cd /root/data/docker_workspace/twenty
# 停止并删除所有容器
docker compose down
# Twenty的数据都在下面两个volume里,分别删除
docker volume rm twenty_db-data
docker volume rm twenty_server-local-data
# 删除镜像
docker rmi twentycrm/twenty:latest
# 回到目录上层
cd /root/data/docker_workspace
# 删除项目文件
rm -rf twenty