Building a Matrix Stack on Rootless Podman
I spent the end of February turning Matrix from a rough idea into a real stack layout.
The main goal was not just "run Synapse." It was to run it in the same style as the rest of my infrastructure:
- one Unix user per app stack,
- rootless containers,
- a reverse-proxy chain instead of direct public exposure,
- and a clear split between static config and runtime state.
The Shape I Wanted
The stack had five main parts:
- Synapse as the homeserver,
- Postgres as the database,
- coturn for TURN,
- a backend NGINX layer for the app-facing HTTP flow,
- and a TLS sidecar in front of that backend.
I also wanted the external routing to match the pattern I already used elsewhere:
- public edge proxy first,
- internal environment proxy second,
- application stack behind that,
- and loopback-bound internal entry points wherever possible.
Why I Did Not Want A Flat Compose File
A simple one-container or two-container setup would have been faster.
But for long-term operations it would have mixed too many concerns:
- public routing,
- internal TLS,
- Synapse config,
- TURN exposure,
- and database persistence.
Once those concerns are separated, the stack becomes easier to reason about:
- proxy issues stay proxy issues,
- app issues stay app issues,
- and the files on disk line up with the service boundaries.
The Practical Layout
The directory structure mattered almost as much as the containers:
- one directory for backend proxy config,
- one for Synapse data,
- one for TURN config,
- one for TLS material,
- one for logs,
- and one for persistent database data.
That sounds boring, but it is the difference between a stack you can recover and a stack you can only remember.
What Turned Out To Matter Most
The best early decision was keeping the stack under its own Unix user.
That gave me:
- cleaner file ownership,
- less accidental secret sharing,
- easier Vault integration later,
- and a more obvious mental model for what belongs to Matrix and what does not.
The whole thing stopped feeling like "a service running somewhere" and started feeling like a defined application boundary.
That was the right foundation for everything that came after: Vault integration, MatrixRTC, MAS, and backup work.