Messages & Calendars Self-Hosting Guide¶
This guide walks you through deploying Messages (collaborative inbox) and Calendars (calendar app) from La Suite territoriale on a single VPS using Docker Compose and Traefik.
Architecture¶
┌─────────────┐
───►│ Traefik │
│ │ (HTTPS) │
│ └──────┬──────┘
│ │
┌──────────────────┼───────────┼──────────────────────────┐
│ │ │ │
▼ ▼ ▼ ▼
┌──────────────┐ ┌──────────────┐ ┌──────────┐ ┌──────────────────┐
│ Messages │ │ Calendars │ │ Keycloak │ │ CalDAV │
│ Frontend │ │ Frontend │ │ (OIDC) │ │ (SabreDAV/PHP) │
│ (Caddy) │ │ (Caddy) │ │ │ │ │
└──────┬───────┘ └──────┬───────┘ └────┬─────┘ └────────┬─────────┘
│ │ │ │
▼ ▼ │ │
┌──────────────┐ ┌──────────────┐ │ │
│ Backend │ │ Backend │ │ │
│ (Django) │◄─┤ (Django) │────────┘ │
└──────┬───────┘ └──────┬───────┘ │
│ │ │
▼ ▼ │
┌──────────────┐ ┌──────────────┐ │
│ PostgreSQL │ │ Redis │ │
│ OpenSearch │ │ (shared) │ │
│ MinIO (S3) │ └──────────────┘ │
└──────┬───────┘ │
│ │
▼ ▼
┌──────────────┐ ┌──────────────────┐
│ MTA-in/out │ │ PostgreSQL │
│ (Postfix) │ │ (caldav DB) │
└──────┬───────┘ └──────────────────┘
│
▼
┌──────────────┐
│ Brevo SMTP │
│ (Relay) │
└──────────────┘
Services Summary¶
| Service | Port/Domain | Purpose |
|---|---|---|
| Traefik | 80, 443 | Reverse proxy, TLS termination |
| Messages Frontend | messages.<domain> |
Next.js app, Caddy reverse proxy |
| Messages Backend | api.messages.<domain> |
Django REST API |
| Calendars Frontend | calendars.<domain> |
Next.js app, Caddy reverse proxy |
| Calendars Backend | api.calendars.<domain> |
Django REST API |
| Keycloak | auth.messages.<domain> |
OIDC identity provider |
| CalDAV | internal | SabreDAV calendar server |
| MTA-in | 25 (SMTP) | Inbound email (Postfix) |
| MTA-out | internal | Outbound email (Postfix → Brevo) |
| PostgreSQL | internal | Shared database |
| Redis | internal | Cache & message broker |
| OpenSearch | internal | Full-text search (Messages) |
| MinIO | internal | S3-compatible object storage |
Shared Components¶
Both applications share:
- Keycloak — single instance with separate realms (
messages,calendars) - PostgreSQL — separate databases (
messages,calendars,caldav) - Redis — on
messages_internalDocker network - Brevo SMTP — same relay for all outbound email
Key Design Decisions¶
Static IP Network for Caddy DNS¶
Caddy's Go DNS resolver caches the backend container's IP. When the backend is recreated, Caddy keeps sending requests to the old IP, causing intermittent 400 errors. Both frontends use a dedicated Docker network with static IPs.
CalDAV Through Django¶
Calendar operations go through the Django backend proxy (/caldav/*), which adds X-LS-User and API key headers before forwarding to the CalDAV PHP server. Direct CalDAV proxy would return 401 and trigger a frontend re-auth loop.
Messages ↔ Calendars Integration¶
Calendars communicates with Messages through a server-to-server API channel with specific scopes. Invitations are sent via the Messages API submit endpoint, which routes through Brevo.
Next Steps¶
Follow the guide sections in order. Each one builds on the previous.