Module Configuration
Defines a deployable application unit within a namespace. A module represents one logical application — which may consist of multiple containers — and declares its source, deployer type, services, lifecycle hooks, and runtime references.
Schema
apiVersion: strata.huybrechts.xyz/v1
kind: module
meta:
name: <module_name> # Required: ^[a-z][a-z0-9_-]*$
annotations:
description: <description>
labels:
version: "<version>"
category: <category> # proxy | database | cache | identity | application | ...
spec:
type: <deployer_type> # compose | helm | argocd | script
source: {} # Required: git-based or chart-based (see Source)
release_name: <name> # Optional: Helm release / ArgoCD app name (default: module name)
kubernetes_namespace: <ns> # Optional: K8s namespace (default: strata namespace name)
references: # Optional: keys resolved from environment stores
variables: []
secrets: []
features: []
services: [] # Optional: container / sub-chart definitions
lifecycle: {} # Optional: deployment lifecycle hooks
properties: # Optional: module-level metadata
endpoints: []
checks: []
mounts: []
configuration: {} # Optional: deployer-specific escape hatch
Source
Two mutually exclusive modes. Use git-based when the source is a repository; use chart-based for Helm chart registries.
Git-based source
source:
repository: platform-modules # PlatformName — from configuration repositories
source_path: services/traefik # relative path in repo
target_path: modules/traefik # optional: build output path
Chart-based source (Helm / ArgoCD)
source:
chart_name: authentik # chart name in the registry
chart_version: "2024.12.0" # optional: pin version (latest if omitted)
chart_repository: https://charts.goauthentik.io # registry URL or OCI reference
# OCI example: oci://ghcr.io/org/helm-charts
Deployer Types
Type |
Description |
Source mode |
|---|---|---|
|
Docker Compose — deployed via SSH |
git-based |
|
Helm chart — deployed via |
git-based or chart-based |
|
ArgoCD Application — managed in gitops repo |
chart-based or git-based |
|
Custom deployment script |
git-based |
Services
The services list defines the containers (compose) or sub-chart components (helm) that make up the module. When absent, the module uses the single-service properties shape for backward compatibility.
Tip: Service images defined here are defaults. Use environment overrides to pin different image versions per environment without modifying the module file.
services:
- name: server # service identifier
image: ghcr.io/goauthentik/server:2024.12 # container image (omit for helm charts)
command: [server] # optional: override entrypoint
restart: unless-stopped # compose only
ports:
- "9000:9000" # compose only; ignored by helm/argocd
environment:
- key: AUTHENTIK_SECRET_KEY
secret: AUTHENTIK_SECRET_KEY # resolved from references.secrets
- key: TZ
value: Europe/Brussels # literal value
- key: APP_VERSION
var: APP_VERSION # resolved from references.variables
- key: ENABLE_METRICS
feature: ENABLE_METRICS # resolved to "true"/"false"
mounts:
- name: pgdata
target_path: /var/lib/postgresql/data
volume_ref: data # compose: references topology volume
depends_on: [postgresql, redis] # intra-module: builder prefixes names
# depends_on: ["@mod_auth/server"] # cross-module: @module/service syntax
healthcheck:
type: http
target: http://localhost:9000/-/health/live/
interval: "30s"
timeout: "5s"
retries: 3
configuration: {} # deployer-specific overrides merged verbatim
Environment variable sources
Each environment entry sets exactly one of:
Field |
Description |
Artifact output |
|---|---|---|
|
Literal string |
Written directly |
|
Key from |
Resolved at build time |
|
Key from |
|
|
Key from |
Resolved to |
Service naming in compose output
The builder prefixes service names with the module name to avoid collisions across modules:
Module: authentik + service: redis → compose key: authentik-redis
Module: caddy + service: caddy → compose key: caddy (prefix skipped when names match)
depends_on entries are rewritten automatically to the prefixed form.
Mounts
mounts:
# Compose: reference a named volume from workspace topology
- name: data
target_path: /var/lib/postgresql/data
volume_ref: data # references WorkspaceVolumeModel.name
# Compose: bind mount
- name: config
type: bind
source_path: ./Caddyfile
target_path: /etc/caddy/Caddyfile
# Helm / K8s: PersistentVolumeClaim
- name: pgdata
target_path: /var/lib/postgresql/data
storage_class: standard
storage_size: 10Gi
access_mode: ReadWriteOnce # optional, defaults to ReadWriteOnce
volume_ref and storage_class are mutually exclusive.
References
Declares the variable, secret, and feature keys this module requires from the environment configuration. Actual values and store backends are defined at environment/workspace level.
references:
variables:
- SUBDOMAIN_AUTH
- DOMAIN_PRIMARY
secrets:
- AUTHENTIK_SECRET_KEY
- AUTHENTIK_DB_PASSWORD
features:
- ENABLE_METRICS
Lifecycle Hooks
Optional hooks executed at specific deployment phases.
lifecycle:
service_start_before:
scripts: ["scripts/pre-start.sh"]
service_start_after:
scripts: ["scripts/post-start.sh"]
deploy_provision:
scripts: ["scripts/provision.sh"]
deploy_configure:
scripts: ["scripts/configure.sh"]
deploy_health:
scripts: ["scripts/healthcheck.sh"]
deploy_destroy_before:
scripts: ["scripts/pre-destroy.sh"]
deploy_destroy_after:
scripts: ["scripts/post-destroy.sh"]
Properties
Module-level metadata — not per-service. Endpoints document external-facing access points; these are informational and used by the platform artifact.
properties:
endpoints:
- name: web
label: Authentik SSO
url: https://auth.example.com
port: 9443
type: https
protocol: tcp
checks:
- name: liveness
type: http
target: http://localhost:9000/-/health/live/
interval: "30s"
timeout: "5s"
retries: 3
Examples
Multi-container compose module (Authentik)
apiVersion: strata.huybrechts.xyz/v1
kind: module
meta:
name: authentik
labels:
category: identity
spec:
type: compose
source:
repository: haven-modules
source_path: services/authentik
references:
variables: [SUBDOMAIN_AUTH, DOMAIN_PRIMARY]
secrets: [AUTHENTIK_SECRET_KEY, AUTHENTIK_DB_PASSWORD]
services:
- name: server
image: ghcr.io/goauthentik/server:2024.12
ports: ["9000:9000"]
environment:
- key: AUTHENTIK_SECRET_KEY
secret: AUTHENTIK_SECRET_KEY
- key: AUTHENTIK_POSTGRESQL__HOST
value: authentik-postgresql
depends_on: [postgresql, redis]
healthcheck:
type: http
target: http://localhost:9000/-/health/live/
interval: "30s"
timeout: "5s"
retries: 3
restart: unless-stopped
- name: worker
image: ghcr.io/goauthentik/server:2024.12
command: [worker]
environment:
- key: AUTHENTIK_SECRET_KEY
secret: AUTHENTIK_SECRET_KEY
depends_on: [postgresql, redis]
restart: unless-stopped
- name: postgresql
image: postgres:16-alpine
environment:
- key: POSTGRES_PASSWORD
secret: AUTHENTIK_DB_PASSWORD
mounts:
- name: pgdata
target_path: /var/lib/postgresql/data
volume_ref: data
restart: unless-stopped
- name: redis
image: redis:alpine
command: ["--save", "60", "1"]
restart: unless-stopped
properties:
endpoints:
- name: web
label: Authentik SSO
url: https://${SUBDOMAIN_AUTH}.${DOMAIN_PRIMARY}
port: 9443
Single-container compose module (Caddy)
apiVersion: strata.huybrechts.xyz/v1
kind: module
meta:
name: caddy
labels:
category: proxy
spec:
type: compose
source:
repository: haven-modules
source_path: services/caddy
references:
variables: [DOMAIN_PRIMARY]
services:
- name: caddy
image: caddy:2-alpine
ports: ["80:80", "443:443"]
mounts:
- name: caddyfile
type: bind
source_path: ./Caddyfile
target_path: /etc/caddy/Caddyfile
- name: caddy_data
target_path: /data
volume_ref: data
restart: unless-stopped
Helm chart module
apiVersion: strata.huybrechts.xyz/v1
kind: module
meta:
name: traefik
labels:
category: proxy
spec:
type: helm
source:
chart_name: traefik
chart_version: "26.0.0"
chart_repository: https://traefik.github.io/charts
kubernetes_namespace: ingress
services:
- name: traefik
configuration:
ingressClass:
enabled: true
ports:
web:
port: 80
Module Categories
Category |
Purpose |
Examples |
|---|---|---|
|
Reverse proxies/load balancers |
Traefik, Caddy, Nginx |
|
Authentication/SSO |
Authentik, Keycloak |
|
Data storage |
PostgreSQL, MySQL, MongoDB |
|
Caching |
Redis, Memcached |
|
Message queues |
RabbitMQ, Kafka, NATS |
|
Business apps |
APIs, web apps, services |
|
Observability |
Prometheus, Grafana, Loki |
|
Security services |
Vaultwarden, Infisical, Vault |
|
Storage solutions |
MinIO, GlusterFS |
Namespace Integration
Modules are referenced from a namespace by name and file path. The order of modules in the namespace list controls deployment sequence.
# namespace.yaml
spec:
modules:
- name: caddy # deployed first
file: modules/mod-caddy.yaml
- name: authentik # deployed second
file: modules/mod-authentik.yaml
- name: vaultwarden # deployed third
file: modules/mod-vaultwarden.yaml
Build Output
For type: compose, the builder merges all modules in a namespace into one docker-compose.yml:
.strata/build/<namespace>/docker-compose.yml
For type: helm, the builder produces per-module values:
.strata/build/<namespace>/<module>/values.yaml
.strata/build/<namespace>/<module>/Chart.yaml
Validation
Name:
^[a-z][a-z0-9_-]*$, max 64 charactersService names must be unique within the module
environmententries must set exactly one ofvalue,var,secret,featurevolume_refandstorage_classare mutually exclusive on mountsstorage_sizeis required whenstorage_classis setsource: either git-based (repository+source_path) or chart-based (chart_repository+chart_name), not both
Troubleshooting
Duplicate service names: Each service in a module must have a unique name.
Environment var missing source: Every environment entry needs exactly one of value, var, secret, feature.
Mount conflict: volume_ref and storage_class cannot both be set on the same mount.
Source mode conflict: Do not mix repository/source_path with chart_repository/chart_name.
Secret not resolved: Ensure the key is listed in references.secrets and the environment store provides it.
Service not found in depends_on: depends_on entries must reference service names within the same module (short names — builder handles prefixing).
Schema
apiVersion: strata.huybrechts.xyz/v1
kind: module
meta:
name: <module_name> # Required: ^[a-z][a-z0-9_]*$
annotations:
description: <description>
labels:
version: "<version>"
category: <category> # proxy, database, cache, etc.
spec:
lifecycle: # Optional: module-specific hooks
bootstrap: {} # Before module deployment
provision: {} # During module provisioning
configure: {} # After module deployment
health: {} # After configuration
protect: {} # After successful deployment
destroy: {} # During module teardown
properties:
source: # Required: deployment source
type: <source_type> # local, gitops, image, script
repository: <repository>
reference: <reference>
source_path: <source_path>
deploy_path: <deploy_path>
Source Types
Local - Workspace configuration:
source:
type: local
repository: /
reference: /
source_path: services/traefik
deploy_path: modules/traefik
Use for: workspace services, custom apps, infrastructure services
GitOps - External Git repository:
source:
type: gitops
repository: https://github.com/org/repo.git
reference: main
source_path: deployments/app
deploy_path: modules/app
Use for: shared configs, external services, multi-workspace deployments
Image - Container image:
source:
type: image
repository: docker.io/traefik/traefik
reference: v2.10
source_path: /
deploy_path: modules/traefik
Use for: pre-packaged apps, third-party services, standard containers
Script - Custom deployment script:
source:
type: script
repository: /
reference: v1.0.0
source_path: scripts/deploy-custom.sh
deploy_path: modules/custom
Use for: complex deployment logic, custom procedures, legacy apps
Examples
Traefik Proxy:
meta:
name: traefik
labels:
version: "1.0.0"
category: proxy
spec:
properties:
source:
type: local
repository: /
reference: /
source_path: services/traefik
deploy_path: modules/traefik
PostgreSQL with Lifecycle:
meta:
name: postgres
labels:
version: "15.0.0"
category: database
spec:
lifecycle:
bootstrap:
scripts:
- file: scripts/postgres/prepare-storage.sh
configure:
scripts:
- file: scripts/postgres/init-db.sh
- file: scripts/postgres/run-migrations.sh
health:
scripts:
- file: scripts/postgres/health-check.sh
protect:
scripts:
- file: scripts/postgres/backup.sh
properties:
source:
type: image
repository: docker.io/library/postgres
reference: "15-alpine"
source_path: /
deploy_path: modules/postgres
GitOps Application:
meta:
name: api_service
labels:
version: "2.3.1"
category: application
spec:
lifecycle:
configure:
scripts:
- file: scripts/api/setup-env.sh
health:
scripts:
- file: scripts/api/smoke-test.sh
properties:
source:
type: gitops
repository: https://github.com/org/api-service.git
reference: v2.3.1
source_path: deploy/kubernetes
deploy_path: modules/api
Container Image:
meta:
name: redis
labels:
version: "7.0.0"
category: cache
spec:
properties:
source:
type: image
repository: docker.io/library/redis
reference: "7.0-alpine"
source_path: /
deploy_path: modules/redis
Module Categories
Category |
Purpose |
Examples |
|---|---|---|
|
Reverse proxies/load balancers |
Traefik, Nginx, HAProxy |
|
Data storage |
PostgreSQL, MySQL, MongoDB |
|
Caching |
Redis, Memcached |
|
Message queues |
RabbitMQ, Kafka, NATS |
|
Business apps |
APIs, web apps, services |
|
Observability |
Prometheus, Grafana, Loki |
|
Security services |
Vault, cert-manager |
|
Storage solutions |
MinIO, GlusterFS |
Path Resolution
source_path - Module source location:
Local: Relative to workspace root (
services/traefik)GitOps: Relative to repository root (
deploy/kubernetes)Image: Container path (typically
/)Script: Path to deployment script
deploy_path - Deployment artifacts location:
Relative to build directory
Platform generates deployment files here
Example:
modules/traefik→build/modules/traefik
Namespace Integration
# namespace.yaml
spec:
modules:
- name: traefik
file: config/modules/traefik.yaml
- name: postgres
file: config/modules/postgres.yaml
Module Patterns
Stateless: Image-based, no lifecycle hooks
Stateful: Lifecycle hooks for bootstrap, configure, protect
System: Local source, critical system services
Best Practices
Naming: Match service name (
traefik,postgres,redis)Version labels: Track versions for rollback
Categories: Organize modules consistently
Idempotent scripts: Safe to run multiple times
Error handling: Include in all lifecycle scripts
Source type: Choose based on deployment method
Paths: Consistent patterns across modules
Documentation: Clear descriptions in annotations
Script ordering: Number scripts for execution control
Dependencies: Document in annotations if needed
Validation
Platform validates:
Valid names (lowercase, alphanumeric, underscores)
Required fields (name, source type, paths)
Source type matches options
Script files exist and executable
Valid path formats
No conflicting module names
Troubleshooting
Module not found: Verify file path in namespace config, check file exists in config/modules/, ensure name matches reference
Source path invalid: Check path exists (local/script), verify URL (gitops), confirm image exists (image), validate format
Lifecycle script failed: Check executable permissions, verify path, review output, ensure dependencies available
Deploy path conflicts: Ensure unique deploy_path per module, check for overlapping directories
Version mismatch: Verify image tag exists, check Git reference exists, confirm version label accuracy
Dependencies
Control module order in namespace:
spec:
modules:
- name: database # First
- name: cache # Second
- name: backend # Third (depends on database + cache)
- name: frontend # Last (depends on backend)
Document external dependencies in annotations.