docker.recipes

Twenty CRM

intermediate

Modern open-source CRM with a beautiful interface, Salesforce alternative.

Overview

Twenty is a modern, open-source Customer Relationship Management (CRM) platform designed as a powerful alternative to Salesforce and other proprietary CRM solutions. Built with a focus on user experience and developer-friendly architecture, Twenty offers a clean, intuitive interface while providing the flexibility to customize objects, fields, and workflows to match specific business needs. The platform emphasizes modern web technologies and GraphQL APIs, making it highly extensible for developers who need to integrate CRM functionality into their existing systems. This Docker deployment creates a complete Twenty CRM environment using four specialized containers: the main Twenty application server, a dedicated worker container for background job processing, a PostgreSQL database for persistent data storage, and Redis for caching and session management. The worker container runs the same Twenty image but executes background tasks like email processing, data synchronization, and scheduled operations, ensuring the main application remains responsive under heavy loads. This configuration is ideal for businesses seeking a self-hosted CRM solution with enterprise-grade features but without the complexity of cloud vendor lock-in. Sales teams, marketing departments, and customer success organizations can benefit from Twenty's modern interface and customization capabilities, while IT teams appreciate the containerized deployment that can be easily scaled, backed up, and integrated into existing infrastructure management workflows.

Key Features

  • Custom object and field creation with flexible data modeling beyond standard CRM entities
  • GraphQL API with full schema introspection for seamless third-party integrations
  • Dedicated background worker processing for email campaigns, data imports, and scheduled tasks
  • Modern React-based interface with real-time updates and collaborative editing features
  • Local file storage system with configurable paths for document and attachment management
  • JWT-based authentication with separate token secrets for access, login, refresh, and file operations
  • PostgreSQL-powered data integrity with support for complex queries and reporting
  • Redis-backed session management and caching for improved application performance

Common Use Cases

  • 1Small to medium businesses replacing Salesforce or HubSpot with a self-hosted alternative
  • 2Sales teams needing custom pipeline stages and deal tracking with specific business logic
  • 3Marketing departments managing lead nurturing campaigns with custom contact properties
  • 4Customer success teams tracking account health and renewal opportunities with tailored dashboards
  • 5Development teams building integrated business applications that need embedded CRM functionality
  • 6Organizations with strict data privacy requirements needing on-premises CRM deployment
  • 7Consultancies and agencies managing multiple client relationships with custom project tracking fields

Prerequisites

  • Docker and Docker Compose installed with at least 2GB RAM available for the complete stack
  • Port 3000 available for the Twenty web interface and API access
  • Environment variables configured for database password and JWT token secrets
  • Basic understanding of CRM concepts like contacts, companies, deals, and sales pipelines
  • Familiarity with PostgreSQL for database maintenance and backup procedures
  • Knowledge of Redis caching concepts for performance optimization and troubleshooting

For development & testing. Review security settings, change default credentials, and test thoroughly before production use. See Terms

docker-compose.yml

docker-compose.yml
1services:
2 twenty:
3 image: twentycrm/twenty:latest
4 container_name: twenty
5 environment:
6 - SERVER_URL=${SERVER_URL}
7 - FRONT_BASE_URL=${FRONT_URL}
8 - PG_DATABASE_URL=postgres://twenty:${DB_PASSWORD}@db:5432/twenty
9 - REDIS_URL=redis://redis:6379
10 - STORAGE_TYPE=local
11 - STORAGE_LOCAL_PATH=/app/docker-data
12 - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
13 - LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET}
14 - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
15 - FILE_TOKEN_SECRET=${FILE_TOKEN_SECRET}
16 volumes:
17 - twenty-data:/app/docker-data
18 ports:
19 - "3000:3000"
20 depends_on:
21 - db
22 - redis
23 networks:
24 - twenty-network
25 restart: unless-stopped
26
27 worker:
28 image: twentycrm/twenty:latest
29 container_name: twenty-worker
30 command: ["yarn", "worker:prod"]
31 environment:
32 - PG_DATABASE_URL=postgres://twenty:${DB_PASSWORD}@db:5432/twenty
33 - REDIS_URL=redis://redis:6379
34 - STORAGE_TYPE=local
35 - STORAGE_LOCAL_PATH=/app/docker-data
36 - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
37 - LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET}
38 - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
39 volumes:
40 - twenty-data:/app/docker-data
41 depends_on:
42 - twenty
43 networks:
44 - twenty-network
45 restart: unless-stopped
46
47 db:
48 image: postgres:15-alpine
49 container_name: twenty-db
50 environment:
51 - POSTGRES_USER=twenty
52 - POSTGRES_PASSWORD=${DB_PASSWORD}
53 - POSTGRES_DB=twenty
54 volumes:
55 - postgres-data:/var/lib/postgresql/data
56 networks:
57 - twenty-network
58 restart: unless-stopped
59
60 redis:
61 image: redis:7-alpine
62 container_name: twenty-redis
63 volumes:
64 - redis-data:/data
65 networks:
66 - twenty-network
67 restart: unless-stopped
68
69volumes:
70 twenty-data:
71 postgres-data:
72 redis-data:
73
74networks:
75 twenty-network:
76 driver: bridge

