Architecture overview
An Exovo deployment is a set of cooperating containers managed by Docker Compose. Understanding what each one does makes logs easier to read, firewall rules easier to reason about, and troubleshooting much faster.
The container stack
Internet
│
80/443 │ 5060/5061/5080 (SIP)
┌─────────────────┤ 5090 (tunnel) 5091 (VPN)
▼ │ 16384–16484 (RTP)
┌─────────┐ │ │
│ traefik │ │ ▼
│ (proxy) │ │ ┌──────────────────────────┐
└────┬────┘ │ │ core (FreeSWITCH) │
│ HTTP │ │ + tunnel (sidecar) │
▼ │ │ + phoneproxy (sidecar) │
┌─────────┐ ESL/API │ └──────────┬───────────────┘
│ web │◄───────────┼─────────────────┘
│ console │ │ 3478/5349 + 49152–49252
└────┬────┘ │ │
│ SQL │ ▼
▼ │ ┌─────────┐
┌─────────┐ └──────────►│ turn │ (media relay,
│ db │ └─────────┘ browser calls)
└─────────┘
| Service | Image | Role |
|---|---|---|
traefik |
Traefik 3 | Reverse proxy: TLS termination, automatic certificates, HTTP→HTTPS |
web |
Exovo web | The web console and web client, provisioning, APIs, background workers |
core |
Exovo core | FreeSWITCH — SIP registration, call routing, media, voicemail, faxing |
db |
PostgreSQL | All configuration and history (users, rules, CDRs, chat, settings) |
turn |
coturn | Media relay so browsers behind NAT can send/receive call audio |
tunnel |
Exovo tunnel | The :5090 front door for mobile apps and on-site SBCs (mutual TLS) |
phoneproxy |
Exovo phoneproxy | Lets admins open a desk phone's own web UI through the console |
dockerproxy |
socket proxy | A locked-down, read-mostly window onto Docker for status and update checks |
The telephony core (FreeSWITCH)
core owns everything real-time: SIP registrations from phones and trunks, call routing, RTP
media, music on hold, voicemail and fax. It exposes SIP on 5060 (phones), 5061 (SIP over TLS)
and 5080 (trunks), plus the RTP range 16384–16484/udp. It doesn't make routing decisions on its
own — for each call it asks the web app for dialplan instructions, so all configuration lives in
one place (the database) and takes effect without reloads.
Two sidecars share the core's network namespace:
tunnelterminates the Exovo tunnel on port 5090 — the single mutual-TLS port that mobile apps and on-site SBCs use for SIP and media. It fetches its certificates from the web app on startup and holds no secrets of its own.phoneproxyreaches phone web UIs on the LAN or over the remote-phone VPN — networks the hardened web container deliberately can't see — so the console's "open phone UI" button works without exposing phones directly.
The web console and web client
web is a single ASP.NET application serving both the admin console and the end-user web client,
plus phone provisioning, the REST endpoints FreeSWITCH calls back into, and background workers
(backups, CRM sync, update checks, the license phone-home). It's the only service that talks to
the database, and it never faces the internet directly — Traefik fronts it.
Database and persistence
Everything configurable lives in PostgreSQL. Bulky media lives on the filesystem:
| Path on host | Contents |
|---|---|
/var/lib/exovo/db |
PostgreSQL data |
/var/lib/exovo/recordings |
Call recordings |
/var/lib/exovo/ivr |
Digital-receptionist and system prompts |
/var/lib/exovo/fax |
Sent/received faxes |
/var/lib/exovo/certs, acme |
TLS certificates |
/var/lib/exovo/keys |
Data-protection keys (encrypts stored secrets) |
/var/lib/exovo/logs |
FreeSWITCH logs |
Backing up /var/lib/exovo plus the database covers the entire system — which is exactly what
the built-in backup does.
Reverse proxy and TLS
Traefik owns ports 80 and 443, redirects HTTP to HTTPS, and either maintains an automatic certificate for your FQDN or serves the custom one you provided during onboarding. Everything web-ish — console, provisioning, phone-UI proxy — flows through it; internal services are never published directly.
How a call flows through the system
- A phone (or trunk) sends SIP to
coreon 5060/5080. coreaskswebhow to route the call;webanswers from the database — user, ring group, queue, IVR, office-hours rules.- Media (RTP) flows directly between the endpoint and
core. Browser calls relay throughturn; mobile-app and SBC calls arrive inside the tunnel on 5090. - When the call ends,
corereports the call record toweb, which stores the CDR, triggers recording transcription and CRM journaling, and updates live dashboards.
Security posture, in brief
Containers run with dropped capabilities and no-new-privileges; the web app has no Docker
socket (the deny-by-default dockerproxy allows only status reads and a scoped restart); the
internet-facing tunnel holds no keys; and actions that need root on the host (updates, restores)
are performed by small systemd watchers reacting to marker files — the containers themselves
can't touch the host. Details in the security section.