Skip to main content

Deploy Aventora Twenty CRM to a VPS with Docker

This guide covers deploying the Aventora-branded Twenty CRM (this repo) to a VPS using Docker. We use our own image aventora-twenty:latest, not the public twentycrm/twenty image.

Alternative: build once, push to Docker Hub, pull on VPS — see docker-hub-deploy.md.
Doc index: README.md


Step-by-step: clone and build on the VPS

Follow these steps on a fresh Linux VPS (e.g. Ubuntu 22.04, 4GB RAM recommended for the build). Replace YOUR_DOMAIN (or use your server IP and port) and secrets as needed.

Step 1 — Install Docker and Git

sudo apt update && sudo apt install -y ca-certificates curl gnupg git
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo usermod -aG docker "$USER"

Log out and back in (or run newgrp docker) so docker works without sudo.

Step 2 — Clone the repo

Use a shallow clone. Standard VPS paths: repo in /opt/aventora/aventora-crm, deploy (compose + .env) in /opt/aventora/crm:

sudo mkdir -p /opt/aventora
cd /opt/aventora
git clone --depth 1 https://github.com/aventora-ai/aventora-crm.git
# Repo is at /opt/aventora/aventora-crm

For a private repo, use a personal access token or SSH in the clone URL.

Step 3 — Create deploy directory and env file

Deploy directory holds docker-compose.yml and .env (e.g. /opt/aventora/crm):

mkdir -p /opt/aventora/crm
cd /opt/aventora/crm
cp /opt/aventora/aventora-crm/packages/twenty-docker/.env.aventora.example .env

If using the Postgres container (default):

cp /opt/aventora/aventora-crm/packages/twenty-docker/docker-compose.aventora.yml docker-compose.yml

If using an existing Postgres on the host (no db container):

cp /opt/aventora/aventora-crm/packages/twenty-docker/docker-compose.aventora.external-db.yml docker-compose.yml

Then edit .env (see below for required vars; for external DB set PG_DATABASE_HOST, etc.).

Edit .env (use your real URL and secrets):

nano .env

Set at least:

  • SERVER_URL=https://YOUR_DOMAIN (e.g. https://crm.aventora.ai or http://YOUR_VPS_IP:3000)
  • APP_SECRET= — run openssl rand -base64 32 and paste the result
  • PG_DATABASE_PASSWORD= — strong password, no special characters

Optional: AVENTORA_BASE_URL, AVENTORA_WEBHOOK_SECRET. Save and exit (Ctrl+O, Enter, Ctrl+X).

Step 4 — Build the Docker image

Build from the repo root (/opt/aventora/aventora-crm). Use the same URL as SERVER_URL for REACT_APP_SERVER_BASE_URL:

cd /opt/aventora/aventora-crm
export CRM_URL="https://YOUR_DOMAIN" # or http://YOUR_VPS_IP:3000
docker build -f packages/twenty-docker/twenty/Dockerfile \
--build-arg REACT_APP_SERVER_BASE_URL="$CRM_URL" \
-t aventora-twenty:latest \
.

Build can take 10–20 minutes. If it fails with "The service was stopped" or exit 130, the VPS likely ran out of memory; use a machine with at least 4GB RAM.

Step 5 — Start the stack

cd /opt/aventora/crm
docker compose up -d

Check that containers are running:

docker compose ps
docker compose logs -f server

Wait until the server is healthy (first run runs DB migrations). Then open http://YOUR_VPS_IP:3000 or your domain in a browser.

Step 6 — (Optional) HTTPS with a reverse proxy

Put Caddy or Nginx in front and proxy to http://localhost:3000. Set SERVER_URL=https://YOUR_DOMAIN in .env, then:

cd /opt/aventora/crm
docker compose down && docker compose up -d

Using an existing Postgres on the VPS (external DB)

If you have a Postgres instance already running on the host (used by other apps), use the external-db compose file so no db container is started.

  1. Create a dedicated database and user on that Postgres (do not share a database with other apps). Twenty expects a database; the default name in the app is default. Example:

    CREATE USER aventora_crm WITH PASSWORD 'your_strong_password';
    CREATE DATABASE "default" OWNER aventora_crm;

    (If you prefer a different database name, set PG_DATABASE_NAME=your_db_name in .env and create that database.)

  2. In /opt/aventora/crm/.env set the connection details. From inside containers, the host is reachable as host.docker.internal (the compose file adds this). Example:

    PG_DATABASE_HOST=host.docker.internal
    PG_DATABASE_PORT=5432
    PG_DATABASE_USER=aventora_crm
    PG_DATABASE_PASSWORD=your_strong_password
    PG_DATABASE_NAME=default

    Plus SERVER_URL, APP_SECRET, and any Aventora vars.

  3. Use the external-db compose file in the deploy directory:

    cd /opt/aventora/crm
    cp /opt/aventora/aventora-crm/packages/twenty-docker/docker-compose.aventora.external-db.yml docker-compose.yml
    docker compose up -d

Redis still runs in Docker (no change). Only Postgres is external.


Prerequisites

  • VPS: Linux (e.g. Ubuntu 22.04), at least 2GB RAM.
  • Docker and Docker Compose v2 installed on the VPS.
  • A domain (optional but recommended) for HTTPS via a reverse proxy.

