hermes - 💡(How to fix) Fix [Bug]: Docker image updates do not run config migration

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

  • clearly detect and warn that the persisted config.yaml is older/unversioned and requires migration before continuing.

Additional Logs / Traceback (optional)

Root Cause

Root Cause Analysis (optional)

Fix Action

Fix / Workaround

  1. Run the official Docker image with a persistent HERMES_HOME, for example a volume mounted at /opt/data.
  2. Let the container create or preserve $HERMES_HOME/config.yaml.
  3. Use a config from an older version, or one with no raw _config_version, or one containing a legacy setting that requires migration such as old custom_providers, flat stt.model, old display/tool-progress settings, old compression summary fields, or pre-opt-in plugin config.
  4. Update the deployment the Docker way: pull a newer nousresearch/hermes-agent image and recreate/restart the container.
  5. Observe that container boot runs docker/stage2-hook.sh, seeds missing files only, and runs bundled skills sync, but does not run migrate_config().
  6. Observe that hermes update inside the container is not a workaround: it exits early for Docker installs before reaching the migration step.
  7. For an unversioned raw config, run hermes config check or inspect check_config_version(): the merged default _config_version can make the config appear current even though the raw file has never been migrated.

Code Example

Not applicable. This is a source-level Docker packaging and update-path bug report.

---
RAW_BUFFERClick to expand / collapse

Bug Description

The official Docker image update path has no equivalent to the config migration step that runs during hermes update for non-Docker installs.

Docker users are expected to update by pulling/replacing the image, and hermes update intentionally exits early when the install method is docker. However, the Docker container boot hook only seeds missing files and syncs bundled skills. It does not run migrate_config() against the persisted $HERMES_HOME/config.yaml.

That means a persistent Docker volume can carry an old raw config schema across image updates. Runtime default merging hides some missing keys, but it does not perform schema rewrites such as moving legacy fields, cleaning obsolete settings, or bumping the on-disk config version.

Steps to Reproduce

  1. Run the official Docker image with a persistent HERMES_HOME, for example a volume mounted at /opt/data.
  2. Let the container create or preserve $HERMES_HOME/config.yaml.
  3. Use a config from an older version, or one with no raw _config_version, or one containing a legacy setting that requires migration such as old custom_providers, flat stt.model, old display/tool-progress settings, old compression summary fields, or pre-opt-in plugin config.
  4. Update the deployment the Docker way: pull a newer nousresearch/hermes-agent image and recreate/restart the container.
  5. Observe that container boot runs docker/stage2-hook.sh, seeds missing files only, and runs bundled skills sync, but does not run migrate_config().
  6. Observe that hermes update inside the container is not a workaround: it exits early for Docker installs before reaching the migration step.
  7. For an unversioned raw config, run hermes config check or inspect check_config_version(): the merged default _config_version can make the config appear current even though the raw file has never been migrated.

Expected Behavior

Docker image updates should preserve the same config-schema safety guarantees as non-Docker hermes update.

At minimum, when a new image starts against an existing persistent HERMES_HOME, Hermes should either:

  • run safe, non-interactive config migrations before starting the gateway, or
  • clearly detect and warn that the persisted config.yaml is older/unversioned and requires migration before continuing.

Actual Behavior

The Docker image update path refreshes the code but not necessarily the persisted config shape.

Current behavior from source inspection:

  • hermes update intentionally exits for Docker installs before the normal update implementation reaches config migration.
  • docker/stage2-hook.sh creates directories, writes .install_method=docker, seeds .env, config.yaml, and SOUL.md only if missing, and runs tools/skills_sync.py.
  • The Docker boot hook does not call migrate_config().
  • Gateway startup validates/warns about config structure but does not migrate it.
  • check_config_version() uses load_config(), and load_config() starts from DEFAULT_CONFIG, so a raw file with no _config_version can appear to already be at the latest config version.

Impact:

  • Missing new defaults are often hidden by runtime deep-merge, but the raw on-disk config stays stale.
  • Version-gated transformations may never run.
  • Old raw config shape can become semantically invalid for newer code.
  • Examples from current migrations include custom_providers -> providers, legacy flat stt.model -> provider-specific STT config, display/tool-progress config moves, compression summary config moves, plugin allow-list seeding, curator defaults/log dir seeding, and .env cleanup.
  • In at least one migration comment, the legacy stt.model shape is documented as capable of crashing faster-whisper with Invalid model size if not migrated.

Affected Component

Configuration (config.yaml, .env, hermes setup)

Messaging Platform (if gateway-related)

No response

Debug Report

Not applicable. This is a source-level Docker packaging and update-path bug report.

Operating System

Official Docker image / Linux container

Python Version

Docker image Python version; not central to the issue.

Hermes Version

Current main/source checkout as of 2026-05-30; deployment image observed: nousresearch/hermes-agent:v2026.5.28.

Additional Logs / Traceback (optional)

Root Cause Analysis (optional)

Relevant source paths in the current checkout:

  • hermes_cli/main.py: cmd_update checks detect_install_method(PROJECT_ROOT) == "docker" and exits before _cmd_update_impl() can run.
  • hermes_cli/main.py: the normal update flow later checks for missing env/config/version and calls migrate_config(interactive=..., quiet=False).
  • docker/stage2-hook.sh: boot hook creates $HERMES_HOME, fixes permissions, seeds .env, config.yaml, and SOUL.md only if missing, writes .install_method=docker, and runs tools/skills_sync.py. There is no config migration call.
  • hermes_cli/config.py: migrate_config() contains multiple version-gated rewrites and always attempts .env sanitization.
  • hermes_cli/config.py: check_config_version() calls load_config() and reads _config_version from the effective config.
  • hermes_cli/config.py: load_config() starts with DEFAULT_CONFIG and deep-merges user config, so a raw file with no _config_version inherits the latest default version in memory.
  • cli-config.yaml.example: the seeded template appears to omit raw _config_version.

The result is a Docker-specific migration gap: image replacement updates code, but the persisted config may remain old/unversioned, and the built-in migration detection can fail to notice unversioned raw files.

Proposed Fix (optional)

  1. Add a Docker-safe config migration step during container boot, before the gateway starts. It should run as the hermes user against the active $HERMES_HOME.
  2. Make config migration/version detection inspect the raw on-disk config.yaml, not the deep-merged effective config returned by load_config(). load_config() is correct for runtime defaults, but it is the wrong source of truth for deciding whether an on-disk schema migration is needed.
  3. Treat a missing raw _config_version as an unversioned/legacy config. It should not inherit the latest version from DEFAULT_CONFIG during migration checks.
  4. Run the Docker boot migration non-interactively, with no API-key prompts, and back up config.yaml / .env first because config migration can rewrite both files.
  5. Add an opt-out environment variable for operators who need controlled/manual migrations, for example HERMES_SKIP_CONFIG_MIGRATION=1.
  6. Add _config_version to newly seeded configs, including Docker-seeded config. This is a follow-up hygiene fix for fresh installs; it is not sufficient by itself because existing persistent Docker volumes and hand-written configs may already be unversioned.
  7. Document the Docker update flow separately from hermes update, including when config migration runs, how to opt out, and how persistent volumes are handled.

Are you willing to submit a PR for this?

  • I'd like to fix this myself and submit a PR

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix [Bug]: Docker image updates do not run config migration