Architecture¶
Configurator builds an Application Hub YAML file from Python profile classes.
The runtime is organized around five layers:
- registry: stores concrete profile classes by slug
- profile families: provide shared defaults and hooks for a class of apps
- CLI: resolves user input, loads profiles, applies overrides, and writes output
- models: Pydantic schema for the generated configuration
- IO/helpers: YAML serialization plus config map / manifest helpers
Main modules¶
configurator.mainThin module that exposes the CLI entrypoint by importingconfigurator.cli.commands.main.configurator.cli.commandsDefines thedump-configClick command and all supported options.configurator.plugins.loaderImports built-in profile packages and optionally imports external plugin modules from--profiles-dir.configurator.apps.registry.ProfileRegistryCentral in-memory registry keyed by profile slug.configurator.apps.base.BaseAppProfileBase class that assembles aProfilePydantic object.configurator.modelsPydantic models for profiles, volumes, role bindings, manifests, config maps, and the finalConfig.configurator.io.yaml_writerWrites the final YAML usingruamel.yaml.
Data flow¶
dump-configstarts inconfigurator.cli.commands.main.- Built-in profiles are registered by importing:
configurator.apps.coderconfigurator.apps.remote_desktopconfigurator.apps.jupyterlab- External profile folders passed through
--profiles-dirare added tosys.path, then every top-level module/package in those folders is imported. - CLI arguments are parsed into:
- enabled profile slugs
- groups
- node selector overrides
- attribute overrides
- Each selected profile class is looked up in
profile_registry, instantiated, and optionally overridden. BaseAppProfile.build()converts the profile instance into aconfigurator.models.Profile.- All built profiles are wrapped in
configurator.models.Config. configurator.io.yaml_writer.write_yaml()serializes the config to disk.
Registration model¶
Profiles do not use auto-discovery inside the repository.
Registration happens explicitly at import time in package __init__.py files, for example:
This keeps profile availability deterministic and makes test setup straightforward.
Profile lifecycle¶
BaseAppProfile exposes hooks that subclasses override to contribute data:
get_default_volumes()get_manifests()get_role_bindings()get_config_maps()get_image_pull_secrets()get_env_config_maps()get_env_secrets()get_secret_mounts()get_pod_env_vars()get_extra_resource_limits()
Concrete profiles should define identity and defaults through class attributes such as:
slugdisplay_namedescriptionimagecpu_guarantee,cpu_limitmem_guarantee,mem_limitdefault_url- optional file-backed paths like
bashrc_pathandinit_script_path
build() is implemented once in BaseAppProfile and should normally not be overridden.
Built-in profile families¶
configurator.apps.coderRegisterscoder_app,gpu_coder_app, andcoder_dask_gateway_app.configurator.apps.remote_desktopRegistersremote_desktop,qgis_remote_desktop,panoply_remote_desktop, andsnap_remote_desktop.configurator.apps.jupyterlabRegistersjupyterlab_small.
Each family packages its own defaults, supporting config maps, and manifests alongside the Python classes.
Deployment-specific profiles such as training or product add-ons should live in external plugin folders loaded with --profiles-dir.
Overrides¶
Overrides are handled in two steps:
configurator.overrides.parserParses--overrideand--node-selectorCLI values.configurator.overrides.applyValidates and applies attribute overrides to instantiated profiles.
Examples:
coder_app:cpu_limit=4gpu_coder_app:image=ghcr.io/example/gpu:latestgpu_coder_app:nodepool=gpu
YAML output¶
The final config is emitted through ruamel.yaml.
Important behavior:
- manifests are written as structured YAML objects, not YAML-encoded strings
- multi-line config map content is preserved as literal blocks where applicable
- output is based on the Pydantic model graph, not ad hoc dict assembly