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.

Why we left Pipedrive

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.

Why Twenty CRM specifically

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.

What you need before starting

Hardware and software prerequisites for the 30-minute install:

  • A Linux host with Docker and Docker Compose installed. Ubuntu 24.04 LTS works. So does Debian 12 or any modern distro.
  • 2 GB of free RAM dedicated to Twenty (server + worker combined idle around 1 GB, peak 1.5 GB).
  • A Postgres 14 or later instance reachable from the Docker network. A shared Postgres on the same host with a dedicated database is fine.
  • A Redis 7 instance with maxmemory-policy noeviction. This is non-negotiable. Twenty uses BullMQ for background jobs and BullMQ refuses to start with any other eviction policy.
  • An S3-compatible storage backend for file uploads. MinIO self-hosted works perfectly. AWS S3 or Backblaze B2 also work.
  • A reverse proxy with TLS in front. Caddy with Cloudflare Tunnel is what we use.
  • A domain name you control for the CRM, like 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.

The actual 30-minute install

Here is the compressed playbook. Adjust paths and hostnames to match your setup.

Minute 0 to 5: Provision the database and the bucket

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

Minute 5 to 10: Generate the three required secrets

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.

Minute 10 to 20: Write the docker-compose file

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.

Minute 20 to 25: Reverse proxy + DNS

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.

Minute 25 to 30: Import your Pipedrive data

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)
Email 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.

Two architectural choices that made this work

One: shared Postgres instead of dedicated

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.

Two: Redis maxmemory-policy noeviction

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.

What does not work, and why we do not care

Twenty is younger than Pipedrive. A few things are missing or rougher:

  • Email sequencing: Pipedrive has a built-in cadence tool. Twenty does not. We use n8n for outbound sequences instead, hitting the Twenty REST API to log activity.
  • Mobile app: Twenty is web-only as of mid-2026. The web UI is responsive and works fine on iOS Safari. A native app is on the roadmap.
  • Marketplace integrations: Pipedrive has hundreds. Twenty has API-first integrations (REST and GraphQL), and we built the few we needed via n8n in under an hour each.

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.

Cost comparison

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.

Frequently asked questions

Can Twenty CRM handle email tracking and open rates?

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.

What happens if Twenty introduces breaking changes in a future release?

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.

Does Twenty support custom fields without writing code?

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.

How is data backed up?

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.

Can two people work on Twenty at the same time?

Yes. Twenty has real-time collaboration via WebSocket and supports concurrent edits without conflict.

Bottom line

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.

Scroll to Top