.env Template

.env
1# Twenty CRM
2SERVER_URL=http://localhost:3000
3FRONT_URL=http://localhost:3000
4DB_PASSWORD=secure_twenty_password
5
6# Generate each with: openssl rand -base64 32
7ACCESS_TOKEN_SECRET=your_access_token_secret
8LOGIN_TOKEN_SECRET=your_login_token_secret
9REFRESH_TOKEN_SECRET=your_refresh_token_secret
10FILE_TOKEN_SECRET=your_file_token_secret

Usage Notes

  1. 1Web UI at http://localhost:3000
  2. 2Create first workspace
  3. 3Modern Salesforce alternative
  4. 4Custom objects and fields
  5. 5GraphQL API available

Individual Services(4 services)

Copy individual services to mix and match with your existing compose files.

twenty
twenty:
  image: twentycrm/twenty:latest
  container_name: twenty
  environment:
    - SERVER_URL=${SERVER_URL}
    - FRONT_BASE_URL=${FRONT_URL}
    - PG_DATABASE_URL=postgres://twenty:${DB_PASSWORD}@db:5432/twenty
    - REDIS_URL=redis://redis:6379
    - STORAGE_TYPE=local
    - STORAGE_LOCAL_PATH=/app/docker-data
    - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
    - LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET}
    - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
    - FILE_TOKEN_SECRET=${FILE_TOKEN_SECRET}
  volumes:
    - twenty-data:/app/docker-data
  ports:
    - "3000:3000"
  depends_on:
    - db
    - redis
  networks:
    - twenty-network
  restart: unless-stopped
worker
worker:
  image: twentycrm/twenty:latest
  container_name: twenty-worker
  command:
    - yarn
    - worker:prod
  environment:
    - PG_DATABASE_URL=postgres://twenty:${DB_PASSWORD}@db:5432/twenty
    - REDIS_URL=redis://redis:6379
    - STORAGE_TYPE=local
    - STORAGE_LOCAL_PATH=/app/docker-data
    - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
    - LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET}
    - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
  volumes:
    - twenty-data:/app/docker-data
  depends_on:
    - twenty
  networks:
    - twenty-network
  restart: unless-stopped
db
db:
  image: postgres:15-alpine
  container_name: twenty-db
  environment:
    - POSTGRES_USER=twenty
    - POSTGRES_PASSWORD=${DB_PASSWORD}
    - POSTGRES_DB=twenty
  volumes:
    - postgres-data:/var/lib/postgresql/data
  networks:
    - twenty-network
  restart: unless-stopped
redis
redis:
  image: redis:7-alpine
  container_name: twenty-redis
  volumes:
    - redis-data:/data
  networks:
    - twenty-network
  restart: unless-stopped

Quick Start