1. Build the image

Build from the root of this repository. The frontend is built with REACT_APP_SERVER_BASE_URL; set it to the URL users will use to access the app.

Important — architecture: Build for the same CPU as where the container will run. If you see exec format error during build, you are building for a different architecture (e.g. --platform linux/amd64 on an ARM machine). See “Cross-building (ARM → amd64)” below if you need an amd64 image on an ARM machine.

On a machine that matches your VPS (e.g. both amd64):

cd /path/to/twenty
docker build -f packages/twenty-docker/twenty/Dockerfile \
--build-arg REACT_APP_SERVER_BASE_URL=https://crm.yourdomain.com \
-t aventora-twenty:latest \
.

If your VPS is amd64 and you are on amd64 (Intel/AMD) Windows/Linux: use the same command; you can add --platform linux/amd64 to be explicit.

Cross-building (ARM → amd64): If you are on Apple Silicon or Windows ARM and the VPS is amd64, prefer building on the VPS (clone repo, run the same docker build there). Cross-building with Buildx + QEMU uses a lot of RAM and often fails with "The service was stopped" or exit 130 (OOM). If you still want to cross-build:

  • Give Docker at least 8GB RAM (Docker Desktop → Settings → Resources).
  • Use Buildx and --load:
docker buildx create --use
docker buildx build -f packages/twenty-docker/twenty/Dockerfile \
--build-arg REACT_APP_SERVER_BASE_URL=https://crm.yourdomain.com \
--platform linux/amd64 \
--load \
-t aventora-twenty:latest \
.

(Requires QEMU/binfmt; on Docker Desktop it is usually already set up.)

  • Replace https://crm.yourdomain.com with your actual public URL (must match SERVER_URL in .env).
  • You can build on your machine or in CI, then push to a registry and pull on the VPS; or build on the VPS after cloning the repo.

Alternative (Makefile):

cd packages/twenty-docker
make TAG=aventora-latest prod-build

Then tag for compose: docker tag twenty:aventora-latest aventora-twenty:latest, or use twenty:aventora-latest as the image in the compose file.

2. Deploy directory on the VPS

Create a directory (e.g. /opt/aventora-twenty or ~/twenty-deploy) and add:

  • docker-compose.yml — Use the Aventora compose file:
    • Copy packages/twenty-docker/docker-compose.aventora.yml to docker-compose.yml, or
    • Run: docker compose -f packages/twenty-docker/docker-compose.aventora.yml up -d from the repo root.
  • .env — Copy from packages/twenty-docker/.env.aventora.example and set the required variables.

3. Required .env variables

VariableDescription
SERVER_URLPublic URL of the app (e.g. https://crm.yourdomain.com). Must match the URL used in the browser and, for the build, REACT_APP_SERVER_BASE_URL.
APP_SECRETRandom secret, e.g. openssl rand -base64 32. Keep private.
PG_DATABASE_PASSWORDPostgres password; no special characters.

Optional for Aventora:

VariableDescription
AVENTORA_BASE_URLBase URL of the Aventora Phone service (e.g. https://phone.yourdomain.com). Required for "Engage By Aventora".
AVENTORA_WEBHOOK_SECRETSecret for validating webhooks from Aventora (X-Aventora-Webhook-Secret). Leave unset to accept without verification.
AVENTORA_HIDE_ENTERPRISE_FEATURESSet to true to hide Enterprise settings and enterprise-only UI in Aventora CRM.

See packages/twenty-docker/.env.aventora.example for the full template.

4. Run the stack

From the deploy directory:

docker compose up -d

If you use the Aventora compose file from the repo:

docker compose -f packages/twenty-docker/docker-compose.aventora.yml up -d

Ensure the image aventora-twenty:latest is present (built locally or pulled from your registry).

5. SSL and reverse proxy

For production, put Twenty behind a reverse proxy with HTTPS (e.g. Caddy or Nginx with certbot):

  • Proxy https://crm.yourdomain.com to http://localhost:3000.
  • Set SERVER_URL=https://crm.yourdomain.com in .env.

6. Build fails with "The service was stopped" or exit 130

This usually means the build ran out of memory (OOM). Do one or more of the following:

  • Build on the VPS (same architecture as the image). No QEMU emulation, so much less RAM use.
  • Increase Docker memory: Docker Desktop → Settings → Resources → Memory → set to 8GB or more, then restart Docker.
  • The Dockerfile already sets NODE_OPTIONS=--max-old-space-size=8192 for the server and front build stages; if your host is very limited, use a machine or CI with more RAM.

7. Image name and updates

  • Image name: aventora-twenty:latest (used in docker-compose.aventora.yml).
  • Updates: Rebuild the image (step 1), then restart: docker compose up -d (or docker compose -f ... up -d). Optionally push the new image to a registry and pull on the VPS before restarting.

Summary

StepAction
1Build image from repo root with REACT_APP_SERVER_BASE_URL; tag aventora-twenty:latest.
2On VPS: create deploy dir with docker-compose.aventora.yml (as docker-compose.yml) and .env from .env.aventora.example.
3Ensure image is available (build on VPS or pull from registry).
4Run docker compose up -d.
5Configure reverse proxy and DNS; keep SERVER_URL in sync with the public URL.