Documentation
fsds/iot-emqx-production-hardening.md
EMQX Production Hardening Plan
Problem Summary
Your EMQX container is running correctly, but has two issues:
- Prometheus push gateway errors - EMQX tries to push metrics to
127.0.0.1:9091which doesn't exist - Insecure Erlang cookie - Using default cookie (security risk)
Root Cause: The Bicep module is out of sync with the Aspire orchestration code:
- Aspire code (line 590): Has
EMQX_PROMETHEUS__PUSH_GATEWAY_SERVER="" - Bicep module: Missing this env var, only has
EMQX_PROMETHEUS__ENABLE=true
Files to Modify
| File | Changes |
|---|---|
infra/emqx/emqx-containerapp.module.bicep |
Add missing env vars + Erlang cookie secret |
infra/emqx/emqx.tmpl.bicepparam |
Add Erlang cookie parameter binding |
infra/main.bicep |
Add Erlang cookie parameter + wire emqx module |
infra/main.parameters.json |
Add Erlang cookie env var mapping |
DynaplexInfrastructureExtensions.cs |
Add Erlang cookie parameter to Aspire |
Implementation Steps
Stage 1: Fix Prometheus Errors (Immediate)
File: projects/bbu-rfid/src/Acsis.Dynaplex.Projects.BbuRfid/infra/emqx/emqx-containerapp.module.bicep
Add after line 74 (EMQX_PROMETHEUS__ENABLE):
{
name: 'EMQX_PROMETHEUS__PUSH_GATEWAY_SERVER'
value: ''
}
Stage 2: Add Erlang Cookie Support
2a. Update Bicep Module
File: infra/emqx/emqx-containerapp.module.bicep
Add parameter:
@secure()
param emqx_erlang_cookie_value string
Add to secrets array:
{
name: 'emqx-node--cookie'
value: emqx_erlang_cookie_value
}
Add to env array:
{
name: 'EMQX_NODE__COOKIE'
secretRef: 'emqx-node--cookie'
}
2b. Update Bicep Parameters Template
File: infra/emqx/emqx.tmpl.bicepparam
Add:
param emqx_erlang_cookie_value = '{{ securedParameter "emqx_erlang_cookie" }}'
2c. Update main.bicep
File: infra/main.bicep
Add parameter (near other emqx params):
@secure()
param emqx_erlang_cookie string
Add emqx module call (if not already present - verify first):
module emqx 'emqx/emqx-containerapp.module.bicep' = {
name: 'emqx'
scope: rg
params: {
aspire_env_outputs_azure_container_apps_environment_default_domain: aspire_env.outputs.azure_container_apps_environment_default_domain
aspire_env_outputs_azure_container_apps_environment_id: aspire_env.outputs.azure_container_apps_environment_id
emqx_admin_password_value: emqx_admin_password
emqx_erlang_cookie_value: emqx_erlang_cookie
location: location
}
}
2d. Update Parameters JSON
File: infra/main.parameters.json
Add:
"emqx_erlang_cookie": {
"value": "${AZURE_EMQX_ERLANG_COOKIE}"
}
2e. Update Aspire Orchestration
File: strata/orchestration/src/Acsis.Dynaplex.Strata.Orchestration/DynaplexInfrastructureExtensions.cs
In AddDynaplexMqttBroker() method, add parameter and env var:
var emqxAdminPassword = builder.AddParameter("emqx-admin-password", secret: true);
var emqxErlangCookie = builder.AddParameter("emqx-erlang-cookie", secret: true);
var emqx = builder.AddContainer("emqx", "emqx/emqx", "5.8")
// ... existing config ...
.WithEnvironment("EMQX_NODE__COOKIE", emqxErlangCookie)
// ... rest of config ...
Stage 3: Production Logging Settings (Optional)
Add to both Bicep and Aspire:
EMQX_LOG__CONSOLE__LEVEL=warning
EMQX_LOG__FILE__ENABLE=false
Deployment Steps
Generate strong Erlang cookie:
openssl rand -base64 48 | tr -d '\n'Set azd environment variable:
azd env set AZURE_EMQX_ERLANG_COOKIE "<generated-cookie>"Deploy:
azd up
Expected Outcome
After deployment:
- No more Prometheus "connection refused" errors in logs
- Erlang cookie warning resolved
- Prometheus metrics available at
/api/v5/prometheus/statson port 18083 for future Grafana integration
Notes
- Keep
10MBmax packet size (matches your emqx.conf) - Keep
EMQX_MQTT__MAX_MQUEUE_LEN=0(unlimited queue) - Keep
2592000s(30 days) session expiry - matches Aspire code - emqx.conf file is NOT mounted - all config via environment variables (simpler, version-controlled)