# Local Setup

Run the backend, gateway, and Core UI from source with MongoDB and Redis


Run the erxes backend and main frontend from source, with MongoDB and Redis running locally.

Run commands from the repository root unless a step says otherwise. The commands below use a Unix-compatible shell on macOS or Linux; on Windows, use WSL2.

## Prerequisites

- [Git](https://git-scm.com/downloads) to clone the repository.
- [Node.js 22](https://nodejs.org/en/download/), matching the backend CI runtime. A version manager such as [nvm](https://github.com/nvm-sh/nvm) is optional.
- [pnpm 9.12.3](https://pnpm.io/installation), the version declared in the root `package.json` `packageManager` field.
- [Docker with Compose](https://docs.docker.com/compose/install/) for the database example below, or locally installed MongoDB and Redis services.
- `curl` and internet access for the gateway's first-run Apollo Router download.

Nx is a repository dependency. Use `pnpm nx` after installing dependencies; a global Nx installation is unnecessary.

If you use nvm and Corepack, select the toolchain with:

```bash
nvm install 22
nvm use 22
corepack enable
corepack prepare pnpm@9.12.3 --activate
```

Check the active versions:

```bash
node --version
pnpm --version
```

## Clone the repository

```bash
git clone https://github.com/erxes/erxes.git
cd erxes
```

To work from a release instead of the development branch, check out a tag (`git tag -l` lists them). A tag checkout puts Git in detached-HEAD mode; create a branch before making changes, and see the [contribution workflow](https://erxes.io/docs/contribute-codebase) when targeting an upstream contribution.

If you already have a checkout, open a terminal at its root instead. The root contains `package.json`, `pnpm-workspace.yaml`, `backend/`, and `frontend/`.

## Start MongoDB and Redis

The following development example uses MongoDB 7 as a single-node replica set and Redis 7. A replica set supports the multi-document transactions plugins run through `startSession()` calls, so use one even for core-only work. The Compose file is a development example, not a product installer; the databases run in Docker while erxes runs on your host machine.

### 1. Create the Compose file

Create `docker-compose.local.yml` in the repository root:

```yaml
services:
  mongo:
    image: mongo:7
    command: ["mongod", "--replSet", "rs0", "--bind_ip_all"]
    ports:
      - "127.0.0.1:27017:27017"
    volumes:
      - erxes-mongo:/data/db
    healthcheck:
      test: ["CMD", "mongosh", "--quiet", "--eval", "db.adminCommand({ ping: 1 })"]
      interval: 5s
      timeout: 5s
      retries: 20

  redis:
    image: redis:7
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - erxes-redis:/data
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 5s
      timeout: 5s
      retries: 20

volumes:
  erxes-mongo:
  erxes-redis:
```

### 2. Start the containers

```bash
docker compose -f docker-compose.local.yml up -d --wait
```

Initialize the replica set once, when using a new MongoDB volume:

```bash
docker compose -f docker-compose.local.yml exec mongo mongosh --quiet --eval 'rs.initiate({_id: "rs0", members: [{_id: 0, host: "localhost:27017"}]})'
```

### 3. Check database health

```bash
docker compose -f docker-compose.local.yml exec mongo mongosh --quiet --eval 'db.hello().isWritablePrimary'
docker compose -f docker-compose.local.yml exec redis redis-cli ping
```

The expected responses are `true` and `PONG`. Election may take a few seconds; repeat the MongoDB check if it returns `false`. On later starts, reuse the volumes and skip `rs.initiate()`.

### Using existing local databases

You can instead install [MongoDB](https://www.mongodb.com/docs/manual/installation/) and [Redis](https://redis.io/docs/latest/install/) directly. Start both services and configure MongoDB as a [replica set](https://www.mongodb.com/docs/manual/tutorial/deploy-replica-set/) for the setup above. Adjust the connection settings below to match your local ports, replica-set name, and authentication settings.

## Configure the environment

Copy the repository's [sample file](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/.env.sample) if you do not already have a local `.env`:

```bash
cp .env.sample .env
```

Most sample entries are commented out, and several variables below are not in the sample. Open `.env` in your editor and add or uncomment these values for the Docker setup above:

```dotenv
NODE_ENV=development
VERSION=os
MONGO_URL=mongodb://127.0.0.1:27017/erxes?replicaSet=rs0&directConnection=true
REDIS_HOST=127.0.0.1
REDIS_PORT=6379

DOMAIN=http://localhost:3001
REACT_APP_API_URL=http://localhost:4000
GATEWAY_URL=http://localhost:4000

ENABLED_PLUGINS=sales
```

- `REDIS_HOST` and `REDIS_PORT` are read by the shared Redis client. `REDIS_URL` does not configure that client. Set `REDIS_PASSWORD` too if your Redis server requires authentication.
- `DOMAIN` is the frontend origin; `REACT_APP_API_URL` points the frontend to the gateway. Keep these origins consistent with the URLs you open in the browser.
- `GATEWAY_URL` supplies the directly reachable gateway address for generated email links; unset, Core API falls back to `${DOMAIN}/gateway`.
- `ENABLED_PLUGINS=sales` starts the existing `sales_api` and `sales_ui` projects. Use base plugin names, not the `_api` or `_ui` suffixes. The startup scripts append the suffix themselves, so `ENABLED_PLUGINS=sales_api` looks for a nonexistent `sales_api_api` project.
- Multiple plugins use a comma-separated list without spaces, for example `ENABLED_PLUGINS=sales,operation`. Only list plugins present in your checkout.
- For core-only development, omit `ENABLED_PLUGINS` or leave it empty. The startup scripts handle both cases. Do not set it to the literal text `null` or a placeholder name.

Optional background services can be enabled with:

```dotenv
ENABLED_SERVICES=automations,logs
```

The API startup script maps these names to `automations-service` and `logs-service`. Enable them when working with automation execution and persisted logs; the logs service also persists the automatic undo journal. `ENABLED_PLUGINS_ONLY_API` can enable additional backend-only plugins without adding frontend remotes.

Set a private `JWT_TOKEN_SECRET` for local authentication instead of relying on the source-code fallback (`'SECRET'`). Generate a value with `openssl rand -hex 32` and add it to your local `.env`. Keep that value consistent across backend services and out of source control.

Restart the development processes after changing plugin or environment settings.

## Install dependencies and build the shared API library

```bash
pnpm install
pnpm nx build erxes-api-shared
```

Backend services use the compiled entry points of `erxes-api-shared`. Build it before starting them, and rebuild it after changing that library's source.

The root pnpm workspace includes `backend/**` and `frontend/**`. Standalone applications under `apps/`, such as the client portal and help center, have separate setup requirements and are not started by the commands below.

## Start erxes

Use two terminals, both at the repository root.

### 1. Start the backend

```bash
pnpm dev:apis
```

The script ([start-api-dev.js](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/scripts/start-api-dev.js)) starts Core API, the gateway, enabled plugin APIs, and any enabled background services. With the example configuration, it includes `sales_api` on port `3305`.

On first startup, the gateway downloads Apollo Router `v1.59.2` into `backend/gateway/src/apollo-router/temp/` and composes the federated GraphQL schema from the registered services. Wait for the APIs and router to finish starting before opening the UI. The router listens on internal port `50000` by default (`APOLLO_ROUTER_PORT` overrides it); clients connect through the gateway on `4000`.

### 2. Start the frontend

```bash
pnpm dev:uis
```

The script ([start-ui-dev.js](https://github.com/erxes/erxes/blob/f91e8c6f4714c910a32c6b6e14fe085f6435c89e/scripts/start-ui-dev.js)) starts the Core UI host on port `3001` and the frontend remotes listed in `ENABLED_PLUGINS`. With `sales` enabled, its remote runs on port `3005`. Use the Core UI host as the application entry point.

## Check it works

1. Check the backend health endpoints:

```bash
curl --fail http://localhost:3300/health
curl --fail http://localhost:4000/health
```

Both should return `ok`. A gateway health response confirms the HTTP server is running; it does not by itself confirm that GraphQL composition has finished.

2. Check the federated endpoint separately:

```bash
curl --fail http://localhost:4000/graphql \
  -H 'Content-Type: application/json' \
  --data '{"query":"query LocalSetupHealth { __typename }"}'
```

Once the router is ready, the response should contain `data` with `__typename` set to `Query`. HTTP 200 with a GraphQL `errors` array is not a successful API check.

3. Open [http://localhost:3001](http://localhost:3001). On a fresh installation without an owner, the frontend calls `/initial-setup` on the gateway, sees `hasOwner: false`, and directs you to `/create-owner`. Complete that form to create your initial account. If an owner already exists, sign in with the account for that local database.

### Local service URLs

| Service | URL | Purpose |
| --- | --- | --- |
| Core UI | [localhost:3001](http://localhost:3001) | Main application |
| Gateway health | [localhost:4000/health](http://localhost:4000/health) | Gateway HTTP health check |
| GraphQL | [localhost:4000/graphql](http://localhost:4000/graphql) | Federated API endpoint |
| Core API health | [localhost:3300/health](http://localhost:3300/health) | Core API health check |
| Sales API | `http://localhost:3305` | Enabled by the example plugin configuration |
| Sales UI remote | `http://localhost:3005` | Loaded by the Core UI host |

## Troubleshooting

### MongoDB or Redis connection refused

Check that the containers are running and healthy:

```bash
docker compose -f docker-compose.local.yml ps
docker compose -f docker-compose.local.yml logs mongo redis
```

Match the published ports with `MONGO_URL`, `REDIS_HOST`, and `REDIS_PORT`. For this host-based setup, use `127.0.0.1`, not Docker service names such as `mongo` or `redis`.

### MongoDB reports no primary or replica-set errors

Initialize the replica set on a new volume, then confirm `db.hello().isWritablePrimary` returns `true`. The URI's `replicaSet` value must match the configured set name (`rs0` in this guide). An `AlreadyInitialized` response means the volume already has a replica-set configuration; inspect it with `rs.conf()` instead of initializing it again.

If a plugin fails with `Transaction numbers are only allowed on a replica set member or mongos`, your `mongod` is running standalone; convert it to a replica set.

### Apollo Router download fails on first startup

The gateway fetches the router binary with `curl -sSL https://router.apollo.dev/download/nix/v1.59.2 | sh` into `backend/gateway/src/apollo-router/temp/` whenever that file is missing and `NODE_ENV` is not `production`. A network restriction or interrupted download leaves the gateway stuck at startup. Check outbound access to `router.apollo.dev`, delete a partial `temp/` directory, and restart `pnpm dev:apis`. You can also run the same `curl` command inside `backend/gateway/src/apollo-router/temp/` manually, as the gateway's error message suggests.

### A plugin project or remote cannot be found

`ENABLED_PLUGINS` takes base names: `sales` maps to `sales_api` and `sales_ui`. Writing `sales_api` produces `sales_api_api`, which does not exist. Check spelling, remove spaces and trailing commas, and confirm both matching projects exist. `posclient` has a backend plugin but no `posclient_ui`, so it belongs in `ENABLED_PLUGINS_ONLY_API`, not `ENABLED_PLUGINS`. Restart both development terminals after changing the list.

### Cannot resolve erxes-api-shared

Run `pnpm nx build erxes-api-shared` from the repository root, then restart the affected API. Repeat the build after editing the shared library.

### GraphQL is unavailable but gateway health is OK

Inspect the backend terminal for service registration, schema composition, or Apollo Router errors. Confirm the selected APIs are running and that port `50000` is available. The first run requires access to the Apollo Router download service.

### A port is already in use

Stop the other process using the reported port. The default setup needs ports `27017`, `6379`, `3300`, `4000`, `50000`, and `3001`, plus each enabled plugin's API and UI ports. Avoid running a second copy of Core API or the gateway alongside `pnpm dev:apis`.

### Node.js runs out of heap memory

Start with fewer plugins. If your machine has enough available memory, add `NODE_OPTIONS=--max-old-space-size=4096` to `.env` and restart the development processes. This limit applies per Node.js process, so account for the number of services you run concurrently.

## Stop and restart

Press `Ctrl+C` in each development terminal. Stop the database containers with:

```bash
docker compose -f docker-compose.local.yml down
```

The named volumes retain your local data. To resume, start the containers with `up -d --wait`, then run `pnpm dev:apis` and `pnpm dev:uis` again. Reinitializing the replica set is unnecessary when reusing the same MongoDB volume.

