By LogicSystemIQ | May 25, 2026 | 9 min read
We cancelled Pipedrive last week. The replacement is self-hosted, free, and took thirty minutes to stand up. Here is the exact playbook.
Quick answer: To replace Pipedrive with Twenty CRM in roughly thirty minutes, run the official Docker Compose with twenty_server plus twenty_worker, point it at a Postgres database and a Redis instance, configure S3-compatible storage like MinIO for file uploads, generate the three required encryption keys, bring it up behind a reverse proxy with HTTPS, then export your Pipedrive contacts and deals as CSV and import them through the Twenty admin UI. No per-seat licensing, no monthly bill, and the full schema is yours to customize.
We were on Pipedrive Starter at fourteen dollars per user per month. Three seats. That is five hundred and four dollars per year for a tool we barely customized. The product is fine. The problem is the per-seat pricing scales with team size, not with value delivered. As soon as you have five sales reps, you are looking at over eight hundred dollars annually for software that, at its core, is a Postgres database with a UI layer on top.
The other thing that pushed us out was data ownership. Pipedrive holds your contact list, deal history, email logs, and call notes in their database. If they ever raise prices, sunset features, or get acquired, you have a migration project on your hands.
Self-hosted CRM solves both problems. We picked Twenty because it is the closest thing to Pipedrive's user experience in the open-source world.
We looked at three contenders before committing.
| CRM | Stack | UX feel | Why we passed |
|---|---|---|---|
| EspoCRM | PHP, MariaDB | Older Salesforce style | Heavy admin panel, dated UI |
| SuiteCRM | PHP, MySQL | Fork of SugarCRM | Same complaint as EspoCRM, more bloat |
| Twenty CRM | NestJS + React, Postgres | Modern, Notion-adjacent | Picked this one |
Twenty's UI looks like 2026 software, not 2010. Pipeline view, Kanban boards, inline editing on rows, custom fields without a database migration, BullMQ background workers for the heavy stuff. The codebase is active on GitHub, AGPL-3.0 licensed, and the maintainers ship weekly.
The trade-off versus the older PHP CRMs: Twenty needs more infrastructure (Postgres 14+, Redis, S3-compatible storage, Node.js runtime via Docker). For a homelab or a small VPS that is fine. For a shared Hostinger plan with PHP and MySQL only, it will not run.
Hardware and software prerequisites for the 30-minute install:
maxmemory-policy noeviction. This is non-negotiable. Twenty uses BullMQ for background jobs and BullMQ refuses to start with any other eviction policy.crm.yourcompany.com.If you do not have a homelab, a five-dollar Hostinger or DigitalOcean VPS with 2 GB RAM is enough to get started.
Here is the compressed playbook. Adjust paths and hostnames to match your setup.
Create a dedicated Postgres database and role:
CREATE ROLE twenty_user WITH LOGIN PASSWORD '<random_32_hex>';
CREATE DATABASE twenty OWNER twenty_user;
GRANT ALL PRIVILEGES ON DATABASE twenty TO twenty_user;
Create a MinIO bucket and a scoped service user:
mc alias set local http://minio:9000 admin <root_password>
mc mb local/twenty
mc admin user add local twenty_storage_key <random_40_chars>
mc admin policy attach local readwrite --user twenty_storage_key
mc admin policy attach local readwrite --user twenty_storage_key --bucket twenty
Twenty needs three random secrets in its environment:
openssl rand -hex 32 # APP_SECRET (used for session signing)
openssl rand -base64 32 # ENCRYPTION_KEY (used for at-rest credential encryption in DB)
openssl rand -base64 32 # FALLBACK_ENCRYPTION_KEY (for key rotation, can mirror above initially)
Save these in your secrets manager. Lose ENCRYPTION_KEY and you cannot decrypt stored OAuth tokens, IMAP credentials, or anything sensitive Twenty has stored in the database.
Two services, both pulling the same image. The worker just runs a different command.
services:
twenty_server:
image: twentycrm/twenty:latest
container_name: twenty_server
restart: unless-stopped
env_file: [.env]
healthcheck:
test: ["CMD-SHELL", "curl -fsS http://localhost:3000/healthz || wget --spider -q http://localhost:3000/healthz || exit 1"]
interval: 30s
timeout: 10s
start_period: 60s
retries: 3
networks: [stack_web, stack_db]
twenty_worker:
image: twentycrm/twenty:latest
container_name: twenty_worker
restart: unless-stopped
command: ["yarn", "worker:prod"]
env_file: [.env]
environment:
DISABLE_DB_MIGRATIONS: "true"
DISABLE_CRON_JOBS_REGISTRATION: "true"
networks: [stack_web, stack_db]
networks:
stack_web: { external: true }
stack_db: { external: true }
The .env file holds the database connection, Redis URL, S3 credentials, the three secrets, and one config flag: IS_SIGN_UP_DISABLED=true (flip to false during initial admin signup, then back to true).
Bring it up:
docker compose up -d twenty_server
# Wait for migrations to run (60 to 90 seconds)
docker compose up -d twenty_worker
The server container runs schema migrations on first boot. You will see it create around forty tables in the twenty database.
Add a Caddy block:
crm.yourcompany.com {
reverse_proxy twenty_server:3000
}
Reload Caddy. Point DNS at your host (or your Cloudflare Tunnel). Visit https://crm.yourcompany.com in a browser.
Twenty shows the signup screen because IS_SIGN_UP_DISABLED=true has not taken effect for the first user. Create the admin workspace. Set your real email and a strong password.
Once you can log in, flip the env var:
IS_SIGN_UP_DISABLED=true
And recreate the container:
docker compose up -d --force-recreate twenty_server
No one else can sign up now. Only invited users.
In Pipedrive: Tools and apps menu, then Export data, then choose Persons and Deals. You get two CSV files.
In Twenty: Settings, then Workspace, then Data Import. Map the CSV columns to Twenty fields. The Pipedrive fields that map cleanly are:
| Pipedrive field | Twenty field |
|---|---|
| Name | Person Name (first + last) |
| Person Email | |
| Phone | Person Phone |
| Organization | Company Name |
| Title | Job Title |
| Deal Title | Opportunity Name |
| Deal Value | Opportunity Amount |
| Deal Stage | Opportunity Stage |
| Owner | Workspace User (you have to invite that user first if they were a Pipedrive teammate) |
Custom fields require a manual setup pass on the Twenty side first. Create them in Twenty Settings, then redo the import. Twenty handles new custom fields without a database migration.
Click Import. A few seconds per hundred rows.
We host Twenty's database on the same Postgres 16 container that runs Vaultwarden, n8n, and GoTrue. Each app gets its own database and role, scoped with grants. The savings: about 200 MB of RAM versus running a dedicated Postgres for Twenty alone. The trade-off: one Postgres upgrade hits all apps simultaneously. Acceptable for a homelab.
Twenty uses BullMQ for email parsing, search indexing, calendar sync, and webhook processing. BullMQ requires maxmemory-policy noeviction because eviction breaks job state guarantees. If your shared Redis was configured for allkeys-lru (common for cache-only workloads), you need to flip it before Twenty will start cleanly. Add this to your redis.conf:
maxmemory-policy noeviction
Then restart Redis. AppFlowy and Plausible (if you also use them) are unaffected because they treat Redis as a cache, not a queue.
Twenty is younger than Pipedrive. A few things are missing or rougher:
None of these are dealbreakers for a small team running a focused sales motion. They might matter at fifty seats with a dedicated RevOps team.
Three seats, twelve months, real numbers:
| Cost line | Pipedrive Starter | Twenty CRM self-hosted |
|---|---|---|
| Per-seat license | $14/mo x 3 x 12 = $504/year | $0 |
| VPS or homelab cost | $0 (Pipedrive runs in their cloud) | $0 if you already run a homelab, $60/year for a $5 VPS |
| Storage | included | $0 (MinIO local) or $1/year for Backblaze B2 |
| Migration time | $0 (you keep the data) | 30 minutes of your time once |
| Annual | $504 | $0 to $60 |
Across three years, that is $1,512 versus $0 to $180. Enough to fund the hardware upgrade we have been planning, or just to stay in the bank.
Yes. Twenty has a Gmail integration that syncs sent emails into the contact timeline. For open and click tracking, you set up a tracking pixel via n8n or use a third-party tool like Mailtrack and pipe the results into Twenty via webhook.
The Docker image tag twentycrm/twenty:latest floats. We pin to a specific minor in production, take a Postgres dump and a MinIO snapshot before each upgrade, and revert via container rollback if anything breaks. Same playbook as any other Postgres-backed app.
Yes. Settings, then Data Model, then add field. Twenty stores custom fields in a metadata table and resolves them at query time. No schema migration required. Tested with five custom fields on the Opportunity object, no perceptible performance impact.
We use Kopia for nightly snapshots of the Postgres dumps and the MinIO bucket. Two destinations: local USB drive and Backblaze B2. Daily snapshots with 7 daily, 4 weekly, 6 monthly retention. We test the restore monthly into a throwaway directory to confirm the backup is actually usable.
Yes. Twenty has real-time collaboration via WebSocket and supports concurrent edits without conflict.
If you are paying for Pipedrive and you can stand up a Postgres database, a Redis instance, and a Docker host, you can replace it with Twenty CRM in roughly thirty minutes. You will save the per-seat license, you will own your data, and you will get a modern UI that does not look like enterprise software from 2010.
The honest constraint: this requires comfort with Docker Compose and basic ops. If your team has none of that, Pipedrive is worth the per-seat fee. If you have one developer or sysadmin who already runs other self-hosted apps, this migration is two coffee breaks away.
We did it last week. Three seats off Pipedrive. Same workflow, same data, zero per-seat cost going forward.
LogicSystemIQ is an IT managed services and SaaS studio based in Peabody, Massachusetts. We build DaycarePro (daycarepro.cloud), a trilingual SaaS for licensed home daycare providers. Reach us at (978) 815-1047 or Support@LogicSystemiq.com.