Hello Toradex team,
I hope you are all doing great! And thank you for your support.
This is a follow-up to my topic Configration RAC from January 2024. At that time, a longer poll_timeout solved our problem on TorizonCore 6. We are now moving our fleet to Torizon OS 7, so we measured the idle data use of the Remote Access Client again. On Torizon OS 7 it is about twice the TorizonCore 6 value with the same poll_timeout, and we think we found why.
Hardware:
- SoM Verdin iMX8M Plus Quad 4GB WB IT V1.1A
- Custom Carrier Board based on Verdin
- Cellular modem SIMCom SIM7600G-H (ModemManager, QMI), on a 100 MB/month data plan
uname:
- Torizon OS 7:
6.6.142-7.7.0 #1-Torizon SMP PREEMPT Fri Jun 19 08:16:23 UTC 2026 aarch64 - TorizonCore 6:
5.15.209-6.8.9+git.3342fc7bf0a9 #1-TorizonCore SMP PREEMPT Thu Jun 18 14:03:37 UTC 2026 aarch64
Images tested:
torizon-docker-verdin-imx8mp-Tezi_7.7.0+build.40.tar, customized with TorizonCore Builder. Remote access clientRAC/0.1.0 (395ff18)torizon-core-docker-verdin-imx8mp-Tezi_6.8.9+build.46.tar, customized with TorizonCore Builder. Remote access client at15845bc
RAC settings (/etc/rac/client.toml):
- Both images:
poll_timeout = { secs = 300, nanos = 0 } - Torizon OS 7 only:
validation_poll_timeout = { secs = 30, nanos = 0 },enable_dbus_client = false, andtzn-mqtt.servicemasked (see the D-Bus section below)
Issue:
With no remote session open, RAC on Torizon OS 7 uses about twice the data of TorizonCore 6:
| 60 min, 12 polls, no session | Torizon OS 7.7.0 | TorizonCore 6.8.9 |
|---|---|---|
Bytes to and from dgw.torizon.io |
120,959 | 59,066 |
| Per poll | ~10.1 KB | ~4.9 KB |
| Per month (30 days) | 87 MB | 42.5 MB |
Each poll also needs a DNS lookup of dgw.torizon.io: its TTL is 60 s, shorter than the poll interval. One lookup (A + AAAA) is 344 bytes, so about 3 MB/month more on both.
So on a 100 MB/month plan, RAC alone takes about 90 % of the budget on Torizon OS 7.
How we measured:
- iptables counters (IP packet bytes) on all traffic to and from the addresses of
dgw.torizon.io(2 IPv4 + 1 IPv6), read every 10 s for 60 min. aktualizr-torizonstopped during the window, because it talks to the same host. No remote session open.- The two halves of the Torizon OS 7 window agree: 87.4 and 86.6 MB/month.
What we see at each poll:
- Every poll opens new TCP + TLS connections. About 60 s later, the gateway closes them. The packet pattern (1 packet out, 2 packets in per connection) shows the server closes first, and the client is not the cause: RAC keeps the reqwest default pool idle timeout, which is 90 s.
- Torizon OS 7 closes two connections per poll (104 bytes out, 208 bytes in). TorizonCore 6 closes one (52 bytes out, 104 bytes in, on IPv4).
- The upload per poll doubles too: 2.9 to 4.3 KB on Torizon OS 7, against 1.5 to 2.4 KB on TorizonCore 6. The gateway asks for the client certificate, so every new connection sends it again.
What we found in the torizon/rac source (at 395ff18, also the current main):
- Since “Add support for remote commands” (35cb7b5, PRs #21 and #23),
src/main.rsstarts two poll loops:start_ssh_event_loop(GET ras/sessions) andstart_remote_commands_event_loop(GET ras/commands). - Both go through
start_poll_loopwith the samepoll_timeout. Both poll once at start, then reset their timer after each poll, so they fire at the same time. - The gateway negotiates HTTP/1.1 only (ALPN
http/1.1, also whenh2is offered). So two requests at the same time need two connections. With the 60 s idle close, every poll pays two full TLS handshakes. - There is no option to turn off the remote-commands loop, or to give it its own interval.
What we tested with the D-Bus “poll now”:
tzn-mqtt would let us poll less often, but its MQTT keepalive is hardcoded at 5 s (src/main.rs), and we measured it at about 115 MB/month on its own. So we keep it masked, and we tried to send the “poll now” ourselves:
- RAC connects with
Connection::session()(src/dbus.rs), so we gave it a private bus through a drop-in:DBUS_SESSION_BUS_ADDRESS=unix:path=/run/..., withenable_dbus_client = true. - Our sender owns
io.torizon.TznServiceon that bus and emitsTznMessageSig("PollRasNow", "{}")on/io/torizon/TznService, interfaceio.torizon.TznService1. - Result: RAC logs
received signal in dbus. command=PollRasNowand both loops poll in the same second. A session that we requested in the Web UI, and that had waited more than 2 minutes, was taken in the same second, and the tunnel was up 6 s later. - A signal from a connection that does not own
io.torizon.TznServiceis ignored, as expected from the proxy’s match rule.
Questions:
- Could both requests share one connection, for example by polling commands right after sessions in one loop? This alone would halve the idle data.
- Could you add an option to turn off the remote-commands loop (or a separate interval for it), for devices that do not use remote commands?
- Does the gateway support TLS session resumption, so a new connection does not send the full certificates again? Is the 60 s idle timeout of
dgw.torizon.iofixed? - Is sending
PollRasNowourselves, as above, a supported use of this interface? Can we expect the signal, its path and its bus name to stay stable in later RAC versions? - As far as we can see, both
racandtzn-mqtt(src/dbus.rs) use the D-Bus session bus, and the stockremote-access.serviceandtzn-mqtt.servicedo not setDBUS_SESSION_BUS_ADDRESS. How is the push meant to reach RAC on a stock image? Did we miss something? - Could the MQTT keepalive of
tzn-mqttbecome configurable? With a long keepalive, the push would be usable on metered links.
Our plan for now: a long poll_timeout on every device, and our own PollRasNow sender. It runs when the device is on Wi-Fi or Ethernet, and on demand when a support session is needed: our application already keeps its own cloud connection, so it can trigger the poll at no extra idle cost.
I can share the measurement script, the test sender and the per-poll logs if useful.
Best regards,
M