# Twenty

Twenty is an open source CRM and alternative to Salesforce and HubSpot

[Twenty](https://github.com/twentyhq/twenty) is an open source CRM built as an Nx monorepo: a NestJS API server that also serves the React frontend, plus a background worker. It needs PostgreSQL and Redis. Upstream only ships Docker images, so the steps below are derived from `packages/twenty-docker/twenty/Dockerfile` and its `entrypoint.sh`.

## Requirements

Create a PostgreSQL database and a Redis database from the Databases tab. When attaching the PostgreSQL database, name the variable `PG_DATABASE_URL` instead of `DATABASE_URL`. Keep the Redis variable named `REDIS_URL`.

Twenty creates the `uuid-ossp`, `unaccent` and `citext` extensions in its database on first boot, so the PostgreSQL contrib package must be installed on the database server (it is on a standard Hatchbox database server).

Building the frontend is memory hungry: upstream runs it with `NODE_OPTIONS=--max-old-space-size=8192`, and the build peaks above 8 GB, so use a server with 16 GB of RAM, or 8 GB with swap added.

The database migration step uses `psql`, so install the `postgresql-client` package on the cron server if it is missing.

## Repository

Fork the upstream repository, add the scripts below to your fork, and deploy the fork:

```
https://github.com/twentyhq/twenty.git
```

Upstream recommends running a tagged release rather than `main`. Create a branch in your fork from the latest `twenty/vX.Y.Z` tag (for example `twenty/v2.44.0`), commit the scripts there, and set that branch on your Hatchbox app. Node (`.nvmrc`) and Yarn (`packageManager`) versions are picked up automatically.

## Environment Variables

```
NODE_ENV=production
SERVER_URL=https://crm.example.com
ENCRYPTION_KEY=&lt;openssl rand -base64 32&gt;
STORAGE_TYPE=local
```

`SERVER_URL` is the public URL of your app. `ENCRYPTION_KEY` encrypts secrets at rest; losing it means losing every stored OAuth token, so keep a copy. `PG_DATABASE_URL` and `REDIS_URL` come from the attached databases. To store uploads in S3 instead of on disk, set `STORAGE_TYPE=S_3` and the `STORAGE_S3_*` variables from the [self-hosting docs](https://twenty.com/developers/self-host).

## Build Scripts

### .hatchbox/pre-build

Uploads are written to `packages/twenty-server/.local-storage`, so point that at a shared directory:

```
#!/usr/bin/env bash
set -e

mkdir -p $DIR/shared/local-storage
ln -s $DIR/shared/local-storage packages/twenty-server/.local-storage
```

### .hatchbox/post-build

Hatchbox runs `yarn install`. This script builds the server and frontend, copies the frontend into the server&#39;s `dist/front` folder the way the Docker image does, and then runs the same database setup the Docker entrypoint runs, on the cron server only:

```
#!/usr/bin/env bash
set -e

npx nx run twenty-server:lingui:extract
npx nx run twenty-server:lingui:compile
npx nx run twenty-emails:lingui:extract
npx nx run twenty-emails:lingui:compile
npx nx run twenty-server:build

npx nx run twenty-front:lingui:extract
npx nx run twenty-front:lingui:compile
NODE_OPTIONS=&quot;--max-old-space-size=8192&quot; npx nx build twenty-front

rm -rf packages/twenty-server/dist/front
cp -r packages/twenty-front/build packages/twenty-server/dist/front

if [ &quot;$CRON&quot; = &quot;true&quot; ]; then
  cd packages/twenty-server

  has_schema=$(psql -X -tAc &quot;SELECT EXISTS (SELECT 1 FROM information_schema.schemata WHERE schema_name = &#39;core&#39;)&quot; &quot;$PG_DATABASE_URL&quot;)
  if [ &quot;$has_schema&quot; = &quot;f&quot; ]; then
    yarn database:init:prod
  fi

  yarn command:prod cache:flush || true
  yarn command:prod upgrade
  yarn command:prod cache:flush || true
  yarn command:prod cron:register:all
fi
```

## Processes

Twenty reads its port from `NODE_PORT`, so pass Hatchbox&#39;s `PORT` through in the command.

1. `web`, web servers: `cd packages/twenty-server &amp;&amp; NODE_PORT=$PORT node dist/main`
2. `worker`, worker servers: `cd packages/twenty-server &amp;&amp; node dist/queue-worker/queue-worker`

## First Login

Open your domain and sign up. The first user becomes the admin of the workspace with access to the admin panel, and new sign-ups are closed once that first workspace exists.

## Notes

Local uploads live in `shared/local-storage`, so keep the app on a single server or switch to S3 storage before adding more. The worker shares that same directory, so run it on the web server when using local storage.

Each deploy rebuilds both packages, which takes several minutes. To upgrade, merge the next release tag into your branch and deploy; the post-build script runs Twenty&#39;s `upgrade` command for you.
