All platform configuration lives under /opt/platform/. Each subdirectory is an independent Docker Compose stack.
Secrets: passwords, tokens, and keys are stored in
.envfiles (not committed to git). Reference them by variable name only — never paste values into the wiki.
/opt/platform/
├── traefik/ Edge proxy, TLS, middleware definitions
├── keycloak/ Keycloak + oauth2-proxy
├── postgres/ Shared PostgreSQL
├── wiki/ Wiki.js
├── gitea/ Gitea Git hosting
├── monitoring/ Prometheus + Grafana + exporters
├── dashboard/ Dashy + logged-out page
├── auth-bounce/ 401 redirect helper
├── api/ Health-check API
├── web/ Static website (remico.eu)
├── analytics/ Umami web analytics + SSO bridge
├── dns/ DNS notes and API examples
└── security/ UFW script, fail2ban (optional)
| Service | File / Path | Controls |
|---|---|---|
| Traefik | /opt/platform/traefik/docker-compose.yml |
Entrypoints, ACME, Docker provider, middleware labels |
| Traefik | /opt/platform/traefik/acme.json |
Let's Encrypt certificates (mode 600, owned by root) |
| Keycloak | /opt/platform/keycloak/docker-compose.yml |
KC env (DB, hostname, proxy, logout SPI), oauth2-proxy env |
| Keycloak | /opt/platform/keycloak/.env |
Optional extra env vars |
| oauth2-proxy | (same compose as Keycloak) | OAUTH2_PROXY_* — client ID/secret, cookie domain, backend logout URL |
| PostgreSQL | /opt/platform/postgres/docker-compose.yml |
Container definition |
| PostgreSQL | /opt/platform/postgres/.env |
POSTGRES_USER, POSTGRES_PASSWORD |
| PostgreSQL | /opt/platform/postgres/data/ |
Database files (volume mount) |
| Wiki.js | /opt/platform/wiki/docker-compose.yml |
Container, Traefik labels, DB connection |
| Wiki.js | /opt/platform/wiki/.env |
WIKI_DB_PASS, WIKI_ADMIN_EMAIL, WIKI_ADMIN_PASS, OIDC_CLIENT_SECRET |
| Gitea | /opt/platform/gitea/docker-compose.yml |
Container, GITEA__* app settings, Traefik labels |
| Gitea | /opt/platform/gitea/.env |
GITEA_DB_PASS, GITEA_SECRET_KEY |
| Gitea | Docker volume gitea-data |
Repositories, avatars, sessions (/var/lib/gitea) |
| Monitoring | /opt/platform/monitoring/docker-compose.yml |
Prometheus, Grafana, exporters |
| Monitoring | /opt/platform/monitoring/prometheus.yml |
Scrape targets and intervals |
| Monitoring | /opt/platform/monitoring/.env |
Optional env vars |
| Grafana | (in monitoring compose) | GF_* env vars — auth proxy, auto Admin role |
| Dashboard | /opt/platform/dashboard/docker-compose.yaml |
Dashy + logged-out nginx |
| Dashboard | /opt/platform/dashboard/dashy/conf.yml |
Dashboard links and layout |
| Dashboard | /opt/platform/dashboard/static/logged-out/ |
Post-logout HTML page |
| auth-bounce | /opt/platform/auth-bounce/docker-compose.yml |
Traefik routing |
| auth-bounce | /opt/platform/auth-bounce/bounce.py |
Redirect logic (auth.remico.eu/oauth2/start) |
| auth-bounce | /opt/platform/auth-bounce/Dockerfile |
Python + nginx image build |
| API | /opt/platform/api/docker-compose.yml |
Traefik labels |
| API | /opt/platform/api/nginx.conf |
/health endpoint config |
| Web | /opt/platform/web/docker-compose.yml |
Traefik labels for remico.eu / www |
| Web | /opt/platform/web/html/ |
Static HTML/CSS assets |
| Umami | /opt/platform/analytics/docker-compose.yml |
Umami + umami-sso containers, Traefik labels |
| Umami | /opt/platform/analytics/.env |
UMAMI_DB_*, UMAMI_APP_SECRET, UMAMI_ADMIN_* |
| Umami | /opt/platform/analytics/setup-analytics.sh |
Creates umami DB/user on shared postgres |
| Umami | /opt/platform/analytics/sso-bridge/ |
Keycloak → Umami session bootstrap |
| Umami | /opt/platform/web/html/js/analytics.js |
Public site tracker (WEBSITE_ID) |
| DNS | /opt/platform/dns/README.md |
DNS zone notes and Hetzner API examples |
| Security | /opt/platform/security/setup-ufw.sh |
Host firewall rules |
| Security | /opt/platform/security/fail2ban/ |
Optional SSH jail (compose + jail.local) |
Services expose themselves to Traefik via Docker labels:
labels:
- traefik.enable=true
- traefik.http.routers.<name>.rule=Host(`subdomain.remico.eu`)
- traefik.http.routers.<name>.entrypoints=websecure
- traefik.http.routers.<name>.tls.certresolver=le
- traefik.http.routers.<name>.service=<name>
- traefik.http.services.<name>.loadbalancer.server.port=<port>
# Optional SSO:
- traefik.http.routers.<name>.middlewares=oauth-errors,oauth-auth
Global middleware definitions live on the traefik container itself:
| Middleware | Type | Purpose |
|---|---|---|
redirect-to-https |
redirectScheme | HTTP → HTTPS |
oauth-auth |
forwardAuth | Check session via oauth2-proxy:4180 |
oauth-errors |
errors | 401/403 → auth-bounce |
| Network | Created by | Used by |
|---|---|---|
frontend |
Manual (docker network create) |
Traefik, all public services, oauth2-proxy |
backend |
Manual | postgres, keycloak, wiki, gitea, umami, postgres-exporter |
These networks are external in every compose file — compose will fail if they do not exist.
| Variable | Purpose |
|---|---|
OAUTH2_PROXY_OIDC_ISSUER_URL |
https://auth.remico.eu/realms/remico |
OAUTH2_PROXY_CLIENT_ID |
remico-web |
OAUTH2_PROXY_CLIENT_SECRET |
Must match Keycloak client secret |
OAUTH2_PROXY_REDIRECT_URL |
https://auth.remico.eu/oauth2/callback |
OAUTH2_PROXY_COOKIE_DOMAINS |
.remico.eu |
OAUTH2_PROXY_COOKIE_SECRET |
Random 32-byte base64 string |
OAUTH2_PROXY_BACKEND_LOGOUT_URL |
Keycloak logout with id_token_hint |
| Variable | Purpose |
|---|---|
KC_HOSTNAME |
auth.remico.eu |
KC_PROXY |
edge (behind Traefik) |
KC_DB_* |
PostgreSQL connection |
KC_SPI_LOGIN_PROTOCOL_OPENID_CONNECT_SUPPRESS_LOGOUT_CONFIRMATION_SCREEN |
Skip logout confirm for Gitea/Wiki |
GITEA__ prefix)| Variable | Purpose |
|---|---|
GITEA__server__ROOT_URL |
Public URL |
GITEA__service__REQUIRE_SIGNIN_VIEW |
Force login to view anything |
GITEA__service__ENABLE_INTERNAL_AUTHENTICATION |
false — SSO only |
GITEA__oauth2_client__ENABLE_AUTO_REGISTRATION |
Create user on first SSO login |
| Variable | Purpose |
|---|---|
DB_* |
PostgreSQL connection |
OIDC_CLIENT_SECRET |
Keycloak wikijs client secret |
/opt/platform/analytics/.env)| Variable | Purpose |
|---|---|
UMAMI_DB_USER / UMAMI_DB_PASSWORD |
PostgreSQL role for database umami on shared postgres |
UMAMI_APP_SECRET |
JWT signing secret (openssl rand -hex 32) |
UMAMI_ADMIN_USER / UMAMI_ADMIN_PASS |
Internal credentials for umami-sso bridge only |
DATABASE_URL |
Set in compose: postgresql://…@postgres:5432/umami |
Platform wiki pages are maintained as markdown in:
/opt/platform/wiki/content/
home.md
server/
overview.md
containers.md
authentication.md
commands.md
configuration.md
Deployed to Wiki.js via the internal seed tooling (not part of operator workflow).