Architecture
Traffic enters through Traefik on ports 80 and 443. The panel and Keycloak share PostgreSQL. Each hosted site
runs in Docker: a web container (generic PHP stack or the dedicated WordPress image), an
optional MariaDB container, and optional SFTP — each stack on an isolated
bridge network. WordPress sites use image jigsaw-wordpress:<version> (extends the PHP image
with baked WordPress core); customer files are still bind-mounted from the host under
/home/.../public_html.
High-level diagram
Internet
|
[Traefik] :80 / :443 — Let's Encrypt (HTTP-01)
|
|-- PANEL_DOMAIN → Jigsaw panel (Node, SSR)
|-- auth.PANEL_DOMAIN → Keycloak (OIDC)
|-- traefik.PANEL_DOMAIN → Traefik API (OAuth2 Proxy + Keycloak)
|
+-- customer site hostnames → per-site web (Nginx + PHP, or WordPress image) on traefik_public + site network
Internal:
PostgreSQL 17 — panel DB + Keycloak DB
OAuth2 Proxy — forward-auth for Traefik dashboard
Per site (example):
jigsaw_<slug>_net — web + db (+ optional SFTP)
Data flow
- Traefik terminates TLS and routes by
Hostheader. - The panel talks to Docker via the host socket to create networks, volumes, and containers.
- Site web containers join
traefik_public(for routing) and a dedicated site network. - Database containers attach only to the site network (not exposed to the public internet by default).
On-disk layout (typical)
| Path | Purpose |
|---|---|
data/postgres |
PostgreSQL data directory (panel + Keycloak) |
data/sites |
Site files (bind-mounted into web containers) |
data/databases |
Per-site MariaDB data |
docker/compose |
Generated compose snippets for site stacks |
Panel data model (summary)
The panel stores users, sites, services (web / database / sftp), and an activity log in PostgreSQL. See app/models/schema.ts for the Drizzle schema.