EMQX Enterprise Version 6
6.1.1
Release Date: 2026-02-27
Make sure to check the breaking changes and known issues before upgrading to EMQX 6.1.1.
Enhancements
Core MQTT Functionalities
- #16637 Improved retained message delivery during session takeover. Previously, when a session was taken over during the delivery of retained messages from a wildcard subscription, the process would restart, causing duplicate messages. EMQX now resumes delivery from the last confirmed message of the previous session, significantly reducing duplication.
Durable Storage
- #16704 Optimized disk space preallocation for RocksDB storage shards. Previously, each Durable Storage shard preallocated significant disk space by default. This led to high storage consumption, especially with the 16-shard default configuration. EMQX now prevents aggressive preallocation, reducing the initial disk footprint for Durable Storage databases.
Message Queue and Streams
#16551, #16714 Refined Message Stream and Message Queue subscription interfaces.
- Named Streams: Users must now use the
$streamprefix and specify a name when subscribing. The syntax isSUBSCRIBE $stream/<name>/<topic_filter>or simplySUBSCRIBE $stream/<name>if the stream already exists. The starting point for consumption is defined via thestream-offsetuser subscription property. - Named Queues: Similarly, message queues now utilize the
$queueprefix. Subscriptions require a name using the syntaxSUBSCRIBE $queue/<name>/<topic_filter>orSUBSCRIBE $queue/<name>for existing queues. - Naming Rules: Names are restricted to alphanumeric characters, underscores, hyphens, and dots. Existing unnamed entities will automatically adopt their topic filter as their name (prepended with
/). - Compatibility: The legacy
$q(v6.0.0) and$s(v6.1.0) interfaces are deprecated but remain functional for compatibility. Note that when Message Queues are enabled, the$queueprefix can no longer be used for standard shared subscriptions.
- Named Streams: Users must now use the
#16820 Added shorter API path aliases
/queues/*and/streams/*for the Message Queue and Message Stream management APIs.The previous
/message_queues/*and/message_streams/*paths remain functional for backward compatibility but are no longer shown in the API documentation.
Gateway
#16719 Added Block-Wise Transfer support for CoAP and LwM2M gateways.
- Added block-wise settings:
enable,max_block_size,max_body_size, andexchange_lifetime. - Improved
POST /gateways/coap/clients/:clientid/requestand LwM2M downlink handling for large block-wise messages.
- Added block-wise settings:
#16736 Enhanced JT/T 808 Gateway features and protocol support.
Added the
jt808.frame.parse_unknown_messageoption, enabling the JT808 gateway to transparently forward unknown messages.Added JT/T 808 protocol 2019 support.
Added GBK character encoding support for JT/T 808 gateway.
The JT/T 808 protocol specifies GBK encoding for STRING type fields. A new
frame.string_encodingconfiguration option is added:utf8(default): Pass through strings as-is (backward-compatible)gbk: Convert GBK-encoded strings from devices to UTF-8 for MQTT, and UTF-8 from MQTT to GBK for devices
This affects both uplink parsing (GBK to UTF-8) and downlink serialization (UTF-8 to GBK), including string fields such as license plates, driver names, text messages, area names, and client parameters.
MQTT payloads always use UTF-8 encoding regardless of this setting.
Added support for custom
msg_snin JT/T 808 gateway downlink messages.When a downlink MQTT message payload contains a
msg_snfield in the header, the gateway will use that value instead of the auto-generated channel sequence number. This allows external systems to control message sequencing for specific use cases.Fixed JT/T 808 gateway parameter setting (0x8103) and query response (0x0104) message handling for CAN bus ID parameters (0x0110~0x01FF), which should use BYTE[8] data type with base64 encoding in JSON instead of string type.
Fixed JT/T 808 0x0702 driver identity report message parsing.
Security
#16447 Added
force_deleteparameter to Certificate Management APIs. TheDELETEmethods for global and namespace-specific certificate endpoints now support aforce_deletequery parameter:DELETE /certs/global/name/:nameDELETE /certs/ns/:ns/name/:name
When this parameter is
false(default), EMQX performs a safety check across all namespaces and prevents deletion if the certificate is currently referenced by any listener or configuration. Setting it totruebypasses these checks for immediate removal.#16461 Support for TLS 1.3 Stateless Session Resumption. EMQX now supports stateless session tickets for TLS 1.3, enabling clients to resume secure sessions without requiring server-side state storage. This improves performance and reduces memory overhead during high-frequency client reconnections.
Configuration:
- Global Secret: Set
node.tls_stateless_tickets_seedas the secret key seed used to encrypt session tickets across the node. - Listener Settings: Configure
listeners.ssl.<name>.ssl_options.session_ticketswith one of the following:disabled(Default): Resumption is deactivated.stateless: Enables resumption using stateless tickets.stateless_with_cert: Enables resumption and includes client certificate information in the ticket.
Important Note: To generate tickets, both a non-empty global seed and a listener-level enablement are required. If a listener is enabled while the global seed is missing, EMQX will log an error and tickets will not be issued.
- Global Secret: Set
Access Control
- #16504 Added a new configuration option to specify which OIDC data source field (claim) is used to generate the EMQX Dashboard username during Single Sign-On (SSO) user creation.
- #16741 Introduced
idp_signs_envelopesandidp_signs_assertionsoptions for the SAML SSO backend to precisely control signature verification behavior.- Resolved an issue where SAML signature verification failed because the IdP certificate fingerprint was not correctly extracted from metadata.
- Both options default to
falseto maintain backward compatibility. Users with IdPs configured to sign SAML responses should explicitly set these totrue.
- #16684 The
mqtt.client_attrs_initexpressions now support the use of client passwords. This allows passwords to be processed by functions (e.g.,jwt_value) to initialize custom client attributes during the connection phase. - #16730 Introduced a
compatibility_modesetting for Redis authorization to support legacy data schemas from EMQX v4.- Activation: Set
compatibility_mode = v4to enable. - Legacy Mapping: Automatically converts
%u/%cplaceholders and maps legacy ACL access values (1,2,3) tosubscribe,publish, andall. - Note: This mode is disabled by default to ensure no impact on existing v5 configurations.
- Activation: Set
Data Integration
- #16511 The IoTDB data integration now supports the Table Model, allowing for more structured data ingestion into Apache IoTDB.
- #16516 Added two specific metrics to track the performance of Aggregated Upload Actions (compatible with S3, Azure Blob Storage, Snowflake, and S3 Tables):
aggregated_upload.success: Incremented upon successful aggregated delivery.aggregated_upload.failure: Incremented when an aggregated delivery fails.
- #16658 Updated EMQX Tables Connector defaults and error handling.
- The default server port for the EMQX Tables Connector has been changed from
80to4001. - Enhanced error messaging for SSL-enabled EMQX Tables Connectors. If
cacertfile,certfile, orkeyfileare missing from the configuration, the system now returns a more descriptive error message to assist in troubleshooting.
- The default server port for the EMQX Tables Connector has been changed from
Rule Engine
#16524 Enhanced base64 encoding and decoding functions in rule engine SQL with support for padding and URL-safe options.
The
base64_encodeandbase64_decodefunctions now support optional parameters to control encoding behavior:no_padding: Encode or decode without padding characters (=). Useful when you need to remove padding from encoded strings or decode strings that don't have padding.urlsafe: Use URL-safe base64 encoding/decoding. Replaces+with-and/with_, making the encoded string safe to use in URLs without encoding.
You can use these options individually or combine them. When combining options, the order doesn't matter.
Examples in rule SQL:
Encode without padding:
sqlSELECT base64_encode(payload, 'no_padding') as encoded FROM "t/#"Encode with URL-safe characters:
sqlSELECT base64_encode(payload, 'urlsafe') as encoded FROM "t/#"Encode with both options (no padding and URL-safe):
sqlSELECT base64_encode(payload, 'no_padding', 'urlsafe') as encoded FROM "t/#"Decode URL-safe base64:
sqlSELECT base64_decode(payload, 'urlsafe') as decoded FROM "t/#"Decode unpadded URL-safe base64:
sqlSELECT base64_decode(payload, 'urlsafe', 'no_padding') as decoded FROM "t/#"#16533 Added
json_valueandjwt_valuehelper functions to Variform expression to extract values from JSON data and JWT tokens using dot-separated key paths.json_value(json_string, path): Parses a JSON-encoded binary string and navigates nested structures to extract a specific value.- Example:
json_value(username, 'shop.floor')extracts thefloorfield from a nestedshopobject within the username string.
- Example:
jwt_value(jwt_string, path): Decodes a JWT token and retrieves specific claim values from its payload.- Example:
jwt_value(password, 'client_attrs.unitid')extracts a customunitidclaim from a JWT provided in the password field.
- Example:
#16539 Added support for Sparkplug B metric alias tracking in
spb_decode. The Rule Engine'sspb_decodefunction now automatically tracks and resolves metric aliases based on Sparkplug B Birth certificates.- Dynamic Mapping: When a device or Edge of Network (EoN) node publishes
DBIRTHorNBIRTHmessages, EMQX stores the included alias-to-name mappings. - Automatic Resolution: Subsequent
DDATAorNDATAmessages processed viaspb_decodewill use these stored mappings to populate the original metric names in the output payload. - Limitation: Metric mappings are not available within the execution environment of fallback actions. If a fallback action republishes an undecoded payload, the metric name fields will remain unpopulated.
- Dynamic Mapping: When a device or Edge of Network (EoN) node publishes
#16581 Introduced
spb_zip_kvsRule SQL function for Sparkplug B data normalization.Added a new Rule Engine function,
spb_zip_kvs, to simplify the structure of decoded Sparkplug B messages. This function merges separatekeysandvaluesarrays into a unified key-value map, making the data significantly easier to process in downstream integrations.Key Transformations:
PropertySets: Recursively "zips"keysandvaluesfields. The original arrays are removed and replaced by a merged map.PropertySetLists: Flattens the structure by removing thepropertysetwrapper and replacing it with an array of transformed PropertySets.DataSets: Mergescolumnsandrowsinto a single object. Metadata fields liketypesandnum_of_columnsare stripped to provide a cleaner output.- Non-destructive: All other fields and values remain untouched.
For example, given this input decoded Sparkplug B message:
json{ "metrics": [ { "properties": { "values": [ {"int_value": 99}, { "propertyset_value": { "values": [{"int_value": 999}], "keys": ["inner"] } }, { "propertysets_value": { "propertyset": [ { "values": [{"int_value": 1}], "keys": ["inner1"] }, { "values": [{"int_value": 2}], "keys": ["inner2"] } ] } } ], "keys": [ "leaf", "nested_prop", "nested_prop_list" ] } }, { "dataset_value": { "num_of_columns": 2, "types": [7, 12], "rows": [ { "elements": [ {"int_value": 3}, {"string_value": "3"} ] }, { "elements": [ {"int_value": 4}, {"string_value": "4"} ] } ], "columns": ["col1", "col2"] } } ] }Then, the output of
spb_zip_kvswill be:json{ "metrics": [ { "properties": { "nested_prop_list": { "propertysets_value": [ {"inner1": {"int_value": 1}}, {"inner2": {"int_value": 2}} ] }, "nested_prop": { "propertyset_value": {"inner": {"int_value": 999}} }, "leaf": {"int_value": 99} } }, { "dataset_value": { "col2": {"elements": [{"int_value": 4}, {"string_value": "4"}]}, "col1": {"elements": [{"int_value": 3}, {"string_value": "3"}]} } } ] }
REST API
#16718 Refined the REST API specification to improve clarity and readability in the Swagger UI.
Previously, summaries and descriptions of spec fields were mixed together. Now, summaries are brief, simple and punctuation-free, while descriptions provide all the details.
#16735 EMQX now supports plugin-defined HTTP API callbacks under the
/api/v5/plugin_api/{plugin}/...path.This allows plugin authors to expose plugin-specific API endpoints through the dashboard API service, with consistent authentication and HTTP error handling.
Observability
#16656 Made system monitor reports such as
busy_portandlong_schedulemore informative by including process labels for easier troubleshooting.#16744 Supported end-to-end tracing of messages published via HTTP API.
Performance
#16413 Improved subscription handling performance.
#16492 Slightly improved idle system memory usage.
#16757 Set
os_monto collect only system-wide memory statistics by default, reducing per-process memory scanning overhead.
Bug Fixes
Core MQTT Functionalities
#16480 Fixed an issue where WebSocket connections could crash after the peer closed the connection, typically observed under moderate load.
crasher: initial call: cowboy_tls:connection_process/4, error: {{case_clause,{error,closed}},[ {cowboy_websocket_linger,websocket_send_close,2,[{file,"cowboy_websocket_linger.erl"},{line,752}]}, {cowboy_websocket_linger,websocket_close,3,[{file,"cowboy_websocket_linger.erl"},{line,743}]}, {proc_lib,wake_up,3,[{file,"proc_lib.erl"},{line,340}]} ]} messages: [ {ssl,{sslsocket,{gen_tcp,#Port<...>,...},[...]},<<130,130,27,93,145,101,251,93>>}, {ssl_closed,{sslsocket,{gen_tcp,#Port<...>,...},[...]}} ], ...#16515 Fixed a bug that caused WebSocket connections to crash when receiving broker messages larger than the client's advertised
Maximum-Packet-Size.#16553 Fixed an issue where reaching the dispatch rate limit would cause some retained messages to be skipped during delivery. Now, if a client hits the rate limit while iterating through retained topics, the process will no longer terminate the delivery; instead, it will retry the iteration using an exponential back-off strategy (ranging from a minimum of 300 ms to a maximum of 10 seconds) until all messages are sent.
Additionally, this update introduces configuration changes to the retainer’s flow control:
- The
retainer.flow_control.batch_deliver_numbersetting is now deprecated. - The
retainer.flow_control.batch_read_numberno longer supports a value of0(which previously indicated an unlimited batch size). If this parameter is set to0, it will now default to1000messages to prevent potential system instability from massive batch reads.
- The
#16569 Fixed a rare race condition that could cause the supporting
emqx_flappingprocess for flapping detection to crash under high system load.#16651 Fixed a rare connection process crash during shutdown caused by operating on an already closed socket, typically under high system stress. Previously, such race conditions typically result in an error-level log saying
{badmatch,{ok,{sock_error,closed}....#16675 Fixed timestamp ordering issue where
disconnected_atcould be later thanconnected_atduring session takeover or discard scenarios.Previously,
disconnected_atwas recorded too late (inensure_disconnected), after the new session'sconnected_atwas already set. This caused a race condition wheredisconnected_at > connected_at, making it difficult to track client presence state externally.The system now captures the
disconnected_attimestamp immediately at the onset of a session takeover or upon receiving a discard request. This adjustment ensures that disconnection events are always sequenced before the new session's connection time, providing reliable, ordered data for external state tracking and analytics.#16715 Fixed an issue where retained
$SYSmessages (for example, broker/node identity topics) were stored without expiry, which could leave stale node identifiers visible in Dashboard views after StatefulSet rotation.Now, newly published retained
$SYSmessages includeMessage-Expiry-Interval = 3600(1 hour).For already existing stale retained
$SYSentries created before this change, you can manually clear them by publishing an empty retained message to the stale topic:bashemqx eval 'emqx:publish(emqx_message:set_flag(retain, true, emqx_message:make(emqx_sys, <<"$SYS/brokers/emqx@127.0.0.1/sysdescr">>, <<>>))).'Replace the topic in the command with the stale
$SYS/...topic you want to remove.#16731 Fixed a crash in the
emqx ctl subscriptions listcommand that occurred when shared subscriptions were present. Previously, listing subscriptions could fail for certain clients and return no output. The command now works reliably for both standard and shared subscriptions.#16782 Fixed MQTT v5 protocol handling for invalid PUBLISH properties. If a client sends a PUBLISH packet containing
Subscription-Identifier, EMQX now treats it as a protocol error and disconnects the client.
Gateway
#16603 Fixed the CoAP Gateway when running in DTLS connection mode.
#16670 NATS gateway now enforces the maximum allowed publish payload size and correctly honors the
echooption to prevent local message delivery (loopback). Additionally, this update improves the validation of publish and subscribe topics and provides more descriptive error messages.
Access Control
#16423 Added support for verifying the
aud(audience) claim within JWT authentication.When the
audclaim is configured inverify_claims, the JWT token must include a validaudclaim. The verification supports both string and array formats:- If
audis a string, it must exactly match the expected value. - If
audis an array, at least one element in the array must match the expected value. - Empty string or empty array will fail verification.
- Missing
audclaim will fail verification when it is configured inverify_claims.
- If
#16459 Fixed the issue in SCRAM authentication HTTP API. Previously, incorrect user ID was returned for the created user in the user creation API call.
Data Integration
#16507 Fixed an issue where an MQTT Source would stop receiving messages after its Connector reconnected.
Previously, when an MQTT Source’s Connector recovered from a connection loss, its topics were not re-subscribed, causing the Source to stop working until the Connector was restarted. The Source now automatically re-subscribes upon reconnect.
#16542 Fixed an issue where Kafka producer connections could disconnect prematurely when Kafka was overloaded, leading to excessive produce request retries.
The produce request timeout is now automatically set to at least twice the metadata request timeout, with a minimum of 30 seconds. This reduces unnecessary reconnections and retries when metadata requests take longer than expected, especially when the metadata request timeout is configured to a small value.
#16622 Fixed an issue where, if an Action used async query mode and its Connector was disconnected after more than one health check, its Fallback Actions could be triggered twice.
#16657 Fixed a configuration migration issue where data imported from older EMQX versions failed to undergo the necessary schema conversions for compatibility with newer versions.
A notable example occurred when migrating MQTT Connectors with static ClientIDs from v5.10.0 to v6.0.0; the internal representation of credentials associated with ClientIDs changed between these versions, but the migration logic failed to apply the required transformation. This fix ensures that all imported configurations are passed through the appropriate converters, maintaining functional integrity.
#16659 Fixed an upgrade compatibility issue where MQTT Connectors migrated from v5.10.0 and earlier ignored root-level credentials when using static ClientIDs. Previously, the migration logic failed to pass root username and password fields to the individual ClientID entries, causing connection failures with remote brokers after an upgrade.
Now, if there are username and/or password fields in the root Connector, those credentials are merged with any specific ones specified per clientid, the latter taking precedence.
#16723 Resolved a self-healing issue within the RabbitMQ Connector, Action, and Source components. Previously, if the underlying connection or channel processes terminated unexpectedly, the component would remain in a "Disconnected" state indefinitely, requiring a manual restart to restore functionality.
#16742 Fixed the issue of GreptimeDB TLS connection failure.
Durable Storage
#16512 Improved the handling of recoverable errors in durable sessions. Durable sessions now retry the creation of durable storage iterators when the operation fails due to network issues, whereas previously the entire session would disconnect.
Fixed an issue in the
emqx_ds_clientcomponent's retry mechanism where the number of retry attempts for recoverable errors was previously limited.Fixed several issues related to shared subscriptions:
- Fixed an issue where the shared subscription leader would not start after a node restart.
- The shared subscription leader no longer advertises streams that have reached the end of replay to clients.
- Added support for configuring the state checkpoint transaction options for the shared subscription leader.
#16614 Introduced improvements and bug fixes for the durable storage feature:
Improved handling of configuration discrepancies between nodes. Previously, inconsistent initial durable storage configurations prevented replica convergence. This change ensures that the shard leader's configuration is replicated to all replicas during storage initialization and subsequent updates.
Note
This change is not backward-compatible. During a rolling upgrade, shards will pause until a majority of replicas are upgraded. Once the majority are upgraded, downgrading to previous EMQX versions is no longer possible.
Resolved an issue in the durable storage subscription mechanism where a subscription created with a new iterator could skip messages if their timestamp precisely matched the iterator's timestamp.
#16770 Improved stability of durable sessions during takeover and garbage collection.
Clustering
#16393 Improved the stability of the Cluster Linking route replication under unstable network conditions.
#16465 Upgraded
gen_rpcto3.5.1.Before the
gen_rpcupgrade, EMQX may experience a long tail of crash logs due to a connect timeout if a peer node is unreachable. The new version of gen_rpc no longer has the long tail and has converted crash logs to more readable error logs. Additionally, the frequent log"failed_to_connect_server"is also throttled to avoid spamming.#16544 Improved the robustness of the cluster autoclean procedure. Previously, if the autoclean feature was disabled during the initial startup of a node, it would not be activated after subsequent configuration changes.
#16739 Improved cluster recovery time following a simultaneous restart of all nodes. The built-in Mria database management system no longer waits for the full synchronization of an internal table used to generate transaction synchronization events.
Observability
#16537 Fixed a formatter crash triggered by certain
gen_rpcerror messages.Previously, EMQX could crash with a “FORMATTER CRASH” error when
gen_rpclogged specific errors (such as transmission timeouts). The formatter now safely handles these messages without crashing.#16661 Improved logging for
topic_metricsandcluster_rpcwhen processing invalid topic requests.#16674 Updated the logging system to ensure the Erlang process identifier (PID) is explicitly included as a structured data field in log outputs.
#16699 Improved the error handling and logging for the Rule Engine metrics worker. Previously, under certain race conditions, long and cryptic logs like the following could be printed:
2026-02-03T13:53:54.576326+00:00 [error] Generic server <0.11323236.0> terminating. Reason: {{badkey,'actions.success'},[{erlang,map_get,['actions.success',#{}],[{error_info,#{module => erl_erts_errors}}]},{emqx_metrics_worker,idx_metric,4,[{file,"emqx_metrics_worker.erl"},{line,683}]},{emqx_metrics_worker,inc,4,[{file,"emqx_metrics_worker.erl"},{line,322}]},{emqx_rule_runtime,do_eval_action_reply_t...Now, the system print more meaningful information to help debug the issue.
Security
#16545 Fixed
node.cookiehandling of#character. Previously, if the cookie contained#, only the prefix before#would take effect. For example, ifabc#dwas configured, onlyabcwas used as the cookie.Added validation to reject problematic characters, including backslash, single quote, double quote, and space.
#16664 Previously, it was possible to upload managed certificate files associated with non-existent managed namespaces. Now, namespace existence is checked before accepting the upload.
#16692 Fixed a CRL cache regression where
emqx_crl_cache:evict/1did not fully clear internal URL state. After eviction, the same CRL URL now re-registers correctly on next use, restores its refresh timer, and avoids repeated HTTP fetches per connection.
Plugin
#16784 Reduced noisy plugin startup warnings in single-node deployments.
EMQX no longer tries to fetch plugin config from the local node during cluster config sync, avoiding repeated
config_not_found_on_nodewarnings at startup.#16823 Fixed a Dashboard plugin management issue for preinstalled plugins.
When a plugin package is unpacked into
plugins/before node startup, starting it from the Dashboard no longer causesPlugin Config Not Foundon the plugin config page.
Miscellaneous
- #16620 Fixed CRC32C dynamic library load issue on aarch64.
6.1.0
Release Date: 2025-12-30
Make sure to check the breaking changes and known issues before upgrading to EMQX 6.1.0.
Feature Highlights
EMQX 6.1.0 introduces MQTT Streams, enhanced namespace capabilities, new data integrations, and centralized certificate management.
MQTT Streams
MQTT Streams feature provide durable collections of messages identified by a topic filter, with explicit lifecycle management. Messages matching a stream's topic filter are automatically appended, enabling consumption with ordering guarantees and support for multiple consumers. Clients can subscribe to streams using the special topic format $s/<timestamp>/topic/filter to consume messages from a specific point in time.
Enhanced Namespace Capabilities
- Configurations for namespace and isolation settings are now grouped together in the dashboard.
- Expanded namespace functionality with namespaced metrics, authentication, and authorization.
- Namespaced metrics are now available for messages, sessions, and data integration operations, exposed via Prometheus endpoints.
- Built-in authentication and authorization backends now support namespace-specific users and rules, enabling better multi-tenant isolation.
- Added automatic topic isolation using client namespaces as mountpoints.
New Data Integrations
- AWS Timestream for InfluxDB connector
- EMQX Tables connector
- InfluxDB API v3 support for InfluxDB and AWS Timestream connectors
- OAuth authentication for Kafka and Confluent Producer connectors
- Parquet file support for Azure Blob Storage and S3 Actions in Aggregated mode
Certificate Management
Added centralized certificate management via HTTP API, allowing certificates to be managed independently and referenced in SSL options for listeners and connectors.
Enhancements
Message Queue and MQTT Stream
#16326 Implemented MQTT Streams.
MQTT Streams are durable collections of messages identified by a topic filter. They have an explicit lifecycle, and any published message that matches the Stream's topic filter is automatically appended to the stream. Streams allow consumption of messages with ordering guarantees and can be consumed multiple times. To consume messages from a stream, clients can subscribe to a special topic of the form
$s/<timestamp>/topic/filter, wheretopic/filterrefers to an existing stream. Subscribing with a timestamp allows consumption to begin at a specific point in time. The timestamp may be a Unix timestamp in microseconds or one of two special values:earliestorlatest.#16454 For Message Queues and MQTT Streams, reconfigured garbage collection interval is now applied immediately. Previously, the new interval was applied only after the next garbage collection cycle.
Core MQTT Functionalities
- #16099 Added a new rule engine event:
$events/client/ping. This is triggered when a client sends aPINGREQpacket.
Access Control
#16132 Added an HTTP API to manage certificates in a centralized manner.
#16154 Added support for referencing managed certificate files in SSL options of listeners and clients.
#16266 Added a new
authorization.include_mountpointconfiguration. When enabled, topics will be prefixed by the listener's mountpoint before being evaluated by authorization backends.#16272 Added support for specifying namespaced rules when using the built-in authorization backend. Now, MQTT clients that belong to a namespace will consider only their namespaced rules when authorizing actions.
#16345 Added support for specifying namespaced users when using the built-in authentication backend. Now, MQTT clients that belong to a namespace will consider only their namespaced data when authenticating.
Data Integration
#15905 Now, for the HTTP Action, the HTTP request timeout is taken to be the same as
resource_opts.request_ttl. Previously, it was a fixed, non-configurable value of 30 seconds.#16169 Updated our
parquerdependency to support encodingtimestampIceberg types to Parquet files.#16179 Added support for writing Parquet files when using the Aggregated mode in Azure Blob Storage and S3 Actions.
#16267 EMQX supports data integration with AWS Timestream for InfluxDB.
#16290 Added support for OAuth authentication when using Kafka and Confluent Producer Connectors.
#16316 Changed the default batch size and time for multiple actions. Actions that previously supported batch operations had their defaults increased, so that now batching is the default behavior for them.
#16372 Added support for InfluxDB API v3 to InfluxDB and AWS Timestream Connectors.
#16396 EMQX supports data integration with EMQX Tables.
Durable Storage
#16136 Improved resource management and performance for durable storage.
Introduced a concept of durable storage database group. Certain resources (such as memtable size and disk usage quota) can be shared between the group members.
Added the following new metrics (per DB group):
emqx_ds_disk_usage: Total size of SST filesemqx_ds_write_buffer_memory_usage: RocksDB memtable sizeemqx_ds_total_trash_size: Disk usage by trash SST files
Added the following group configurations:
durable_storage.db_groups.<group>.storage_quota: Soft quota for the SST files sizedurable_storage.db_groups.<group>.write_buffer_size: Maximum memtable sizedurable_storage.db_groups.<group>.rocksdb_nthreads_highanddurable_storage.db_groups.<group>.rocksdb_nthreads_low: Size of RocksDB thread pools.
Added a new alarm that is raised when the quota is exceeded:
db_storage_quota_exceeded:<DB>. Please refer to the "Storage Quota" section of the documentation for more details.Default session checkpoint interval has been changed to 15s.
#16286 Optimized the default durable storage settings to reduce CPU load. This PR disables subscriptions for DBs that don't use them.
Namespace
#16211 Added initial support for namespaced metrics.
- Messages received
- Count
- Bytes
- Messages sent
- Count
- Bytes
- Number of sessions
- Data integration
- Number of actions triggered
- DB records
- Number of AuthN records
- Number of AuthZ records
Clients in managed namespaces will bump the namespaced metrics above, as well as continue to bump the usual global metrics.
These metrics are exposed in Prometheus format to be scraped from the
GET /prometheus/ns/statsendpoint. By specifying thens=NAMESPACEquery parameter, only data fromNAMESPACEwill be returned. Omitting this parameter causes data from all namespaces to be scraped. Namespaces are added as labels to metrics.#16314 Now, global admin users will see resources from all namespaces (by default) when listing namespaced resources (connectors/sources/actions/rules). They may focus on one particular namespace when performing CRUD operations by passing the
ns=NSquery parameter. If they want to list only the global namespace resources, they omitnsand passonly_global=truequery parameter. Namespaced resources now return thenamespacefield to denote where they come from, withnamespacebeingnullfor global resources to distinguish them from a potential namespace called"global".#16360 Added a
GET /mt/ns/:ns/metricsendpoint that will return namespace-specific metrics in JSON format.#16472 Added a new configuration option
namespace_as_mountpointto enable automatic topic isolation using client namespaces.When enabled, EMQX uses the client's namespace (from
client_attrs.tns) as a topic mountpoint if no mountpoint is configured on the listener.Topics are automatically prefixed with the namespace for PUBLISH, SUBSCRIBE, UNSUBSCRIBE, and Will messages, and the prefix is stripped when delivering messages to clients.
This setting is ignored if the listener already has a mountpoint configured, ensuring existing configurations take precedence.
Observability
#16135 Added two new metrics and corresponding rates for the
GET /monitor_currentHTTP API:rules_matchedandactions_executed. They track the number of rules that matched and action execution rate (i.e., success + failure), respectively.#16213 Added MQTT client ID as a process label so crash logs (including max-heap and force-shutdown errors) now include the client ID for easier troubleshooting.
Performance
#16368 Upgraded the underlying runtime system from Erlang/OTP 27 to Erlang/OTP 28.
#16377 Reduced the number of pre-allocated metrics counters, which should contribute to reduced memory usage, especially in clusters using lots of namespaces.
MQTT over QUIC
#16133 MQTT over QUIC: Added support for connection probing using datagrams.
EMQX now supports zero-length datagram packets sent by clients to test connectivity. Clients can also send non-zero-length datagram packets, but they will be ignored by EMQX.
Bug Fixes
Core MQTT Functionalities
#16344 Fixed a crash in MQTT v5 connections caused by a type mismatch when processing the request-response-information property.
#16354 Backported the MQTT v5
request-response-informationschema type fix to the 6.0.x release line.
Access Control
#16308 Fixed an issue where Multi-Factor Authentication (MFA) could not be enabled after upgrading EMQX from versions earlier than 5.3.0 due to incompatible login-user database records.
#16446 Fixed an issue with authenticator metrics when using SCRAM in which the 'Total' count would be incremented twice for each authentication attempt, and the 'Success' count would not be bumped.
Data Integration
#16265 The health check now verifies leader connectivity only for the partitions assigned to the current EMQX node, preventing unnecessary idle connections and false alarms.
Previously, the Kafka source connector checked leader connectivity for all partitions. In clustered deployments, each node owns only a subset of partitions, leaving connections to unassigned partition leaders idle. Because Kafka closes idle connections after a timeout (10 minutes by default), this could result in false connectivity alarms.
#16352 Upgraded Apache Pulsar client to 2.1.2. When Pulsar producer action's
batch_sizeis configured to1, the producer will now encode single messages instead of single-element batch. This should allow consumers to share load using Key Share strategy.#16383 Previously, when using IoTDB Connector with its RestAPI driver, credentials would not be checked during health checks. Now, we send a no-op query during IoTDB connector health-check. This enables early detection of misconfigured client credentials.
Message Queue
- #16270 Fixed a shutdown handling issue in the EMQX message queue consumer.
Clustering
#16453 Upgraded
gen_rpcto3.5.1.Prior to the
gen_rpcupgrade, EMQX may experience long tail of crash logs due to connect timeout if a peer node is unreachable. The new versiongen_rpcno longer has the long tail and converted crash logs to more readableerrorlogs, and the frequent log"failed_to_connect_server"is also throttled to avoid spamming.
Cluster Linking
#16269 Fixed an issue in the Cluster Link route replication protocol recovery sequence where re-bootstrapping was incorrectly skipped even though the remote side needed it.
#16317 Fixed an issue in Cluster Link garbage-collection logic that could accidentally remove live routes from the internal routing table in the process of cleaning up stale route replication state. This problem occurred only when multiple independent Cluster Links were set up, and some of these links went down for relatively long periods of time.
Observability
#16417 Reduced the volume of logs generated when a resource exception occurs (
resource_exception). These logs are now throttled, and some potentially large terms are redacted from them.#16434 Now, clearing an alarm name will clear it from all nodes. Previously, using the HTTP API to force deactivate an alarm would not clear it from all nodes.
Gateway
- #16425 Improved the returned errors when creating or updating a Gateway via the HTTP API.
Miscellaneous
#16397 Added TLS certificate validation before listener start. Fail-fast if listener is misconfigured with invalid certificates.
#16311 Updated error codes to correct terminology from misspelled
REST_FAILEDtoRESET_FAILED.
6.0.2
Release Date: 2026-01-16
Make sure to check the breaking changes and known issues before upgrading to EMQX 6.0.2.
Enhancements
Security
#16461 EMQX now supports TLS 1.3 session resumption using stateless session tickets, allowing clients to resume TLS connections without requiring server-side session state.
Configuration
Node-level:
node.tls_stateless_tickets_seedSecret key seed used to generate TLS 1.3 stateless session tickets.
Listener-level:
listeners.ssl.<name>.ssl_options.session_ticketsEnables TLS 1.3 session resumption. Supported values:
disabled(default)statelessstateless_with_cert(includes certificate information in the ticket)
Notes
- Session tickets are generated only when
node.tls_stateless_tickets_seedis configured (non-empty), andsession_ticketsis enabled in listener SSL options. - If
session_ticketsis enabled butnode.tls_stateless_tickets_seedis empty, session tickets will not be generated and an error log will be emitted when starting the listener.
This PR also included a fix for the TLS 1.2 session resumption configuration. Previously, the
reuse_sessionsoption for SSL listener did not take effect, i.e. EMQX always tried to enable TLS 1.2 session resumption. It is now possible to turn it off. Please note that TLS 1.2 session resumption will be disabled by default starting version 6.2.0.
Rule Engine
#16524 Enhanced base64 encoding and decoding functions in rule engine SQL with support for padding and URL-safe options.
The
base64_encodeandbase64_decodefunctions now support optional parameters to control encoding behavior:no_padding: Encode or decode without padding characters (=). Useful when you need to remove padding from encoded strings or decode strings that do not have padding.urlsafe: Use URL-safe base64 encoding/decoding. Replaces+with-and/with_, making the encoded string safe to use in URLs without encoding.
These options can be used individually or combined in any order.
Examples in rule SQL:
Encode without padding:
sqlSELECT base64_encode(payload, 'no_padding') as encoded FROM "t/#"Encode with URL-safe characters:
sqlSELECT base64_encode(payload, 'urlsafe') as encoded FROM "t/#"Encode with both options (no padding and URL-safe):
sqlSELECT base64_encode(payload, 'no_padding', 'urlsafe') as encoded FROM "t/#"Decode URL-safe base64:
sqlSELECT base64_decode(payload, 'urlsafe') as decoded FROM "t/#"Decode unpadded URL-safe base64:
sqlSELECT base64_decode(payload, 'urlsafe', 'no_padding') as decoded FROM "t/#"#16533 Added two new variadic expression helper functions,
json_valueandjwt_value, for extracting values from JSON data and JWT tokens using dot-separated key paths.json_valueextracts values from JSON binary strings by navigating nested objects with a dot-separated key path.jwt_valuedecodes the payload of a JWT and extracts claim values using the same dot-separated path syntax.
Examples:
- If
usernamecontains a JSON object, you can access a nested field withjson_value(username, 'shop.floor'). - If
passwordcontains a JWT with a customized claim, you can access a nested value withjwt_value(password, 'client_attrs.unitid').
#16539 Added support for tracking Sparkplug B metric aliases when using the
spb_decodeRule Engine function.After a device or Edge of Network (EoN) node publishes its
NBIRTHorDBIRTHmessages, EMQX records the alias-to-name mappings defined in those messages. Whenspb_decodeis later applied toNDATAorDDATAmessages from the same session, the original metric names are automatically restored and included in the decoded output.Note: when executing fallback actions, the mapping is not available in the environment where they run. This means that, if a fallback action republishes the undecoded
DDATA/NDATApayload to a Sparkplug BDDATA/NDATAtopic, the metricnamefields will not be populated by the alias mapping.
Durable Storage
#16136 Improved resource management and performance for durable storage.
Introduced a concept of a durable storage database group. Certain resources (such as memtable size and disk usage quota) can be shared between the group members.
Added the following new metrics (per DB group):
emqx_ds_disk_usage: Total size of SST filesemqx_ds_write_buffer_memory_usage: RocksDB memtable sizeemqx_ds_total_trash_size: Disk usage by trash SST files
Added the following group configurations:
durable_storage.db_groups.<group>.storage_quota: Soft quota for the SST files sizedurable_storage.db_groups.<group>.write_buffer_size: Maximum memtable sizedurable_storage.db_groups.<group>.rocksdb_nthreads_highanddurable_storage.db_groups.<group>.rocksdb_nthreads_low: Size of RocksDB thread pools.
Added a new alarm that is raised when the quota is exceeded:
db_storage_quota_exceeded:<DB>. Please refer to the "Storage Quota" section of the documentation for more details.Default session checkpoint interval has been changed to 15s.
#16286 Optimized the default durable storage settings to reduce CPU load. This PR disables subscriptions for DBs that don't use them.
Performance
- #16413 Improved subscription handling performance by reducing redundant monitoring of MQTT session processes.
Bug Fixes
Core MQTT Functionalities
#16354 Fixed a crash in MQTT v5 connections caused by a type mismatch when processing the request-response-information property.
#16515 Fixed an issue where WebSocket connections could crash when the broker sent messages exceeding the client-advertised
Maximum-Packet-Size.#16569 Fixed a rare race condition that could cause the supporting
emqx_flappingprocess for flapping detection to crash under high system load.
Data Integration
#16265 The health check now verifies leader connectivity only for the partitions assigned to the current EMQX node, preventing unnecessary idle connections and false alarms.
Previously, the Kafka source connector checked leader connectivity for all partitions. In clustered deployments, each node owns only a subset of partitions, leaving connections to unassigned partition leaders idle. Because Kafka closes idle connections after a timeout (10 minutes by default), this could result in false connectivity alarms.
#16542 Fixed an issue where Kafka producer connections could disconnect prematurely when Kafka was overloaded, leading to excessive produce request retries.
The produce request timeout is now automatically set to at least twice the metadata request timeout, with a minimum of 30 seconds. This reduces unnecessary reconnections and retries when metadata requests take longer than expected, especially when the metadata request timeout is configured to a small value.
#16352 Upgraded Apache Pulsar client to 2.1.2. When Pulsar producer action's
batch_sizeis configured to1, the producer will now encode single messages instead of single-element batch. This should allow consumers to share load using Key Share strategy.#16383 Improved the IoTDB Connector health check when using the REST API driver.
Previously, client credentials were not validated during health checks. The health check now sends a lightweight no-op query, allowing misconfigured credentials to be detected early.
#16507 Fixed an issue where an MQTT Source would stop receiving messages after its Connector reconnected.
Previously, when an MQTT Source’s Connector recovered from a connection loss, its topics were not re-subscribed, causing the Source to stop working until the Connector was restarted. The Source now automatically re-subscribes upon reconnect.
Clustering
#16269 Fixed an issue in the Cluster Linking route replication protocol recovery sequence where re-bootstrapping was incorrectly skipped even though the remote side needed it.
#16317 Fixed an issue in Cluster Linking garbage-collection logic that could incorrectly remove active routes from the internal routing table while cleaning up stale route replication state.
This issue could occur only in setups with multiple independent Cluster Links, where some links remained down for extended periods.
#16465 Upgraded
gen_rpcto3.5.1.Before the
gen_rpcupgrade, EMQX may experience a long tail of crash logs due to a connect timeout if a peer node is unreachable. The new version of gen_rpc no longer has the long tail and has converted crash logs to more readable error logs. Additionally, the frequent log"failed_to_connect_server"is also throttled to avoid spamming.#16544 Improved the robustness of the cluster autoclean procedure. Previously, if the autoclean feature was disabled during the initial startup of a node, it would not be activated after subsequent configuration changes.
Upgrade
- #16308 Fixed an issue where Multi-Factor Authentication (MFA) could not be enabled after upgrading EMQX from versions earlier than 5.3.0 due to incompatible login-user database records.
Configuration Management
#16397 Added TLS certificate and key file validation before listener startup.
EMQX now performs basic validation when parsing SSL listener configuration and emits error-level logs if invalid PEM files are detected (for example,
invalid_pem_file_ignoredandbad_keyfile_ignored). This makes troubleshooting easier as administrators can observe errors when starting/reconfiguring, instead of troubleshooting TLS handshake failures.
Access Control
#16423 Added support for verifying the JWT
aud(audience) claim during authentication.When the
audclaim is configured inverify_claims, the JWT must include a validaudvalue. Both string and array formats are supported:- If
audis a string, it must exactly match the configured value. - If
audis an array, at least one element must match the configured value. - An empty string or empty array fails verification.
- The verification also fails if the
audclaim is missing when it is configured inverify_claims.
- If
#16459 Fixed the issue in SCRAM authentication HTTP API. Previously, incorrect user ID was returned for the created user in the user creation API call.
Observability
#16417 Reduced log volume for
resource_exceptionevents. Logs generated when a resource exception occurs are now throttled, and potentially large terms are redacted to prevent excessive log output.#16537 Fixed a formatter crash triggered by certain
gen_rpcerror messages.Previously, EMQX could crash with a “FORMATTER CRASH” error when
gen_rpclogged specific errors (such as transmission timeouts). The formatter now safely handles these messages without crashing.
6.0.1
Release Date: 2025-11-11
Make sure to check the breaking changes and known issues before upgrading to EMQX 6.0.1.
Enhancements
Message Queue
- #16080 Added a configuration option to disable the Message Queues feature. Disabling Message Queues can slightly reduce the resource usage in the cluster. When Durable Sessions are also disabled, EMQX avoids maintaining Durable Storage, further reducing administrative overhead and improving performance.
- #16096 Added support for automatic creation of message queues when clients subscribe to non-existent
$q/topics. Now configuration options are available to enable auto-creation for both regular and last-value semantics queues. - #16097 Optimized message writing to regular message queues by replacing transactional appends with dirty append functions. For QoS 0 messages, asynchronous append operations are now used. These changes significantly improve the performance of message insertion into regular queues.
- #16098 Added a maximum queue count configuration option to limit the total number of message queues in the system.
- #16152 Introduced per-queue limits for maximum message count and total message size. Also added new metrics to monitor message append latency and help diagnose performance or queue-limiting issues.
Data Integration
#16121 Upgraded the GreptimeDB ingester client to v0.2.3, which fixes several bugs and introduces support for row-based gRPC protocol (the column-based protocol is now deprecated).
Additionally, updated the CI image to the latest stable version of GreptimeDB.
#16127 Fixed an invalid string value issue in the GreptimeDB connector, following the changes introduced in #16121.
Performance
#15949 Changed the default value of the
parse_unitoption in listener configuration fromchunktoframe. This change can significantly reduce CPU usage when the payload size exceeds the socket buffer (default is 4 KB).Note: With
parse_unit = frame, if aPUBLISHpacket exceeds the maximum allowed size, EMQX will close the connection instead of sending aDISCONNECTpacket.#16165 Optimized the performance of the
GET /clients_v2API. Previously, when the cluster had around 50,000 clients or more, API calls to retrieve the client list could be extremely slow or even time out.
Bug Fixes
Core MQTT Functionalities
- #15884 Resolve an issue where, in rare cases, the global routing table could indefinitely retain routing information for nodes that had long left the cluster.
- #15518 Resolved a race condition that may lead to accumulating inconsistencies in the routing table and shared subscriptions state in the cluster when a large number of shared subscribers disconnect simultaneously.
Upgrade
#16047 Added support to perform rolling upgrade from EMQX Enterprise base version 5.8.0 and newer to 6.0. During the upgrade, legacy configurations are automatically migrated to the new format supported in 6.0. Specifically, the deprecated
bridgesconfiguration root is converted into the newconnectors,sources, andactionsroots.However, the GCP PubSub Consumer and Kafka Consumer sources will still require manual changes. If any source configuration still includes the deprecated
topic_mappingfield, it must be removed. Then, for each entry previously defined intopic_mapping, a separate "Source + Rule" pair must be created manually.
Security
#16156 Fixed an issue where some dependencies were missing default configurations compared to EMQX 5.10, potentially causing RSA signature verification failures. The missing defaults could lead to errors, such as the following log message:
{sign_unsupported,[[{rsa_padding,rsa_pkcs1_padding}]]}, [{jose_jwa_unsupported,verify,5,[{file,"src/jwa/jose_jwa_unsupported.erl"},{line,55}]}#16175 Fixed an issue with periodic TLS certificate garbage collection. Previously, the garbage collection process incorrectly deleted certificate files that were actively used by configurations in managed namespaces.
Access Control
#16081 Fixed an issue where clients using extended authentication and memory-based sessions could crash with a
session_stepdown_request_exceptioncaused by acalling_selferror.Example error log
2025-09-24T07:13:08.973954+08:00 [error] clientid: someclientid, msg: session_stepdown_request_exception, peername: 127.0.0.1:41782, username: admin, error: exit, reason: calling_self, stacktrace: [{gen_server,call,3,[{file,"gen_server.erl"},{line,1222}]},{emqx_cm,request_stepdown,4,[{file,"emqx_cm.erl"},{line,427}]},{emqx_cm,do_takeover_begin,2,[{file,"emqx_cm.erl"},{line,398}]},{emqx_cm,takeover_session,2,[{file,"emqx_cm.erl"},{line,384}]},{emqx_cm,takeover_session_begin,2,[{file,"emqx_cm.erl"},{line,305}]},{emqx_session_mem,open,4,[{file,"emqx_session_mem.erl"},{line,210}]},{emqx_session,open,3,[{file,"emqx_session.erl"},{line,263}]},{emqx_cm,'-open_session/4-fun-1-',4,[{file,"emqx_cm.erl"},{line,290}]},{emqx_cm_locker,trans,2,[{file,"emqx_cm_locker.erl"},{line,32}]},{emqx_channel,post_process_connect,2,[{file,"emqx_channel.erl"},{line,575}]},{emqx_connection,with_channel,3,[{file,"emqx_connection.erl"},{line,852}]},{emqx_connection,process_msg,2,[{file,"emqx_connection.erl"},{line,470}]},{emqx_connection,process_msgs,2,[{file,"emqx_connection.erl"},{line,462}]},{emqx_connection,handle_recv,3,[{file,"emqx_connection.erl"},{line,406}]},{proc_lib,wake_up,3,[{file,"proc_lib.erl"},{line,340}]}], action: {takeover,'begin'}, ...
Clustering
#16123 Fix a bug in the component managing Mria replication that could cause cluster joins to hang or remain incomplete in core-replicant clusters.
During cluster changes involving adding new core nodes, those new core nodes could sometimes fail to start replication-related processes required by replicants. As a result, upgraded or newly added replicants could hang during startup.
In Kubernetes deployments, this often caused readiness probes to fail, leading the controller to repeatedly restart the affected replicant pods.
This issue typically affected upgrade rollouts involving the addition of new core and replicant nodes. For example, adding two cores and two replicants (running a newer EMQX version) to an existing cluster with 2 cores and 2 replicants.
Rule Engine
#16028 Fixed rule engine
jqfunction memory leak.Previously if
jqbuilt-in functionindexis used (e.g..key | index("name")), it would result in memory leak.
Data Integration
#16010 Fixed an issue where a Republish Fallback Action could fail with a
function_clauseerror if the originating rule's SQL did not include themetadatafield from the rule environment.Example error log:
[error] tag: RESOURCE, msg: failed_to_trigger_fallback_action, reason: {error,function_clause}, fallback_kind: republish, primary_action_resource_id: <<"action:type:name:connector:type:name">>, republish_topic: <<"republish/topic">>#16046 Fixed a potential out-of-memory (OOM) crash when loading or restarting a configuration containing a Connector with several hundred Actions.
#16140 Fix a Redis cluster failover issue that could cause the Connector to remain stuck in a "connecting" state.
Previously, EMQX’s Redis cluster client only refreshed the cluster topology when regular queries (such as
GET) failed. However, failures in periodicPINGcommands did not trigger a refresh. As a result, after a failover, the connector could continue using the outdated cluster topology if no other commands were issued, preventing recovery.With this fix, failed
PINGresponses now trigger a cluster topology refresh, ensuring that the connector can detect failovers and recover promptly.
MQTT Durable Sessions
- #16105 Durable storage performance optimization. In particular, this fix reduces the latency of
CONNACKfor clients using a durable session. - #16129 Durable storage transaction configuration can be changed in the runtime. Previously changing this configuration required a node restart.
Observability
#15963 Reduced excessive audit log entries generated during looped evaluations in the remote shell (
remsh).#15967 Fixed an issue where Mnesia transaction blocking during the cleanup of large volumes of audit logs could lead to rapid memory growth.
#16060 Fixed a logger formatter crash that could occur for some debug-level log messages containing deeply nested terms with non-ASCII characters.
Example error log
2025-09-29T06:55:34.120640+00:00 debug: FORMATTER CRASH: {report,#{request => #{messages => [#{role => <<"user">>,content => <<"{\"msg\": \"hello\"}">>}],system => <<"将输入的 JSON 数据中,值为数字的 value 相加起来,并输出,只需返回输出结果。"/utf8>>,model => <<"claude-3-haiku-20240307">>,max_tokens => 100},msg => emqx_ai_completion_request}} 2025-09-29T06:55:34.120780+00:00 [debug] formatter_crashed: emqx_logger_textfmt, config: #{time_offset => [],chars_limit => unlimited,depth => 100,single_line => true,template => ["[",level,"] ",msg,"\n"],with_mfa => false,timestamp_format => auto,payload_encode => text}, log_event: #{meta => #{line => 44,pid => <0.281254.0>,time => 1759128934120640,file => "emqx_ai_completion_anthropic.erl",gl => <0.4317.0>,mfa => {emqx_ai_completion_anthropic,call_completion,3},report_cb => fun logger:format_otp_report/1,matched => <<"t/1">>,namespace => global,clientid => <<"c_emqx">>,trigger => <<"t/1">>,rule_id => <<"r1sczoo0">>,rule_trigger_ts => [1759128934120]},msg => {report,#{request => #{messages => [#{role => <<"user">>,content => <<"{\"msg\": \"hello\"}">>}],system => <<"将输入的 JSON 数据中,值为数字的 value 相加起来,并输出,只需返回输出结果。"/utf8>>,model => <<"claude-3-haiku-20240307">>,max_tokens => 100},msg => emqx_ai_completion_request}},level => debug}, reason: {error,badarg,[{erlang,iolist_to_binary,[["[",[["messages",": ",[[91,[[35,123,[["role"," => ",[60,60,"\"user\"",62,62]],44,["content"," => ",[60,60,"\"{\\\"msg\\\": \\\"hello\\\"}\"",62,62]]],125]],93]]],", ",["system",": ","将输入的 JSON 数据中,值为数字的 value 相加起来,并输出,只需返回输出结果。"],", ",["model",": ","claude-3-haiku-20240307"],", ",["max_tokens",": ","100"]],"]"]],[{error_info,#{module => erl_erts_errors}}]},{emqx_trace_formatter,format_term,2,[{file,"emqx_trace_formatter.erl"},{line,126}]},{emqx_logger_textfmt,format_term,2,[{file,"emqx_logger_textfmt.erl"},{line,230}]},{emqx_logger_textfmt,try_encode_meta,4,[{file,"emqx_logger_textfmt.erl"},{line,206}]},{lists,foldl_1,3,[{file,"lists.erl"},{line,2151}]},{emqx_logger_textfmt,enrich_report,3,[{file,"emqx_logger_textfmt.erl"},{line,102}]},{emqx_logger_textfmt,format,2,[{file,"emqx_logger_textfmt.erl"},{line,24}]}]}#16134 Fixed a backward compatibility issue that could prevent new Log Traces from being created in some cases.
Rate Limit
#16160 Improved the rate limiting algorithm for individual client connections. Previously, clients could temporarily exceed their publish rate limits, particularly just after connecting or after periods of inactivity.
This update makes the limiter behavior more predictable and consistent, ensuring rate limits are correctly enforced from the start of a connection.
6.0.0
Release Date: 2025-09-30
Make sure to check the breaking changes and known issues before upgrading to EMQX 6.0.0.
Feature Highlights
EMQX Enterprise 6.0.0 is the first release of the EMQX Enterprise version 6 series, bringing significant architectural improvements and new capabilities.
Message Queue
The native Message Queue feature unifies real-time MQTT publish/subscribe with persistent asynchronous queuing. The server buffers messages that match a topic filter, retaining them even when subscribers are offline. Clients can consume these messages through the special $q/{topic} topic, ensuring reliable message delivery.
Message Queues support offline message storage, last-value retention, and flexible dispatch strategies, enhancing MQTT with both real-time and durable messaging capabilities.
Namespace
The Namespace feature improves multi-tenancy and observability with namespace-level roles in the Dashboard. Users are restricted to their own resources (e.g., Rules, Actions, and Connectors) with fine-grained permissions such as Administrator or Viewer, and roles can be managed via the Dashboard, API, or CLI, simplifying multi-tenant operations.
Session count tracking has also been optimized: counts refresh on demand when there are fewer than 1,000 connections, and every 5 seconds otherwise. During rolling upgrades from older versions, counts may temporarily appear inconsistent, but will stabilize once all nodes are updated.
MQTT Durable Sessions
Durable storage has been optimized by separating session data from the broker’s other metadata, significantly reducing RAM usage and improving storage efficiency.
New configuration options provide finer control over RocksDB memory usage and performance. In addition, the default serialization schema for stored messages has been updated to ASN.1, further enhancing efficiency.
New Data Integrations
- Google BigQuery
- AWS AlloyDB
- CockroachDB
- AWS Redshift
Enhanced Integration
AWS:
- Support for Instance Metadata Service v2 APIs from EC2 instances when using S3 or S3Tables data integration. This enables seamless access to S3 buckets without manual AWS credential configuration, leveraging IAM roles for better security.
- Parquet format support for S3 Tables Action.
RabbitMQ: Define custom Headers and Properties Templates in RabbitMQ Sink to enhance message routing and compatibility within RabbitMQ.
Snowflake: Snowpipe Streaming upload mode for Snowflake Action (preview feature).
RocketMQ: New
keyandtagtemplate fields in Action, along with akey_dispatchoption for the Produce Strategy, allowing greater customization of message metadata.
Elixir Support
All packages now ship with Elixir support through the Mix build system, opening EMQX to the Elixir community and enabling better tooling with IEx console.
Enhanced LDAP Support
LDAP authorization now supports extended ACL rules in JSON format, and LDAP authentication can fetch ACL rules directly from LDAP with client-side caching.
Improved Tracing
Configurable limits for maximum traces (trace.max_traces) and trace file sizes (trace.max_file_size). After max_file_size is reached, the trace log will rotate to a new file instead of halting.
Cluster Management
New cluster.description configuration option allows users to set and display custom cluster descriptions in the EMQX Dashboard.
Enhancements
Message Queue
- #15789 Implemented Message Queues, which are collections of messages identified by
topic_filter. Each queue has an explicit lifecycle and is automatically replenished with published messages matched with the queue's topic filter during the queue's lifetime. Clients can cooperatively consume messages from a queue by subscribing to a special topic in the format:$q/{topic}.
Core MQTT Functionalities
- #15805 Introduced a dedicated worker pool for handling sharded fanout message delivery. Previously, the broker pool handled both subscription management and message dispatch, which could lead to scheduling contention. This change separates the fanout dispatch workload into its own pool to ensure more balanced and efficient handling of pub/sub operations.
Access Control
#15349 Optimize external resource management for authentication and authorization. Previously, EMQX could remain connected to a resource configured for a disabled authenticator or authorizer.
#15294 Enhanced LDAP authentication and authorization. LDAP authorization now supports extended ACL rules in JSON format. LDAP authentication can now fetch ACL rules from LDAP. These rules are cached in the client's metadata, so authorization is performed without additional LDAP queries.
#15730 Added support for overriding the client ID based on authentication results. If an authentication backend returns a
clientid_overrideattribute upon successful authentication, it will replace the client’s original client ID.The following backends now support
clientid_override:- HTTP
- JWT
- LDAP
- MongoDB
- MySQL
- Postgres
- Redis
#15820 Changed default value of config
authorization.no_matchfromallowtodenyfor better security defaults.
Clustering
- #15600 Introduced a new configuration option
cluster.descriptionthat allows you to add a descriptive label to the EMQX cluster. This description can be updated viaPUT /cluster, and retrieved with theGET /clusterAPI.
LLM-Based MQTT Data Processing
#15467 Exposed transport configuration options for AI Completion Providers. Users can now configure connection timeouts and the maximum number of connections to AI Completion Providers. This helps prevent
checkout_timeouterrors when message throughput is high and the provider is under load.Flow designer supports integrating with the Google Gemini model.
#15631 Added a new API endpoint to list all models available for an AI provider.
#15467 Exposed transport options for AI Completion Providers. These options allow configuring connection timeouts and maximum connections to an AI Completion Provider.
#15724 Introduced
openai_responsetype for AI Completion Providers and completion profiles to use OpenAI'sresponseAPI.
Data Integration
#15418 EMQX supports data integration with BigQuery.
#15401 Added support for the Snowpipe Streaming upload mode in the Snowflake Action. Note: Snowpipe Streaming is currently a preview feature and is only available for Snowflake accounts hosted on AWS.
#15387 Added rate limiting to Kinesis Producer Connector and Action health checks to comply with AWS API quotas and improve cluster behavior.
- Health check calls to
ListStreamsandDescribeStreamare now limited to 5/s and 10/s per Connector, respectively, matching AWS rate limits. - A distributed limiter is coordinated by a core node in the cluster to enforce these limits consistently.
- If a health check is throttled or times out, the Connector or Action will now retain its previous status instead of being marked as disconnected.
Also introduced a new
resource_opts.health_check_interval_jitter, which adds a uniform random delay toresource_opts.health_check_intervalto reduce the chance of multiple Actions under the same Connector running health checks at the same time.- Health check calls to
#15176 Upgraded the GreptimeDB Connector client and supported an optional new parameter
ttlto set the default time-to-live for automatically created tables.#15649 EMQX supports data integration with AWS AlloyDB, CockroachDB, and AWS Redshift.
#15635 Added new
keyandtagtemplate fields in the RocketMQ Action, allowing customization of the message's key and tag. Also, introduced a newkey_dispatchoption for theProduce Strategyfield.#15621 Now,
access_key_idandsecret_access_keyare optional fields for the S3 Tables Connector. If omitted, they'll be obtained from the Instance Metadata Service v2 APIs from the EC2 instance where EMQX is deployed.#15628 Removed HStreamDB data integration.
#15544 Added Arrow Flight SQL NIF driver support for Datalayers Integration.
#15637 Added support for templating message headers and properties for the RabbitMQ Action.
#15864 Removed the deprecated "Bridges V1" APIs and configuration schemas. All endpoints under
/bridges/*and configuration entries under thebridgesroot key are no longer available, as data integrations have fully migrated to the "Connectors/Actions/Sources" model.#15583 Updated the
brodclient to version 4.4.4, expanding support for a wider range of Kafka APIs. This update addresses the deprecation ofJoinGroupsAPI versionsv0-v1.
Smart Data Hub
- #15525 Prevented deletion of internal schemas that are still in use. If a schema is referenced by a Schema Validation or Message Transformation, it can no longer be removed to avoid runtime errors and configuration inconsistencies.
Durable Storage
#15463 Improved durable storage RAM usage and storage efficiency.
- Introduced the following configuration parameters for the durable storage to improve control over RocksDB memory usage and storage performance:
durable_storage.messages.rocksdb.write_buffer_size: RocksDB memtable size per shard.durable_storage.messages.rocksdb.cache_size: RocksDB block size per shard.durable_storage.messages.rocksdb.max_open_files: Limits the number of file descriptors used by RocksDB per shard.durable_storage.messages.layout.wildcard_thresholds: Allows to tune wildcard thresholds for thewildcard_optimized_v2storage layout.
- Additionally, the default
serialization_schemafor stored messages has been changed toasn1.
- Introduced the following configuration parameters for the durable storage to improve control over RocksDB memory usage and storage performance:
#16044 Some of config fields for durable sessions have been removed or renamed, and old values are marked as deprecated:
durable_sessions.heartbeat_intervalhas been renamed todurable_sessions.checkpoint_interval.durable_sessions.idle_poll_intervalanddurable_sessions.renew_streams_intervalhave been removed, as sessions are now fully event-driven.durable_sessions.session_gc_intervalanddurable_sessions.session_gc_batch_sizehave been removed as obsolete.
CLI
- #15399 The
node_dumptool now exports the current system configuration in HOCON format, with sensitive information (such as passwords and secrets) automatically redacted for security.
Namespace
#15841 Improved the refresh rate of the session count for namespaced sessions.
- If a namespace has fewer than 1000 connections, its session count is now updated on demand.
- For namespaces with 1000 or more connections, the count is updated every 5 seconds.
During a rolling upgrade from versions prior to 6.0, session counts may appear inconsistent due to changes in the internal tracking tables. This is expected: as clients reconnect to upgraded nodes, the session counts will gradually stabilize and become accurate once all nodes are running version 6.0 or later.
Observability
#15594 Introduced a new configuration option
trace.max_tracesto control the maximum number of active cluster-wide traces. This limit does not apply to node-local traces managed usingemqx ctl trace.This update also optimized tracing implementation to eliminate potential atom leaks per created trace.
#15556 Introduced a new configuration option
trace.max_file_sizeto limit the maximum file size for each individual trace.#15650 Implemented automatic trace log rotation.
When a trace file size exceeds
trace.max_file_size, EMQX no longer discards all subsequent events and emits an incomprehensible warning tostderr. Instead, portions of the oldest events are discarded while the most recent ones are retained.As such, this also implies that:
- EMQX now maintains multiple trace log files per active trace. The layout of the trace directory has changed accordingly.
- Trace API has been updated to reflect this behavior. The Log Stream API may return new errors, such as when a stream becomes stale due to a slow consumer.
#15904 Support viewing and updating of tracing configuration through Trace API.
Performance
- #15451 Introduced an experimental
socketbackend for TCP listeners, aimed at improving message processing latency and reducing compute resource usage. The feature can be enabled with the newtcp_backendlistener option.
Build and Tooling
- #15484 Switched the build system to Elixir's Mix, enabling all packages to include native Elixir support. This change improves developer tooling, allows integration with Elixir dependencies when needed, and enables use of the IEx shell as a more powerful EMQX console.
License
- #15921 Introduced a license alarm for cluster-wide maximum transactions per second (TPS).
- Each node calculates TPS as the average number of MQTT messages sent and received over the past 10 seconds.
- The total cluster TPS is aggregated every 5 seconds.
- If the observed TPS exceeds the licensed limit, an alarm is triggered.
- The alarm remains active until a license with a higher TPS allowance is applied.
MQTT over QUIC
- #15997 Added support for disabling QUIC stack loading by setting the environment variable
QUICER_SKIP_NIF_LOAD=1.
Bug Fixes
Core MQTT Functionalities
#15396 Removed redundant cleanup operations for shared subscriptions of disconnected clients. These operations were prone to crashes under high disconnect volumes and could lead to inconsistencies in the global broker state.
#15361 Fixed a
function_clauseerror when parsing a malformedUser-Propertypair with invalid (too short) length.#15783 Ensure that any changes to connection rate limits take effect immediately after the listener update has completed. Previously, parts of internal limiter state were not directly affected by configuration changes. For example, after increasing the burst rate, the effective rate limit could appear stricter than expected.
Access Control
- #15489 Fixed OIDC issuer URL validation in Single Sign-On (SSO) settings. Previously, issuer URLs containing a port number (for example,
https://xxxxxxxx:8443/webman/sso/.well-known/openid-configuration) were rejected with abad_port_numbererror. These URLs are now supported.
Rule Engine
- #15569 Fixed an issue where a Republish Rule Action could fail if the
direct_dispatchtemplate was empty or resolved to a non-boolean value. In these cases, the default valuefalseis now used.
Data Integration
- #15522 Fixed an issue where Snowflake Connector would fail to start correctly if
usernamewas not provided. - #15476 Fixed a missing callback in
emqx_connector_aggreg_deliverythat caused a crash when formatting delivery process status for aggregated-mode Actions (e.g., Azure Blob Storage, Snowflake, S3 Tables). This occurred during failures or when inspecting delivery processes withgen_server:format_status/1. The issue is now resolved, and more detailed delivery status information will be logged. - #15394 Fixed a rare race condition where Action metrics could become inconsistent due to unexpected asynchronous replies.
- #15647 Fixed an issue where a MongoDB Connector was marked as
Disconnectedif the MongoDB account specified in the connector configuration lacked privileges to performfindqueries on thefoocollection. - #15603 Fixed an issue in the MQTT bridge where a stale connection could be shown as
Connectedand would not automatically reconnect. - #15383 Fixed a potential resource leak in MQTT bridge. When a bridge failed to start, the topic index table was not properly cleaned up.
- #15786 Fixed a potential atom leak when probing RocketMQ Connectors.
- #15806 Improved validation for Oracle Actions during creation. Previously, in rare cases, an Action containing an invalid SQL statement could be added successfully.
- #15848 Improved error reporting for the Oracle Connector. When the connector becomes disconnected, its status now includes a more specific reason, making diagnostics easier.
- #15693 Fixed a resource leak in Postgres-based bridges. Under certain race conditions during pool initialization, deleting a Connector could leave its connection pool behind. This has been corrected to ensure connection pools are properly cleaned up.
- #15543 Fixed an issue in HTTP Server data integration when sending large payloads. If the payload size was 10 MB or more, the HTTP request could fail.
Smart Data Hub
#15839 Fixed an encoding issue with Protobuf schemas that use
map<_, _>fields. Previously, schemas containingmap<string, string>fields could fail to encode valid payloads, resulting in cryptic runtime errors.Example schema:
protobufsyntax = "proto3"; message test { map<string, string> args = 1; }Example rule:
sqlSELECT schema_encode('xxx', json_decode(payload), 'test') as protobuf_test FROM "t/#"Example payload failed to be encoded:
json{ "args": { "env": "stag" } }Previous error similar to:
2025-06-17T06:59:22.725785+00:00 [warning] tag: RULE_SQL_EXEC, clientid: c_emqx, msg: SELECT_clause_exception, reason: {error,{gpb_type_error,{bad_unicode_string,[{value,env},{path,"test.args.key"}]}},[{'$schema_parser_xxx',mk_type_error,3,[{file,"$schema_parser_xxx.erl"},{line,437}]},{'$schema_parser_xxx','-v_map<string,string>/3-lc$^0/1-0-',3,[{file,"$schema_parser_xxx.erl"},{line,429}]},{'$schema_parser_xxx','v_map<string,string>',3,[{file,"$schema_parser_xxx.erl"},{line,429}]},{'$schema_parser_xxx',v_msg_test,3,[{file,"$schema_parser_xxx.erl"},{line,404}]},{'$schema_parser_xxx',encode_msg,3,[{file,"$schema_parser_xxx.erl"},{line,73}]},{emqx_schema_registry_serde,with_serde,2,[{file,"emqx_schema_registry_serde.erl"},{line,212}]}...
Observability
#15931 Resolved a bug where spurious but harmless error logs could appear during node startup:
[error] Generic event handler emqx_alarm_handler crashed ... Reason: {aborted,{no_exists,[emqx_activated_alarm,runq_overload]}}#15973 Fixed a bug where an alarm activation timeout could crash the connection process under certain conditions.
MQTT over QUIC
- #15614 QUIC Listener: When TLS key logging (
SSLKEYLOGFILE) is enabled, EMQX now dumps TLS keys even if the handshake fails.
Clustering
- #16021 Fixed issues that occasionally prevented the DS Raft backend from functioning correctly when an existing node joined a new cluster and subsequently became member of DS replica sets.
Cluster Linking
- #15894 Previously, when listing all cluster links via
GET /cluster/links, disabled links would be returned having aninconsistentstatus. Now they are returned asdisconnected.
Performance
#15696 Added connection rate limiting support for WebSocket (WS) and WebSocket Secure (WSS) listeners. The
max_conn_rateandmax_conn_burstconfiguration options are now enforced: incoming connections exceeding the defined rate are immediately closed upon acceptance, consistent with existing TCP listener behavior.Additionally, the behavior of
max_connectionshas been updated. When the connection limit is exceeded, WS/WSS listeners now close connections immediately before any HTTP handshake, resulting in an abrupt socket close instead of returning an HTTP 429 response.#15854 Reduced the default
active_nvalue from100to10to improve MQTT client responsiveness, especially under high message rates with small payloads.The lower
active_nintroduces more backpressure at the TCP layer, stricter than the defaultReceive-Maximumof32, which helps in the following scenarios:- The client process is blocked by external authorization checks
- Data integration operations are delaying message handling
- The system is under heavy load or nearing resource limits
#15981 Prevented excessive memory growth caused by Mnesia transaction blocking during cleanup of large volumes of audit logs. This improves system stability and memory efficiency during heavy audit log maintenance operations.