terminal
1# 1. Create the compose file
2cat > docker-compose.yml << 'EOF'
3services:
4 twenty:
5 image: twentycrm/twenty:latest
6 container_name: twenty
7 environment:
8 - SERVER_URL=${SERVER_URL}
9 - FRONT_BASE_URL=${FRONT_URL}
10 - PG_DATABASE_URL=postgres://twenty:${DB_PASSWORD}@db:5432/twenty
11 - REDIS_URL=redis://redis:6379
12 - STORAGE_TYPE=local
13 - STORAGE_LOCAL_PATH=/app/docker-data
14 - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
15 - LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET}
16 - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
17 - FILE_TOKEN_SECRET=${FILE_TOKEN_SECRET}
18 volumes:
19 - twenty-data:/app/docker-data
20 ports:
21 - "3000:3000"
22 depends_on:
23 - db
24 - redis
25 networks:
26 - twenty-network
27 restart: unless-stopped
28
29 worker:
30 image: twentycrm/twenty:latest
31 container_name: twenty-worker
32 command: ["yarn", "worker:prod"]
33 environment:
34 - PG_DATABASE_URL=postgres://twenty:${DB_PASSWORD}@db:5432/twenty
35 - REDIS_URL=redis://redis:6379
36 - STORAGE_TYPE=local
37 - STORAGE_LOCAL_PATH=/app/docker-data
38 - ACCESS_TOKEN_SECRET=${ACCESS_TOKEN_SECRET}
39 - LOGIN_TOKEN_SECRET=${LOGIN_TOKEN_SECRET}
40 - REFRESH_TOKEN_SECRET=${REFRESH_TOKEN_SECRET}
41 volumes:
42 - twenty-data:/app/docker-data
43 depends_on:
44 - twenty
45 networks:
46 - twenty-network
47 restart: unless-stopped
48
49 db:
50 image: postgres:15-alpine
51 container_name: twenty-db
52 environment:
53 - POSTGRES_USER=twenty
54 - POSTGRES_PASSWORD=${DB_PASSWORD}
55 - POSTGRES_DB=twenty
56 volumes:
57 - postgres-data:/var/lib/postgresql/data
58 networks:
59 - twenty-network
60 restart: unless-stopped
61
62 redis:
63 image: redis:7-alpine
64 container_name: twenty-redis
65 volumes:
66 - redis-data:/data
67 networks:
68 - twenty-network
69 restart: unless-stopped
70
71volumes:
72 twenty-data:
73 postgres-data:
74 redis-data:
75
76networks:
77 twenty-network:
78 driver: bridge
79EOF
80
81# 2. Create the .env file
82cat > .env << 'EOF'
83# Twenty CRM
84SERVER_URL=http://localhost:3000
85FRONT_URL=http://localhost:3000
86DB_PASSWORD=secure_twenty_password
87
88# Generate each with: openssl rand -base64 32
89ACCESS_TOKEN_SECRET=your_access_token_secret
90LOGIN_TOKEN_SECRET=your_login_token_secret
91REFRESH_TOKEN_SECRET=your_refresh_token_secret
92FILE_TOKEN_SECRET=your_file_token_secret
93EOF
94
95# 3. Start the services
96docker compose up -d
97
98# 4. View logs
99docker compose logs -f

One-Liner

Run this command to download and set up the recipe in one step:

terminal
1curl -fsSL https://docker.recipes/api/recipes/twenty-crm/run | bash

Troubleshooting

  • Twenty container failing to start with database connection errors: Verify DB_PASSWORD environment variable matches between twenty and db services, and ensure PostgreSQL container is fully initialized before Twenty starts
  • GraphQL API returning 500 errors on queries: Check that all four JWT token secrets (ACCESS_TOKEN_SECRET, LOGIN_TOKEN_SECRET, REFRESH_TOKEN_SECRET, FILE_TOKEN_SECRET) are set and consistent between twenty and worker containers
  • Background jobs not processing in worker container: Ensure Redis container is running and accessible, verify REDIS_URL environment variable format is redis://redis:6379 in worker service
  • File uploads failing with storage errors: Check that twenty-data volume is properly mounted and STORAGE_LOCAL_PATH=/app/docker-data is correctly configured in both twenty and worker services
  • Performance issues with large datasets: Monitor PostgreSQL memory usage and consider increasing shared_buffers, verify Redis memory limits are appropriate for your session and cache requirements
  • Twenty web interface not loading after container restart: Ensure SERVER_URL and FRONT_BASE_URL environment variables match your actual domain/IP configuration and are accessible from client browsers

Community Notes

Loading...
Loading notes...

Download Recipe Kit

Get all files in a ready-to-deploy package

Includes docker-compose.yml, .env template, README, and license

Ad Space