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.aiorhttp://YOUR_VPS_IP:3000)APP_SECRET=— runopenssl rand -base64 32and paste the resultPG_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.
-
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_namein.envand create that database.) -
In
/opt/aventora/crm/.envset the connection details. From inside containers, the host is reachable ashost.docker.internal(the compose file adds this). Example:PG_DATABASE_HOST=host.docker.internalPG_DATABASE_PORT=5432PG_DATABASE_USER=aventora_crmPG_DATABASE_PASSWORD=your_strong_passwordPG_DATABASE_NAME=defaultPlus
SERVER_URL,APP_SECRET, and any Aventora vars. -
Use the external-db compose file in the deploy directory:
cd /opt/aventora/crmcp /opt/aventora/aventora-crm/packages/twenty-docker/docker-compose.aventora.external-db.yml docker-compose.ymldocker 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.comwith your actual public URL (must matchSERVER_URLin.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.ymltodocker-compose.yml, or - Run:
docker compose -f packages/twenty-docker/docker-compose.aventora.yml up -dfrom the repo root.
- Copy
- .env — Copy from
packages/twenty-docker/.env.aventora.exampleand set the required variables.
3. Required .env variables
| Variable | Description |
|---|---|
SERVER_URL | Public 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_SECRET | Random secret, e.g. openssl rand -base64 32. Keep private. |
PG_DATABASE_PASSWORD | Postgres password; no special characters. |
Optional for Aventora:
| Variable | Description |
|---|---|
AVENTORA_BASE_URL | Base URL of the Aventora Phone service (e.g. https://phone.yourdomain.com). Required for "Engage By Aventora". |
AVENTORA_WEBHOOK_SECRET | Secret for validating webhooks from Aventora (X-Aventora-Webhook-Secret). Leave unset to accept without verification. |
AVENTORA_HIDE_ENTERPRISE_FEATURES | Set 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.comtohttp://localhost:3000. - Set
SERVER_URL=https://crm.yourdomain.comin.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=8192for 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 indocker-compose.aventora.yml). - Updates: Rebuild the image (step 1), then restart:
docker compose up -d(ordocker compose -f ... up -d). Optionally push the new image to a registry and pull on the VPS before restarting.
Summary
| Step | Action |
|---|---|
| 1 | Build image from repo root with REACT_APP_SERVER_BASE_URL; tag aventora-twenty:latest. |
| 2 | On VPS: create deploy dir with docker-compose.aventora.yml (as docker-compose.yml) and .env from .env.aventora.example. |
| 3 | Ensure image is available (build on VPS or pull from registry). |
| 4 | Run docker compose up -d. |
| 5 | Configure reverse proxy and DNS; keep SERVER_URL in sync with the public URL. |
Related docs
- README.md — documentation index
- docker-hub-deploy.md — Docker Hub build/push flow (
docker/folder) - CRM Complete Guide — post-deploy upgrade, TIPS, and ops
- Nginx example (aventora-crm repo)