How to Self-Host Twenty CRM: Setup, Backups, Updates and Costs
A complete guide to self-hosting Twenty CRM: Docker setup, server sizing, first-boot migrations, backups, updates, and real monthly running costs on a VPS.

Self-hosting Twenty CRM takes about an hour of real work: one small VPS, the official Docker Compose stack, a reverse proxy with HTTPS, and a backup cron job. The running cost is roughly EUR 6 to 10 a month for a small team, against $9 to $19 per user per month on Twenty Cloud and far more on the proprietary CRMs it replaces. The catch is that you own the operations: first-boot database migrations, an encryption key you must never lose, and upgrades that ship every couple of weeks. This guide walks through the whole thing, including the failure modes that most install tutorials skip.
Twenty is the most-starred open-source CRM on GitHub, with over 55,000 stars and a release cadence that has already reached version 2.44 in 2026 (github.com/twentyhq/twenty). It is licensed AGPL-3.0, which means running an unmodified instance for your own team carries no obligations at all. If you are weighing it against HubSpot on price, we broke that down seat by seat in Twenty CRM vs HubSpot: real cost for a 20-person sales team. This post is about the other side of the decision: what it actually takes to run Twenty yourself, properly.
What you are actually installing
Twenty is not one process. The official Docker Compose stack from the twentyhq/twenty repository brings up four services:
- Server. A Node.js application that serves the web UI and the GraphQL and REST APIs on port 3000.
- Worker. A second Node.js process that runs everything asynchronous: email sync, CSV imports, workflow automations, scheduled jobs.
- PostgreSQL. The system of record. Every contact, company, deal, view, and permission lives here.
- Redis. The queue backend the worker reads from, using BullMQ.
Two details in that list matter more than they look. First, Redis must run with maxmemory-policy noeviction. BullMQ jobs are not safe under an eviction policy that silently drops keys, and a CRM that quietly loses import or email-sync jobs is worse than one that crashes loudly. The community compose examples set this explicitly (selfhosting.sh Twenty guide). Second, uploaded files and attachments go to local disk by default (STORAGE_TYPE=local). You can point Twenty at S3-compatible object storage instead, but for a single VPS, local storage in a Docker volume is simpler and fine.
Server sizing: why 4 GB is the real floor
Twenty's published minimum is 2 GB of RAM, and that number is technically true and practically misleading. It describes one container, not the four-service stack you will actually run. Measured idle consumption across the stack is roughly 750 MB: about 400 MB for the server, 200 MB for the worker, 100 MB for PostgreSQL, and 50 MB for Redis. With five or more active users, expect 1 to 2 GB in use, before the operating system and your reverse proxy take their share (selfhosting.sh).
The sizing guidance that holds up in practice, from the SelfHost Atlas deployment guide:
- Small team, up to about 10 users: 2 vCPU and 4 GB RAM. This is the realistic floor, not the published one.
- Larger team, or a big initial import: 4 vCPU and 8 GB RAM. Worker memory spikes during large CSV imports and the first email sync; running out of RAM mid-import means OOM-killed containers and partial data.
- Disk: start with 40 GB. The application takes about 1 GB; the rest is headroom for the database, uploaded files, Docker images, and old image layers after upgrades.
On Hetzner, after the June 2026 price adjustment, a CX23 (2 vCPU, 4 GB RAM, 40 GB disk, 20 TB traffic) costs EUR 5.49 a month and a CX33 (4 vCPU, 8 GB, 80 GB) costs EUR 8.49 a month, plus a small charge for an IPv4 address (Hetzner price adjustment documentation). Any comparable VPS from another provider works; Twenty does not care where it runs.
Step by step: from bare VPS to working CRM
This procedure uses the official compose stack from the Twenty repository, which is the documented and supported install path (Twenty self-hosting docs).
1. Prepare the server
On a fresh Ubuntu 24.04 box:
apt update && apt -y upgrade
apt -y install curl ufw
ufw allow OpenSSH
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
curl -fsSL https://get.docker.com | sh
apt -y install docker-compose-plugin
Point a DNS A record, for example crm.yourcompany.com, at the server. You want this done before install, because HTTPS and the SERVER_URL variable both depend on it.
2. Pull the official compose files
mkdir -p /opt/twenty && cd /opt/twenty
curl -fsSL https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/docker-compose.yml -o docker-compose.yml
curl -fsSL https://raw.githubusercontent.com/twentyhq/twenty/main/packages/twenty-docker/.env.example -o .env
3. Set the environment variables that bite people
Edit .env. Four values cause most failed installs:
- SERVER_URL. Set it to your full public URL,
https://crm.yourcompany.com, and get it exactly right. Twenty uses this for auth callbacks, invite links, and file URLs. A mismatch here produces logins that loop or uploaded files with broken links. - PG_DATABASE_PASSWORD. Generate it with
openssl rand -hex 24, notopenssl rand -base64 32. Twenty's own example file warns that this password must not contain special characters, and base64 output can include+and/, which corrupt the connection string. The failure mode is a server container stuck in a restart loop with a cryptic psql error. Also note this password is baked into the database at first boot; changing it later does not change the running database. - APP_SECRET and ENCRYPTION_KEY. Generate each with
openssl rand -base64 32. The encryption key protects sensitive data at rest and signs sessions. Treat it like a root password, because of what happens at restore time, covered in the backup section below. - Email settings.
EMAIL_FROM_ADDRESS, the email driver, and your SMTP credentials. Without these, invite emails and password resets silently go nowhere.
4. First boot: expect the migration wait
docker compose up -d || true
On first boot, the server runs on the order of 180 database migrations against PostgreSQL, and that routinely takes longer than the healthcheck's retry budget. The compose command may exit complaining that the server is unhealthy and the worker never started. That is normal, not a failed deploy: the server container has restart: always and keeps working through the migrations in the background. Wait for it to report healthy, then start the worker without bouncing the server:
until docker compose ps server | grep -q '(healthy)'; do sleep 5; done
docker compose up -d --no-recreate
You should end with db, redis, server, and worker all up. This first-boot behaviour is documented in the SelfHost Atlas guide and is the single most common false alarm in self-hosted Twenty installs.
5. Put it behind HTTPS
Twenty listens on port 3000. Terminate TLS at a reverse proxy such as nginx or Caddy and proxy through. The nginx config needs two non-obvious settings: client_max_body_size raised to around 50M so attachments upload, and the WebSocket upgrade headers, because Twenty uses GraphQL subscriptions for real-time UI updates. Then issue a certificate with certbot or let Caddy handle it automatically.
6. Claim the workspace, then smoke test
A fresh Twenty install shows an open sign-up screen to anyone who can reach it. Whoever signs up first owns the workspace. Create your admin account the moment the proxy is live, before you announce the URL to anyone, including in a ticket or chat message.
Then verify the whole stack with three actions, each of which exercises a different service:
- Create a company and a person attached to it. This confirms the server and PostgreSQL.
- Drag a deal card between pipeline stages. This confirms the API round trip and real-time updates.
- Upload a file to a record and reload the page. This confirms the worker and file storage, the two pieces a broken install most often hides.
Backups: three things, not one
Most backup guides for self-hosted apps tell you to dump the database. For Twenty that is necessary and not sufficient. There are three things to protect:
- The PostgreSQL database. Every record, pipeline, view, and permission. Dump it nightly:
docker compose exec -T db pg_dump -U postgres default | gzip > twenty-db-$(date +%F).sql.gz
- The storage volume. Uploaded files and attachments, if you use local storage. Tar the volume the same night.
- The
.envfile, especially ENCRYPTION_KEY. This is the one people miss. Twenty encrypts sensitive fields at rest with this key. Restore a database dump onto an instance running a different key and the rows are intact but the encrypted contents are unreadable. Your backup is only a backup if the key travels with it.
Cron the dumps nightly and copy them off the box, to object storage or another machine. A backup on the same disk as the database is a copy, not a backup. And run a real restore drill once, onto a throwaway VPS, before you need it. Restores are where backup strategies go to die.
Updates: pin, pull, verify
Twenty ships fast. The releases page shows multiple version bumps a month, with the project already at v2.44 this year (Twenty releases). That is a strength of the project and an operational decision for you.
The sensible pattern:
- Pin a version tag in your compose file, for example
twentycrm/twenty:v2.44.0, instead of trackinglatest. Upgrades become deliberate events, not surprises. - Upgrade on your schedule, monthly is reasonable for a CRM. Skim the release notes for anything flagged as breaking or requiring manual steps; Twenty maintains an upgrade guide in its self-hosting documentation for exactly this.
- The upgrade itself is three commands:
docker compose pull, thendocker compose down, thendocker compose up -d. Migrations run automatically on server start. - Take a database dump immediately before every upgrade. Migrations are one-way. If an upgrade misbehaves, the dump is your way back.
What it costs to run
A worked example for a 12-person sales and operations team, self-hosting on Hetzner in the EU, at the post-June-2026 prices from Hetzner's documentation:
- VPS: CX33, 4 vCPU and 8 GB RAM for import headroom: EUR 8.49 a month, call it EUR 9.50 with an IPv4 address.
- Transactional email: a low-volume SMTP plan for invites and notifications: EUR 0 to 5 a month.
- Backup storage: a few gigabytes of object storage: under EUR 1 a month.
- Total infrastructure: roughly EUR 10 to 15 a month, or EUR 120 to 180 a year, for unlimited users.
- Your time: budget two to four hours for the initial install, then an hour a month for updates and a backup check. This is the real cost line, and it is honest to put a number on it.
The same team on Twenty Cloud Pro pays $9 per user per month, so $1,296 a year, with zero operations work. On Organization, which adds SSO, audit logs, and row-level permissions, it is $19 per user per month, so $2,736 a year (Twenty pricing). Against a proprietary CRM the gap widens sharply: a 10-person team on Salesforce Professional at $80 per user per month spends $9,600 a year, per the comparison in the selfhosting.sh guide.
Self-hosting wins on cash cost and data control. Cloud wins on time and on features like SSO and audit logs that are gated to paid plans. Pick based on which of those you are actually short on. For a wider view of what a full self-hosted stack costs to run, see our post on self-hosting your business stack: server sizes and real costs and the companion piece on the real cost of replacing SaaS with an open-source stack.
When self-hosting Twenty is the wrong choice
Be honest with yourself on these before you start:
- Nobody owns it. If no named person is responsible for updates, backups, and the 2 a.m. alert, do not self-host your CRM. It is the system your revenue pipeline lives in. Use Twenty Cloud instead.
- You need SSO, audit logs, or row-level permissions now. Those are Organization-tier features. A regulated team that needs them should price the paid plan rather than workaround them.
- Your team lives in marketing automation. Twenty is a sales and relationship CRM. If your centre of gravity is email campaigns and marketing journeys, you will end up bolting on other tools anyway. Pairing Twenty with self-hosted Listmonk covers some of that, but know what you are signing up for.
- You want a finished, static product. Twenty moves fast. The interface and APIs you document internally this quarter will shift this year. For most teams that is a feature; for some it is churn.
Common mistakes
A checklist of the failures that recur across install guides and issue trackers:
- Generating the database password with base64. Special characters break the connection string. Use
openssl rand -hex. - Declaring victory before the migrations finish. The first
up -dfailing its healthcheck is expected. Wait for healthy, then start the worker. - Forgetting SMTP settings. Everything looks fine until the first invite email never arrives.
- Leaving the sign-up screen exposed. Create the admin account immediately after the proxy goes live.
- Backing up the database but not the encryption key. The dump restores cleanly and the encrypted fields are garbage.
- Tracking the
latestimage tag. You will run an unintended upgrade on a random Tuesday. Pin versions. - Skipping the restore drill. An untested backup is a hypothesis.
Frequently asked questions
Is Twenty CRM really free to self-host? Yes. The software is AGPL-3.0 open source, and self-hosting the unmodified code for your own team costs nothing in licence fees. You pay for the server, email delivery, and backup storage, roughly EUR 10 to 15 a month all in for a small team, plus your own maintenance time.
What size server do I need to self-host Twenty? Plan for 2 vCPU and 4 GB RAM as the real floor for a small team, despite the published 2 GB minimum, because the stack is four services, not one container. Go to 4 vCPU and 8 GB if you have more than about 10 users or plan a large initial data import.
Can I migrate from HubSpot or Salesforce to a self-hosted Twenty? Yes. Twenty imports people, companies, and opportunities from CSV, with column mapping in the UI. Export from your current CRM, clean the data, and import in stages, verifying record counts as you go. Custom fields need to be created in Twenty before import so the columns have somewhere to land.
How hard are Twenty upgrades? Mechanically easy: pull the new image, restart the stack, and migrations run automatically. The discipline is in pinning a version tag, reading release notes for breaking changes, and taking a database dump first. Budget an hour a month.
Self-hosted Twenty or Twenty Cloud? Self-host if data residency, customisation, or per-seat cost at scale drives the decision and someone owns the operations. Choose Cloud if you want SSO and audit logs, or if nobody on your team wants to patch a server. The Cloud Pro plan is $9 per user per month, which is cheap operations insurance.
Does self-hosted Twenty include the AI features? Twenty's AI agents and workflows are part of the product, and self-hosted instances can use them, though you may need to configure your own model provider credentials. Check the current documentation for your version, since this area is evolving quickly. For a concrete example of what an AI layer on top of your CRM can do, see our walkthrough on automating CRM data enrichment with an AI agent.
Where to go from here
If you want Twenty running without owning the pager, that is exactly what our managed open-source stack service does: we deploy Twenty, Chatwoot, Cal.com, Plausible, Listmonk, and Formbricks on your infrastructure or ours, handle updates, backups, and monitoring, and can extend the CRM with custom builds when the data model is not enough. If you would rather talk through whether self-hosting makes sense for your team at all, book a call and we will give you a straight answer, including when the answer is to just pay for the cloud version.
Related reading
About AI Agents Plus Editorial
AI automation expert and thought leader in business transformation through artificial intelligence.



