What happened?
Description
When using !secret.yaml references in configuration.yaml (as documented on the MQTT configuration page), Z2M's write() function in lib/util/settings.ts correctly detects the reference and preserves it — but then immediately calls applyEnvironmentVariables(toWrite) after the secret-reference preservation logic. This stamps any matching ZIGBEE2MQTT_CONFIG_* environment variable value (as plain text) over the !secret reference before writing the file.
The result is that !secret.yaml references are silently overwritten with plain text values on every write cycle (device join, settings change, restart, etc.), making the secret reference feature effectively non-functional when env vars are in play.
Root cause
In lib/util/settings.ts, the write() function:
- Correctly detects
!secret.yaml references in the existing file and routes values to secret.yaml, preserving the reference in toWrite
- Then calls
applyEnvironmentVariables(toWrite) which overwrites those same keys with plain text from env vars
- Then writes
toWrite to configuration.yaml — with the plain text value, not the secret reference
// Step 1: secret reference preservation (correct)
for (const [ns, key] of [["mqtt", "server"], ["mqtt", "user"], ["mqtt", "password"], ...]) {
if (actual[ns]?.[key]) {
const ref = parseValueRef(actual[ns][key]);
if (ref) {
yaml.updateIfChanged(data.joinPath(ref.filename), ref.key, toWrite[ns][key]);
toWrite[ns][key] = actual[ns][key]; // preserves !secret reference
}
}
}
// Step 2: env vars applied AFTER, clobbering the reference (bug)
applyEnvironmentVariables(toWrite);
// Step 3: plain text password written to configuration.yaml
yaml.writeIfChanged(CONFIG_FILE_PATH, toWrite);
Suggested fix
applyEnvironmentVariables() should be called before the secret-reference preservation loop, not after. That way the resolved value gets routed to secret.yaml and the reference is preserved in configuration.yaml. Alternatively, the secret-reference preservation loop could be moved to run after applyEnvironmentVariables().
How to reproduce
This is particularly evident when using the Home Assistant addon, where docker-entrypoint.sh automatically injects ZIGBEE2MQTT_CONFIG_MQTT_PASSWORD (sourced from the Mosquitto broker service). Even with a correctly configured !secret.yaml mqtt_password reference in configuration.yaml, Z2M overwrites it with the plain text password on every startup.
Impact
Users cannot use !secret.yaml to keep credentials out of configuration.yaml when any matching ZIGBEE2MQTT_CONFIG_* env var is set, defeating the documented secret management feature.
What did you expect to happen?
I expected that !secret.yaml references in configuration.yaml would be preserved on write, even when ZIGBEE2MQTT_CONFIG_* environment variables are set. The secret reference should survive restart cycles without the plain text value being written back to configuration.yaml.
How to reproduce it (minimal and precise)
- Using the Home Assistant addon with Mosquitto broker also installed
- Leave the addon UI config mqtt section empty (server/user/password all blank)
- Add
password: '!secret.yaml mqtt_password' to configuration.yaml
- Add
mqtt_password: yourpassword to secret.yaml
- Start the addon
- Observe that
configuration.yaml has the plain text password written back in place of the !secret.yaml reference
Zigbee2MQTT version
latest (bug is present in current master — see lib/util/settings.ts)
Adapter firmware version
N/A - this is a code/configuration bug, not hardware-specific
Adapter
N/A - this is a code/configuration bug, not hardware-specific
Setup
Home Assistant addon (hassio-zigbee2mqtt) with Mosquitto broker addon
Device database.db entry
No response
Debug log
No response
Notes
No response
What happened?
Description
When using
!secret.yamlreferences inconfiguration.yaml(as documented on the MQTT configuration page), Z2M'swrite()function inlib/util/settings.tscorrectly detects the reference and preserves it — but then immediately callsapplyEnvironmentVariables(toWrite)after the secret-reference preservation logic. This stamps any matchingZIGBEE2MQTT_CONFIG_*environment variable value (as plain text) over the!secretreference before writing the file.The result is that
!secret.yamlreferences are silently overwritten with plain text values on every write cycle (device join, settings change, restart, etc.), making the secret reference feature effectively non-functional when env vars are in play.Root cause
In
lib/util/settings.ts, thewrite()function:!secret.yamlreferences in the existing file and routes values tosecret.yaml, preserving the reference intoWriteapplyEnvironmentVariables(toWrite)which overwrites those same keys with plain text from env varstoWritetoconfiguration.yaml— with the plain text value, not the secret referenceSuggested fix
applyEnvironmentVariables()should be called before the secret-reference preservation loop, not after. That way the resolved value gets routed tosecret.yamland the reference is preserved inconfiguration.yaml. Alternatively, the secret-reference preservation loop could be moved to run afterapplyEnvironmentVariables().How to reproduce
This is particularly evident when using the Home Assistant addon, where
docker-entrypoint.shautomatically injectsZIGBEE2MQTT_CONFIG_MQTT_PASSWORD(sourced from the Mosquitto broker service). Even with a correctly configured!secret.yaml mqtt_passwordreference inconfiguration.yaml, Z2M overwrites it with the plain text password on every startup.Impact
Users cannot use
!secret.yamlto keep credentials out ofconfiguration.yamlwhen any matchingZIGBEE2MQTT_CONFIG_*env var is set, defeating the documented secret management feature.What did you expect to happen?
I expected that
!secret.yamlreferences inconfiguration.yamlwould be preserved on write, even whenZIGBEE2MQTT_CONFIG_*environment variables are set. The secret reference should survive restart cycles without the plain text value being written back toconfiguration.yaml.How to reproduce it (minimal and precise)
password: '!secret.yaml mqtt_password'toconfiguration.yamlmqtt_password: yourpasswordtosecret.yamlconfiguration.yamlhas the plain text password written back in place of the!secret.yamlreferenceZigbee2MQTT version
latest (bug is present in current master — see lib/util/settings.ts)
Adapter firmware version
N/A - this is a code/configuration bug, not hardware-specific
Adapter
N/A - this is a code/configuration bug, not hardware-specific
Setup
Home Assistant addon (hassio-zigbee2mqtt) with Mosquitto broker addon
Device
database.dbentryNo response
Debug log
No response
Notes
No response