orbotodocs
Self-hosting

Multiple domains

Run one workspace under a second brand domain for customer-facing invitations, while the internal address stays as-is.

Run orboto on more than one public domain - typically because you want customer-facing invitations to use a separate brand domain while the workspace itself stays under your internal one. Example: workspace lives at orboto.example.internal, but every invitation email a customer receives points at kunden.example.com.

Three places to touch. Order matters.

1. Coolify - add the second domain + extend WEB_ORIGIN

Add the customer-facing hostname (e.g. kunden.example.com) to your API service in Coolify so its reverse proxy routes the second domain to the same container. TLS is automatic.

Then extend the WEB_ORIGIN env var on the API service to list BOTH hostnames:

WEB_ORIGIN=https://kunden.example.com,https://orboto.example.internal

Two rules to know:

  • Order matters. orboto builds every outbound email link (invitations, password resets, alerts) from the first entry of the comma list. List the customer-facing domain first if you want invitation links to land there.
  • All listed entries are CORS-allowed. Both domains keep working for the web app + the REST API.

Restart the API container so the new process.env value is picked up. Coolify's "Redeploy" button is enough.

2. Email From-address - Admin Settings, not env

The From-address of outbound mail lives in system_config (DB-backed), not in env. Set it through the admin UI so it matches the customer-facing domain:

  • Admin → System Settings → Email
  • For OMS / Resend: change the From field (e.g. einladung@kunden.example.com)
  • For SMTP: change the SMTP From field

Cache: the EmailService memoises the sender config for ~60 seconds, so a fresh send within that window still uses the old From. Either wait the minute or restart the API container.

3. DNS at the email provider

For the new domain's emails to actually deliver (and not land in spam), you need the standard outbound-mail records:

RecordWhy
SPF (TXT)Authorises your mail provider to send from kunden.example.com. Provider-specific value - OMS / Resend / SMTP provider give the exact string in their console.
DKIM (CNAME or TXT)Cryptographic signature so recipients can verify the mail came from your provider. Provider-specific too; usually a couple of CNAMEs.
DMARC (TXT, optional but recommended)Tells receivers what to do with mail that fails SPF / DKIM. Start with p=none to monitor, tighten later.

Where to set the records depends on your mail provider:

  • OMS (orboto Mail Service): Customer Portal → Domains → "Add domain" → kunden.example.com → Portal shows the exact CNAMEs to paste at your DNS host.
  • Resend: Dashboard → Domains → Add domain → same flow.
  • SMTP provider (SES, Mailgun, …): each has its own DNS-records page; the same SPF + DKIM concept applies.

Skipping this step is the most common cause of "invitations arrive but say 'via amazonses.com'" - which usually drops them in junk and erodes the brand.

Verification

After the three steps above:

  1. Generate a test invitation from the admin UI.
  2. Open the email:
    • From matches the new domain (e.g. einladung@kunden.example.com)
    • Link in the body points at https://kunden.example.com/... - NOT the internal domain
  3. Select the link → the accept-invitation page loads on the customer-facing domain.

Troubleshooting

The From address is right, but the link in the email still points at the old domain. Cause: either the customer domain is listed second (not first) in the WEB_ORIGIN comma-list, or the API container hasn't picked up the change yet. Fix: put the customer-facing domain first in WEB_ORIGIN, then restart the API container (redeploy, on Coolify).

The link is right, but the From address is still the old one. Cause: the email sender config is cached for about 60 seconds after a save, or the From field wasn't actually saved. Fix: wait a minute and resend the test, or re-check Admin → System Settings → Email to confirm it saved.

Neither field updates, and no invitation email arrives at all. Cause: WEB_ORIGIN resolves to a loopback or internal-only address (a comma-list starting with localhost, a bare IP, *.local, or *.internal) - orboto deliberately suppresses invitation links rather than send a broken one. Fix: put a real public hostname first in the WEB_ORIGIN list.

Why this setup makes sense

The same orboto workspace can present multiple brands. Internal team uses the workspace at the internal hostname (intranet bookmark, SSO redirect, etc.). External invitees see a branded customer-facing URL with matching email From - feels like a dedicated customer portal, no rebrand confusion.

It is the same workspace under the hood: one database, one set of users, one set of permissions. Only the URL the recipient sees and the From-address on the mail change.

On this page