Service Management ๐งฑยถ
This page describes how shared services are declared, discovered, ordered, loaded, and injected.
What Is a Service? ๐งฉยถ
A service is a reusable dependency that an application enables via services.<service_key>.
Examples:
web-svc-cdnprovides the primary service keycdnweb-app-keycloakprovidesoidcsvc-db-mariadbprovidesmariadb
Each service entry lives in the provider roleโs own meta/services.yml, where the file root IS the services map keyed by <entity_name> (no compose: and no services: wrapper).
See layout.md for the full per-role meta layout.
Role-Local Service Metadata ๐ท๏ธยถ
Service providers are self-describing. The provider role owns:
enabledsharedoptional
providesoptional
canonical
Example (file root of meta/services.yml):
keycloak:
enabled: false
shared: true
provides: oidc
Canonical aliases are also role-local:
cdn:
enabled: false
shared: true
css:
enabled: true
shared: true
canonical: cdn
javascript:
enabled: true
shared: true
canonical: cdn
Rules:
The primary service entry is the role entity name returned by
get_entity_name.provides:is only used when the public service name differs from the entity name.canonical:is only used on alias entries that resolve back to the primary service key.frontendvs.backendis derived from the role name prefix, not stored in config.
Service Discovery ๐ยถ
Service discovery is built from role configs, not from a central registry file.
Primary implementation files:
The discovery layer:
scans role configs from roles/
discovers provider entries from
servicesderives deploy type and loader bucket from the role name
resolves
provides:andcanonical:validates and applies
run_after:frommeta/services.yml.<primary_entity>
Load Order ๐งฎยถ
main.yml is the single loader entry point for all shared services.
It runs from 01_constructor.yml before the normal application stage.
Global bucket order:
universalworkstationserverweb-svcweb-app
Within the same bucket, ordering is refined by run_after: declared on the provider roleโs primary entity in meta/services.yml (i.e. services.<primary_entity>.run_after).
Rules:
run_after:entries are role names, not service keys.Cross-type
run_after:is invalid and fails hard.Later-bucket dependencies are invalid and fail hard.
Cross-type service dependencies do not need
run_after:because constructor-stage loading already brings backend services up before normal app deployment.
Loading vs Injection ๐ยถ
Service loading and frontend injection are separate mechanisms.
Loading ๐ฅยถ
Loading decides whether the provider role is deployed at all.
queries the ordered discovered service list
checks
lookup('service', service_key).requiredskips roles already protected by
run_once_*loads services through load/app.yml
Frontend service probe/load helper:
Injection ๐ยถ
Injection decides whether a deployed app gets extra nginx integration such as dashboard, logout, CSS, or JavaScript hooks.
This stays in:
Injection still reads the current appโs services.<feature>.enabled flags.
It does not load provider roles.
Lookup Plugins ๐ยถ
service ๐งทยถ
File: service.py
Examples:
lookup('service', 'matomo')
lookup('service', 'sso')
lookup('service', 'web-svc-cdn')
Returns:
idroleenabledsharedrequiredis True when some deployed app has the service with bothenabled: trueANDshared: true, directly or transitively through its own enabled service dependencies. This is the flag consumers should gate on when deciding whether to load, integrate with, or configure the service. A plainenabledflag is not enough on its own, because a service that is enabled but never shared with another app is not actually contractually required by anyone.
Behavior:
accepts either a service key or a provider role name
resolves aliases through
canonicalresolves provider roles through discovered primary service keys
computes
requiredtransitively from enabled shared services
service_registry ๐ยถ
File: service_registry.py
Examples:
query('service_registry') | first
query('service_registry', 'ordered') | first
Modes:
default: full discovered registry mapping
ordered: ordered primary service entries for the service loader
applications_current_play ๐งญยถ
File: applications_current_play.py
Builds the current-play application set including:
group-selected roles
transitive shared service dependencies
meta dependencies
Database Services ๐๏ธยถ
Relational databases are regular services:
svc-db-mariadbprovidesmariadbsvc-db-postgresprovidespostgres
Applications express database choice directly via the file root of meta/services.yml:
mariadb:
enabled: true
shared: true
or:
postgres:
enabled: true
shared: false
The lookup('database', ...) API is the convenience accessor for database connection values; it resolves the active direct database service from the role-local keys above.