Self-host Twenty
An open-source CRM with a modern, Notion-like interface covering contacts, companies, and fully customizable sales pipelines. Positioned as an open alternative to Salesforce, with a GraphQL API and a Postgres-backed data model you can extend without code.
Key facts
Pick Twenty when you want a modern, AI-native CRM that actually looks and feels like Salesforce or HubSpot rather than a decade-old admin panel — a clean, spreadsheet-style UI with real-time collaboration and an active community driving fast iteration. It's the youngest project in this cluster (give it 4 GB of RAM); weigh that youth against SuiteCRM's or EspoCRM's longer track record if stability matters more than polish.
Follow the Twenty deploy guide →What you need
- Any VPS with at least 2048 MB of RAM
- A domain you control — most self-hosted setups need HTTPS in front of them
- About an afternoon — budget time for troubleshooting
Paid link — we earn a commission if you shop through it.
Install
Run these commands on your server:
# Twenty — official docker-compose.yml + generated secrets
mkdir twenty && cd twenty
wget -O docker-compose.yml https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml
wget -O .env https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/.env.example
# base64 (with +/) breaks Twenty's DB connection string — Twenty's own .env.example
# warns the password must have no special characters, so this uses hex instead
echo "PG_DATABASE_PASSWORD=$(openssl rand -hex 24)" >> .env
echo "ENCRYPTION_KEY=$(openssl rand -base64 32 | tr -d '\n')" >> .env
docker compose up -d || true # first boot reports "unhealthy" — expected, the wait below is the real bring-up
# first boot runs ~180 DB migrations and can take 2-3 min — the server's own
# healthcheck budget is shorter than that, so the first command exits with
# "container twenty-server-1 is unhealthy" without starting the worker
# wait for it, then start the worker once the server reports healthy — use
# --no-recreate so a plain up -d doesn't bounce the healthy server and reset
# its healthcheck clock:
until docker compose ps server | grep -q '(healthy)'; do sleep 5; done && docker compose up -d --no-recreate # → http://SERVER_IP:3000What you take on
Twenty trades maturity for momentum, and that trade is worth knowing before you build a sales team's workflow on it: