Profiles and Local Overrides

Use profiles and .devctl.override.yaml to select plugins and keep local adjustments out of shared config.

Sections

Terminology & Glossary
📖 Documentation
Navigation
6 sectionsv0.1
📄 Profiles and Local Overrides — glaze help profiles-guide
profiles-guide

Profiles and Local Overrides

Use profiles and .devctl.override.yaml to select plugins and keep local adjustments out of shared config.

Topicdevctlprofilesconfigurationoverridespluginsprofilesupplanpluginsprofile

Profiles let a repository define named ways to run devctl. A profile selects which plugins participate in the pipeline and can add environment overrides for those plugins. This is useful when a repository has several valid development modes: frontend-only, backend-only, full-stack, production-like, or a local debugging setup.

A profile is not a separate pipeline. It is a selection step before plugins are started. Once devctl has selected the active profile, the rest of the system behaves normally: selected plugins run config.mutate, build.run, prepare.run, validate.run, and launch.plan, and devctl supervises the services returned by the launch plan.

1. Defining profiles in .devctl.yaml

Profiles live in the shared project config under profiles:. Each profile names plugin IDs from the top-level plugins: list.

profile:
  active: development

profiles:
  development:
    display_name: Development
    description: Hot reload, verbose logs, local services
    plugins:
      - api
      - web
    env:
      LOG_LEVEL: debug

  backend:
    display_name: Backend Only
    description: API plus database, no frontend
    plugins:
      - api
      - database

plugins:
  - id: api
    path: python3
    args: [./plugins/api.py]
  - id: web
    path: python3
    args: [./plugins/web.py]
  - id: database
    path: python3
    args: [./plugins/database.py]

The plugins: field inside a profile is a list of plugin IDs. It does not control ordering. Ordering still comes from plugin priority, with ID as the stable tie-breaker.

2. Selecting a profile

Use --profile when you want an explicit one-off choice:

devctl up --profile backend
devctl plan --profile development
devctl plugins list --profile backend

Use profile.active when the repository should have a default profile:

profile:
  active: development

Selection precedence is:

1. --profile <name>
2. profile.active from .devctl.override.yaml
3. profile.active from .devctl.yaml
4. no profile: load all top-level plugins

The final case is the backward-compatible mode. Existing repositories with only a top-level plugins: list continue to load all plugins.

3. The explicit default profile

A profile named default is allowed, but it is not magic. It is active only when selected explicitly:

profile:
  active: default

profiles:
  default:
    plugins: [api, web]

or:

devctl up --profile default

If .devctl.yaml defines profiles.default but neither profile.active: default nor --profile default is provided, devctl loads all top-level plugins. The mere presence of a profile named default does not change old behavior.

4. Local overrides with .devctl.override.yaml

A local .devctl.override.yaml can add or adjust profiles without changing the shared project config. devctl loads .devctl.yaml first, then merges .devctl.override.yaml if it exists.

# .devctl.override.yaml
profile:
  active: local-debug

profiles:
  local-debug:
    display_name: Local Debug
    description: Backend plus local trace settings
    plugins:
      - api
      - database
    env:
      LOG_LEVEL: trace
      DEVCTL_TRACE: "1"

The expected workflow is that .devctl.override.yaml is local to one developer. Add it to .gitignore unless your team intentionally wants to commit a shared override template.

5. Merge rules

The override file is deliberately simple. It is the same schema as .devctl.yaml, but every field is optional.

Field shapeMerge rule
Scalars such as profile.active and strictnessOverride value wins when non-empty.
Maps such as profiles and envKeys merge; override keys win.
Profile recordsMerge field by field.
Profile plugins listReplaced when the override provides a non-empty list.
Top-level plugins listMerged by plugin id; existing IDs are patched, new IDs are appended.
Plugin args listReplaced when the override provides a non-empty list.
Plugin env mapMerged; override keys win.

This means a local override can adjust an existing profile's env without restating its plugin list:

profiles:
  development:
    env:
      LOG_LEVEL: trace

It can also add a local plugin and a profile that selects it:

profiles:
  tracing:
    plugins: [api, jaeger-local]

plugins:
  - id: jaeger-local
    path: ./plugins/devctl-jaeger-local

6. Profile env and plugin env

Profile env is applied to every selected plugin and overlays that plugin's configured env for that run.

profiles:
  backend:
    plugins: [api]
    env:
      LOG_LEVEL: debug

plugins:
  - id: api
    path: python3
    args: [./plugins/api.py]
    env:
      LOG_LEVEL: info
      API_ONLY: "1"

When backend is active, the api plugin receives LOG_LEVEL=debug and API_ONLY=1.

7. Inspecting profiles

List the profiles devctl sees after applying the local override:

devctl profiles list

Show the resolved active profile:

devctl profiles active

If no profile is selected, profiles active prints (none). That means devctl will load all top-level plugins.

8. Dynamic commands and profiles

Dynamic commands are discovered from the plugins selected by the active profile. If a plugin is excluded by --profile backend, dynamic commands from that plugin are not registered for that invocation.

This is intentional. A profile describes the active operating mode, and dynamic commands should come from the plugins participating in that mode.

Troubleshooting

ProblemCauseSolution
profile "x" not foundThe selected profile is not present after merging .devctl.yaml and .devctl.override.yaml.Run devctl profiles list and check spelling.
profile "x" references unknown plugin "y"The profile names a plugin ID that is not defined or discovered.Add the plugin to top-level plugins: or fix the ID.
profiles active prints (none)No --profile and no profile.active are set.This is valid backward-compatible all-plugins mode. Set profile.active if you want a default.
profiles.default is ignoreddefault is not implicit.Use profile.active: default or --profile default.
Local changes affect teammates.devctl.override.yaml was committed accidentally.Add .devctl.override.yaml to .gitignore unless the team wants it committed.

See Also

  • devctl help user-guide
  • devctl help scripting-guide
  • devctl help plugin-authoring
  • devctl profiles list
  • devctl profiles active