This repository is meant to run behind a shared reverse proxy. The target layout is:
- a public TLS-terminating proxy stack at
/root/projects/nginx-proxy - this application stack at
/root/projects/evo-crm-community - public frontend and API hostnames mapped through the proxy
The deploy playbook and compose files are already wired for this model.
The app stack exposes two public entrypoints:
- frontend:
https://<site_dns> - backend:
https://api.<site_dns>
Internally, the stack still uses service names and private Docker networking. The reverse proxy only sees the public hostnames.
- Docker and Docker Compose installed on the target host
- the shared reverse proxy repository present at
/root/projects/nginx-proxy - this repository present at
/root/projects/evo-crm-community - DNS records pointing the chosen hostnames to the server
- valid SMTP settings if you want password reset and confirmation email flows
The deployment assumes the proxy stack and this app stack share a Docker
network named reverse-proxy.
The install playbook is deploy/install.yml. It:
- validates the requested DNS name
- creates or reuses a host-local
.deploy-secrets.jsonfile with secrets - writes a host-specific
.envfile - ensures the shared
reverse-proxynetwork exists - starts the shared proxy stack
- starts this repository's app stack
- waits for the frontend and backend health checks over HTTPS
Use only the public DNS name, without http:// or a path:
ansible-playbook -i motoko-new, deploy/install.yml -e site_dns=app.example.comIf you are bootstrapping a fresh clone locally or on a server where the app repository is already present, you can use the bundled script instead:
make bootstrap SITE_DNS=app.example.comThe playbook derives the public hosts from site_dns:
- frontend host:
app.example.com - backend host:
api.app.example.com
If you need to deploy by hand, the equivalent sequence is:
cd /root/projects/evo-crm-community
docker network inspect reverse-proxy >/dev/null 2>&1 || docker network create reverse-proxy
cd /root/projects/evo-crm-community && docker compose --env-file .env up -d --remove-orphansThe .env file must set at least:
BACKEND_HOSTFRONTEND_HOSTBACKEND_URLFRONTEND_URLAUTH_SERVICE_URLCORS_ORIGINSLETSENCRYPT_EMAIL
The install playbook also fills in the shared database passwords, app secrets,
bot runtime secret, and the frontend VITE_* URLs so the deployed stack boots
with a complete configuration.
The generated secret values are stored on the target host in
/root/projects/evo-crm-community/.deploy-secrets.json and reused on future
runs.
The repository ships with .env.example. The most important
values for a reverse-proxy deployment are:
| Variable | Purpose |
|---|---|
BACKEND_HOST |
Public hostname for the API gateway, usually api.<site_dns> |
FRONTEND_HOST |
Public hostname for the UI |
BACKEND_URL |
Public URL the backend uses when generating absolute links |
FRONTEND_URL |
Public UI URL used by auth redirects and browser-facing links |
AUTH_SERVICE_URL |
Public auth URL exposed to the browser |
CORS_ORIGINS |
Allowed browser origins, usually frontend plus backend hosts |
LETSENCRYPT_EMAIL |
Email used by the shared proxy for TLS issuance |
The frontend image is configured through build-time Vite variables, while the backend services consume runtime environment variables. Keep the public URLs consistent across both layers.
The shared proxy stack must:
- listen on the same Docker network as this app stack
- route
BACKEND_HOSTto the API gateway service on port3030 - route
FRONTEND_HOSTto the frontend container on port80 - terminate TLS for both hostnames
The gateway and frontend containers in this repository already publish the
required proxy metadata via VIRTUAL_HOST, VIRTUAL_PORT, and
LETSENCRYPT_HOST.
After deploy, verify these URLs:
curl -k https://app.example.com
curl -k https://api.app.example.com/health/readyIf either one fails, check:
- DNS points to the right host
- the
reverse-proxynetwork exists - the proxy stack is running
- the backend service logs for boot or migration failures
Usually means one of these:
- the shared proxy cannot reach the app container
- the
reverse-proxynetwork is missing - the hostname in
VIRTUAL_HOSTdoes not match the public DNS name - the upstream service name changed and the proxy is still pointing at the old one
Usually means CORS_ORIGINS does not include both public hosts. Make sure the
frontend origin and the backend origin are listed in the generated .env.
That means one of the public URL variables is still set to a local value. In a real deploy, replace the localhost defaults with the actual public domains.
- docs/backup.md for backup and restore steps
- nginx/README.md for the backend gateway routing table
- deploy/install.yml for the Ansible flow
- docs/install-playbook.md for playbook usage details
- docker-compose.yml for the full service topology