Skip to content
Open source, AGPL v3.See the plans

Docs

Use an existing reverse proxy

Run Reloop CRM behind your own reverse proxy instead of the bundled Caddy.

Answer "yes" to the reverse proxy question. Caddy then stays off, and the app listens on 127.0.0.1:3000.

Send every path of your domain to that address, for example with Caddy:

crm.example.com {
	reverse_proxy 127.0.0.1:3000
}

When your proxy runs in its own container, 127.0.0.1 is not the host. Set APP_BIND=0.0.0.0 in deploy/.env, block port 3000 in your firewall, and point the proxy at the host address. Then run docker compose up -d in the deploy folder.

Move to another domain

install.sh writes the domain you gave it. Nothing changes it later, so a domain you add in front of an existing install does not reach the app. The sign in page still works, but every address the app prints for a provider, an SSO callback or an OAuth redirect names the old host.

Three values carry the domain and they must change together. Edit deploy/.env:

RELOOP_DOMAIN=crm.example.com
APP_URL=https://crm.example.com
API_URL=https://crm.example.com

RELOOP_DOMAIN is the name Caddy asks a certificate for. Change only API_URL and Caddy keeps serving the old certificate.

APP_URL takes a comma separated list. Keep the old host on it while you move, so a bookmark and an open session still work:

APP_URL=https://crm.example.com,https://old-host.example.com

Then restart:

cd deploy && docker compose up -d

Before you restart, add the new redirect URI at every provider you connected. Google, Microsoft and Slack each reject a callback they do not know, so sign in through that provider breaks the moment the app starts naming the new host. Keep the old URI as well until the move is done. The addresses are printed in Settings, Connections.

Add a colleague

The owner and every admin add people in the app. Open Settings, Members and choose Add person. Enter the address, the name and the role. The CRM creates the account and shows a one-time password once. Copy it and pass it to the person yourself. They sign in with the address and that password, and they change it in Settings, General.

Two rules decide whether the button works:

  • The owner grants the address as part of adding the person. A default install writes only your own address into ALLOWED_SIGN_IN, so a colleague would be refused. The owner adding them writes the address to a second list in the database, and sign-in then accepts both lists. ALLOWED_SIGN_IN stays the floor: the app only adds to the database list and never edits the environment. An admin who is not the owner still gets the old message naming the value to put in deploy/.env.
  • PASSWORD_SIGN_IN="1" must be set, because the new person signs in with a password. Without it the button stays hidden. With Google, Microsoft or an identity provider a colleague on an allowed domain signs in alone and needs no account from you.

Your own session must be less than five minutes old, the same rule that guards a password change. Sign out and sign in again when the CRM asks for it.

The shell still works and does the same thing:

printf '%s' 'a long password' | docker compose exec -T api bun apps/api/scripts/create-owner.ts colleague@example.com

That script creates the account and sets the password. The person becomes a member on the first sign-in. Change the role in Settings, Members.

Removing a person is not in the app. Take the address off ALLOWED_SIGN_IN in deploy/.env and run docker compose up -d in the deploy folder. The next request locks the person out. The account, the member row and every record stay. For a person on a domain you keep, set a password only you know with the script above; that also ends every session they have.

An address the owner granted in the app sits in the database, not in deploy/.env, so the step above does not reach it. List and remove those addresses in the deploy folder:

docker compose exec -T api bun apps/api/scripts/sign-in-grants.ts list
docker compose exec -T api bun apps/api/scripts/sign-in-grants.ts revoke colleague@example.com

The next request locks that person out.