Infrastructure & DevOps
Hosting
The CMS platform is hosted on Scaleway (EU data residency required). S3-compatible object storage is provided by Scaleway Object Storage or MinIO.
Container Topology
| Container | Role | Key configuration |
|---|
fo | Nuxt SSG static artifacts | Served by edge nginx, static behind CDN |
bo | Nuxt SPA | Edge history fallback on /bo/* |
api-php | PHP-FPM (Symfony + API Platform) | Non-root user, stateless, horizontally scalable |
api-nginx | Nginx fastcgi proxy | Non-root user, client_max_body_size 12m |
postgres | PostgreSQL | ICU locale alignment with PHP (pinned to v74) |
redis | Cache | JWKS cache, RateLimiter, preview one-time tokens |
elasticsearch | Search engine | Full-text search, per-website index isolation, multilingual analyzers |
nginx-edge | Reverse proxy (TLS termination) | Brotli/gzip, HSTS preload, security headers, X-Request-Id |
| Service | Role |
|---|
| Keycloak | OIDC identity provider (one BO client per tenant) |
| S3 | Object storage (presigned uploads, tenant-prefixed) |
| CDN | Static asset delivery with Surrogate-Key invalidation |
| Elasticsearch | Full-text search (Arabic support planned) |
| Matomo | Self-hosted analytics (GDPR-compliant) |
| n8n | Workflow automation |
Monitoring & Logging
Logging
- Symfony: Monolog (structured JSON logging)
- Log aggregation: ELK or EFK stack
- Correlation:
X-Request-Id header propagated across all services
- Retention: 90 days
Metrics & Observability
- Stack: OpenTelemetry + Prometheus + Grafana
- Key metrics: request latency (p50/p95/p99), error rate, cache hit ratio, tenant resource usage
- Alerting: Grafana alerts on SLO breach
SLO Targets
| Metric | Target |
|---|
| API p95 latency | < 300ms |
| API p99 latency | < 600ms |
| Error rate | < 1% |
| FO Lighthouse score | ≥ 90 |
| TTFB | ≤ 600ms |
| Cache hit ratio | ≥ 85% |
Security
Container Hardening
- All containers run as non-root user
readOnlyRootFilesystem enabled
no-new-privileges security option
- Vulnerability scanning: Trivy and Grype in CI
Secrets Management
- Quarterly secret rotation
- Rate-limiter pepper included in rotation cycle
- No secrets in Docker images or environment files committed to git
Backup & Recovery
| Aspect | Configuration |
|---|
| Database | Daily full backup + WAL (Write-Ahead Log) continuous |
| Retention | 30 days |
| Restore test | Monthly on shadow database — must pass |
| S3 | Versioning enabled, cross-region replication for production |
Keycloak Configuration
Per-Tenant Setup
- One public PKCE client per tenant (
bo-<tenant>)
- No Refresh Token in V0.1 (BFF planned for V0.2)
- Required claims:
roles, tenantId, email, name
api-cms audience mapper configured
azp allowlist maintained per environment
- Logout: RP-Initiated Logout
Automation
- New tenant provisioning target: < 10 minutes
- Scripted via Keycloak Admin API
S3 / Object Storage
| Setting | Value |
|---|
| Upload method | Presigned PUT (max 10 MiB) |
| Encryption | SSE-S3 or SSE-KMS |
| Access | BlockPublicAccess = true |
| IAM | Scoped to tenant prefix (tenant_id/media/...) |
| CORS | Allowed origins whitelist |
| Upload validation | content-length-range enforced, HEAD post-upload with backoff |
CDN Configuration
Cache Invalidation
- Primary:
Surrogate-Key headers (exposed via CORS)
- Key format:
t:<tenant>, type:<entity>, locale:<xx>, slug:<slugAscii>
- Fallback: Pattern-based purge (documented runbook)
Vary: Origin, Accept-Language, Accept-Encoding
- CORS headers exposed through CDN
Authorization stripped on /v1/public/* paths
DNS & TLS
- FO domain pattern:
fo.<tenant>.<domain>
- HSTS preload enabled
- TLS termination at
nginx-edge
Prerequisites Matrix
Each prerequisite must be validated by its owner before V0.1 can proceed.
| Prerequisite | Owner | Dependency |
|---|
| Keycloak realms + clients | Platform team | API + BO authentication |
| S3 buckets + IAM policies | Platform team | Media uploads |
| CDN with Surrogate-Key | Platform team | Cache invalidation |
| DNS + TLS certificates | Platform team | FO multi-tenant domains |
| PostgreSQL with ICU v74 | DBA / Platform | Slug normalization, i18n |
| Redis instance | Platform team | JWKS cache, rate-limit, preview tokens |
| Elasticsearch cluster | Platform team | Full-text search for pages and articles |
| ELK/EFK stack | Platform team | Logging |
| Prometheus + Grafana | Platform team | Metrics + alerting |
| Trivy/Grype scanning | DevOps | Container security |
| k6 load test environment | DevOps | SLO validation |