app.example.com, while your website stays on example.com. The hosted ReachCrew runs this way at app.reachcrew.ai. One image serves any domain: the address is a runtime setting, so nothing is rebuilt.
What you need
- A working install (Docker Compose or Dokploy).
- A domain whose DNS you can edit.
- A server with ports 80 and 443 open to the internet.
1. DNS
Add anA record for the subdomain pointing at your server’s IPv4 address (and an AAAA record for IPv6, if it has one):
Check it before going on:
dig +short app.example.com must print your server’s address. The certificate cannot be issued until it does.
2a. Docker Compose: Caddy
docker-compose.https.yml adds Caddy, which gets and renews a Let’s Encrypt certificate on its own. Download it next to docker-compose.yml:
.env:
COMPOSE_FILE=docker-compose.yml:docker-compose.external-db.yml:docker-compose.https.yml.
Then docker compose up -d and open https://app.example.com. The first request takes a few seconds while the certificate is issued; docker compose logs caddy shows it.
What changes:
APP_URLbecomeshttps://<APP_DOMAIN>. Do not setAPP_URLyourself as well.- The app’s port 3000 is no longer published. Everything goes through Caddy, which is also what makes the client address that rate limits use trustworthy.
- Uploads up to 50 MB (lead imports) and requests up to 10 minutes (imports, exports) pass through.
APP_URL is no longer localhost, the app refuses to start while any CHANGEME secret remains. Replace them first (openssl rand -hex 32).
2b. Dokploy
Dokploy’s router already terminates HTTPS. In the application’s Domains tab, addapp.example.com, port 3000, HTTPS on, certificate Let’s Encrypt. Then set APP_URL=https://app.example.com (or BETTER_AUTH_URL) under Environment and redeploy. See Dokploy.
Behind Cloudflare
With the record proxied (orange cloud):- Set SSL/TLS mode to Full (strict). Caddy and Dokploy hold real certificates, so strict works, and Flexible would loop redirects.
- Set
CLIENT_IP_HEADER=cf-connecting-ip. Otherwise every request seems to come from a Cloudflare address, and sign-in and request rate limits would count all your users as one. - Only do this while the server is reachable through Cloudflare alone (firewall ports 80 and 443 to Cloudflare’s ranges): anyone reaching it directly could set that header.
3. After the address changes
The address is stored in a few places outside ReachCrew. After moving (fromlocalhost, or from another domain):
- Unipile: Settings → LinkedIn → Connection → Register again. Webhook URLs carry the old address until you do, and registration is skipped entirely on
localhost. - Google sign-in (if used): add
https://app.example.com/api/auth/callback/googleto the OAuth client’s redirect URIs. - Call recorder extension: it trusts
https://app.reachcrew.aiout of the box. For your own domain, each caller adds it once on the extension’s Options page (details). - People’s sessions: sign-in cookies belong to the old address, so everyone signs in again once.
Troubleshooting
The browser shows a certificate error, or Caddy logs 'challenge failed'
The browser shows a certificate error, or Caddy logs 'challenge failed'
Let’s Encrypt could not reach the server on port 80 for this name. Check that the DNS record resolves to this server, that ports 80 and 443 are open in the firewall and the cloud provider’s security group, and that nothing else (an old nginx) holds port 80. Behind Cloudflare, set SSL to Full (strict). Then
docker compose restart caddy.Too many redirects
Too many redirects
Cloudflare’s SSL mode is Flexible: it calls the server over http, which redirects to https, forever. Set it to Full (strict).
Sign-in or email links still point at localhost
Sign-in or email links still point at localhost
APP_URL (or BETTER_AUTH_URL) still holds the old address. With the Caddy file it comes from APP_DOMAIN; remove any APP_URL line from .env and run docker compose up -d again.