Service infrastructure¶
This section describes what is in the SODa SCS Manager deployment and how the services are wired.
For a C4-style Mermaid map (Compose → containers → modules → integrations → configs/scripts), see Architecture overview. Naming scheme: Naming vocabulary. Migration (aliases, Manager settings, env, code): Naming migration plan.
Overview¶
The deployment is built from multiple Docker Compose files merged via the COMPOSE_FILE environment variable (see example-env). It includes:
- A main stack (
docker-compose.yml): Traefik reverse proxy, shared MariaDB, Portainer, phpMyAdmin, and the Nextcloud FUSE mounter sidecar (nextcloud-mounter). - Submodule stacks: SCS Manager (Drupal), Nextcloud, JupyterHub, Keycloak, OpenGDB, WebProtégé, the project website stack, and the SCS Health dashboard.
All services that serve HTTP or need to be reached by Traefik attach to the same Docker network, reverse-proxy. The shared MariaDB instance is used by Keycloak, SCS Manager, Nextcloud, and the project website; each has its own database and user created by pre-install scripts.
Keycloak is the central identity provider (IdP): SCS Manager, Nextcloud, JupyterHub, and WebProtégé use OpenID Connect to authenticate users against Keycloak.
Architecture diagram¶
flowchart LR
subgraph internet [Internet]
Users[Users]
end
subgraph traefik [Traefik]
Web[web :80]
WebSecure[websecure :443]
end
subgraph services [Services]
Keycloak[Keycloak]
SCSManager[SCS Manager]
Nextcloud[Nextcloud]
JupyterHub[JupyterHub]
OpenGDB[OpenGDB]
WebProtege[WebProtégé]
ProjectPage[Project Website]
end
subgraph shared [Shared]
DB[(MariaDB)]
end
Users --> Web
Users --> WebSecure
Web --> WebSecure
WebSecure --> Keycloak
WebSecure --> SCSManager
WebSecure --> Nextcloud
WebSecure --> JupyterHub
WebSecure --> OpenGDB
WebSecure --> WebProtege
WebSecure --> ProjectPage
Keycloak --> DB
SCSManager --> DB
Nextcloud --> DB
ProjectPage --> DB
SCSManager -.->|OIDC| Keycloak
Nextcloud -.->|OIDC| Keycloak
JupyterHub -.->|OIDC| Keycloak
WebProtege -.->|OIDC| Keycloak
Main stack (docker-compose.yml)¶
- core--traefik--edge (alias
scs--reverse-proxy) — Traefik: HTTP/HTTPS entrypoints, TLS (e.g. Let’s Encrypt), middlewares (HTTPS redirect, Nextcloud headers, rate limiting). Routes byHost(...)using Docker labels. The Docker network remainsreverse-proxy. - core--mariadb--db (alias
scs--database) — MariaDB. Single instance; databases and users for Keycloak, SCS Manager, Nextcloud, and project website are created by pre-install scripts. - core--portainer--app (alias
scs--portainer) — Portainer CE for container management; used by SCS Manager for WissKI stack operations. - dbms--phpmyadmin--app (alias
scs--phpmyadmin) — phpMyAdmin for database access (optional). - dbms--forwardauth--proxy (alias
scs--forward-auth) — Keycloak forward-auth for phpMyAdmin SSO. - nextcloud-mounter — rclone
rcdsidecar for per-user Nextcloud WebDAV/FUSE mounts. See Nextcloud mount sidecar.
Compose file aggregation¶
The COMPOSE_FILE in example-env lists, in order:
- Main:
docker-compose.yml - Stacks:
scs-manager-stack/docker-compose.yml,scs-nextcloud-stack/docker-compose.yml,jupyterhub/docker-compose.yml,keycloak/docker-compose.yml,open_gdb/docker-compose.yml,webprotege/docker-compose.yml,scs-project-website-stack/docker-compose.yml,scs-health/docker-compose.yml - Overrides: the corresponding
docker-compose.override.ymlfiles for each stack.
Override files are copied from 00_custom_configs/<stack>/docker/ to each stack directory by start.sh (see Pre-start steps).
Stacks and wiring¶
Traefik¶
- Listens on ports 80 (HTTP) and 443 (HTTPS).
- Uses Let’s Encrypt for TLS (certificate resolver
le). - Middlewares:
rate-limit,rate-limit-high,rate-limit-strict, and redirects (e.g. SCS Manager second domain). - Drupal-facing services (SCS Manager, WissKI instances via Portainer, project website) use
rate-limit-high@dockeron external routers with an internal bypass router for Docker CIDRs. See WissKI stack — bot protection. - Each service that should be reachable gets Traefik labels for router rule, entrypoints, TLS, and service port.
Database¶
- core--mariadb--db (MariaDB, alias
scs--database) is started first bystart.sh. Pre-install scripts create: - Keycloak DB and user
- SCS Manager DB and user
- Nextcloud DB and user
- Project website DB and user
Keycloak¶
- Realm is generated from
00_custom_configs/keycloak/templates/realm/scs-realm.json.tpl(pre-install) and imported at startup. - Clients: JupyterHub (by
JUPYTERHUB_DOMAIN), Nextcloud (byNEXTCLOUD_NEXTCLOUD_DOMAIN), SCS Manager (bySCS_MANAGER_DOMAIN), phpMyAdmin/DBMS (bySCS_DBMS_DOMAIN), WebProtégé, DIDMOS. Each has a client secret from.env. - Uses the shared MariaDB for persistence.
- Exposed via Traefik on the Keycloak domain (e.g.
auth.scs.localhost).
SCS Manager¶
- Drupal application (and optionally Varnish in front). Image and mode from env (e.g.
scs-manager-image-development:latest). - OpenID Connect client config is generated from
00_custom_configs/scs-manager-stack/openid/openid_connect.client.scs_sso.yml.tplintoscs-manager-stack/custom_configs/openid_connect.client.scs_sso.ymlduring pre-install. Drupal uses this for Keycloak SSO. - Drupal settings (e.g. OpenID behaviour, reverse proxy) come from
00_custom_configs/scs-manager-stack/drupal/(e.g.openid-connect.settings.php,reverse-proxy.settings.php). - Nginx / clean URLs:
scs-manager-stack/configs/nginx/drupal.confis mounted intoscs-manager--drupalso Drupal generates paths without/index.php. See SCS Manager clean URLs if links still showindex.php. - PHP / OPcache: Production must not mount
scs-manager-stack/configs/php/zz-xdebug-debug.ini(that overlay disables OPcache and enables Xdebug). For local debugging, add the volume mount back in a dev-only compose override; see PHP debugging (Xdebug). - User lifecycle (create/delete): SCS Manager user create and delete routines — Keycloak registration, OIDC linking, Nextcloud credential attributes, and
hook_user_deletecleanup. - Nextcloud mounts: On
nextcloud-mounter-net; owns mount lifecycle viaNextcloudMountManager/ Drushscs:nextcloud-*. See Nextcloud mount sidecar. - WissKI stacks are deployed via Portainer from
wisski-base-stack. Drupal reverse proxy addresses in SCS Manager settings (autoby default) becomeDRUPAL_PROXY_ADDRESSESon the stack; the wisski-base-image syncs trusted proxy CIDRs into each instancesettings.phpon boot. - Varnish VCL is generated from
00_custom_configs/scs-manager-stack/varnish/default.vcl.tplduring pre-install. - Connects to shared MariaDB (own database).
Nextcloud¶
- Nextcloud FPM, reverse proxy (nginx), Redis, OnlyOffice. Database and user created in pre-install.
- Post-installation hooks in
scs-nextcloud-stack/hooks/post-installation/install and configure apps (e.g. Social Login, OnlyOffice, Draw.io) and trusted domains. These must be run when the stack is up (see Post-configuration).
JupyterHub¶
- Generic OAuthenticator pointing at Keycloak (client ID, secret, authorize/token/userinfo URLs from env).
- Group and GID handling in
jupyterhub/jupyterhub/jupyterhub_config.py:auth_state_groups_keyreadsgroupsandgidsfrom the OAuth userinfo; spawner usesgroup_map.jsonand gids for Linux group membership in the notebook container. - Spawner binds Keycloak project Team Folders only (
${NEXTCLOUD_MOUNTS_ROOT}/<user>/<project-label>→/home/jovyan/nextcloud/<project-label>,rslave, GID33). See Nextcloud mount sidecar. - Spawner image can include tools (e.g. wisski_py, OpenRefine). JupyterHub needs the Keycloak “groups” (and optionally “gids”) scope on its client for group-based access and spawner gids to work (see Post-configuration checklist).
WebProtégé¶
- Shared instance (
webprotege+webprotege-mongodb), not a container per SCS project. Imagewebprotege:scsis built from thewebprotege/submodule (00_custom_configs/webprotege/docker/docker-compose.override.yml). - Deltas vs protegeproject/webprotege (not a fork) live in
00_custom_configs/webprotege/patches/README.md: OIDC SSO, project REST (owner permissions, collaborators, trash), and the Docker build. - Users sign in with Keycloak OIDC (
preferred_username, sanitised to[a-z0-9_.-]). SCS Manager stores the ontology-project UUID on the Drupal project fieldwebprotegeProjectId. - Creating an SCS project calls
POST /data/projectswithAuthorization: apikey <key>and then grantsEDITto members (PUT /data/projects/{id}/collaborators/{user}). The project-page WebProtégé card deep-links to#projects/{uuid}/perspectives/69df8fa8-4f84-499e-9341-28eb5085c40b. The dashboard card stays on#projects/list. - API key (once per environment): rebuild/recreate
webprotegeafter Java changes, then generate a key for a user withCREATE_EMPTY_PROJECT:
docker compose up -d --force-recreate --build webprotege--webprotege--app
docker exec -it webprotege--webprotege--app java -jar /webprotege-cli.jar generate-api-key
Store the key in SCS Manager settings (WebProtégé → API key), not in git. Optional API base URL http://webprotege:8080 for Docker-internal REST (Today alias until soak; Target http://webprotege--webprotege--app:8080). Run drush updb for field webprotegeProjectId (update 11028) and rebuild caches. Then backfill existing SCS projects (owners + member EDIT):
scs-drush soda_scs_manager:backfill-webprotege-projects --dry-run --all
scs-drush soda_scs_manager:backfill-webprotege-projects --all
The same Drush command is the migration on scs-prime once that environment has the API key and update 11028.
OpenGDB¶
- RDF4J (
opengdb--rdf4j--db, aliasscs--rdf4j), AuthProxy (opengdb--authproxy--proxy, aliasscs--authproxy), OutProxy, nginx (opengdb--nginx--edge). Internal SPARQL from WissKI still uses the alias until cutover.
Project website (scs-project-website-stack)¶
- Drupal plus Varnish and Redis. Database and user created by pre-install; Varnish VCL is generated from a template.
- Note: The pre-install script lives in
01_scripts/scs-project-page/and references00_custom_configs/scs-project-website/varnish/default.vcl.tplin the script, but the template file is under00_custom_configs/scs-project-page/varnish/default.vcl.tpl. If the script fails, use the path underscs-project-page.start.shlists the script as01_scripts/scs-project-website/pre-install.bash; the actual directory isscs-project-page. These path/naming inconsistencies may need to be fixed in a separate change.
SCS Health (scs-health)¶
- Next.js health dashboard for operators: HTTPS checks against stack service domains and optional Docker container status via the host socket.
- Container
scs--health, submodule atscs-health/, Traefik hostSCS_HEALTH_DOMAIN. - Auth: local credentials (
SCS_HEALTH_AUTH_*) and optional Keycloak OIDC (SCS_HEALTH_OIDC_*). UI follows SODa corporate design (Inter, orange#e68500, menu-bar divider) aligned withkeycloak/keycloak/themes/scsandsoda_scs_manager_theme. See SCS Health dashboard.
WissKI stacks (per-instance, via Portainer)¶
WissKI is not merged into the root COMPOSE_FILE. Each catalogue instance is a separate stack (wisski-base-stack) managed by SCS Manager through Portainer.
- Infrastructure: WissKI stack — Traefik → Varnish → Nginx + PHP-FPM, networks, headers,
raw.*bypass - Nextcloud: Project Team Folder via sidecar (
NEXTCLOUD_MOUNT_MODE=external) — WissKI Nextcloud mount and sidecar - Drupal
settings.phpsnippets: configs/ — trusted hosts, private files, Redis, reverse proxy