Package Information
Documentation
n8n-nodes-tcp-duplex
Two-way TCP for n8n. Devices connect once and stay connected. Workflows read their messages and write back on the same connection. There's no dial-back, tunnel or port forwarding, and replies work through NATs and load balancers.
| Node | What it does |
|---|---|
| TCP Duplex Trigger | TCP server. Registers every connection under a connectionId, splits the stream into messages, and optionally captures a deviceId. Emits connect, data and disconnect events. |
| TCP Duplex | Send to one connection (by connectionId or deviceId), Broadcast to all, List Connections, or Disconnect. |
The two nodes share an in-memory registry of live sockets. That's how a Send in any workflow execution (a webhook, a schedule, a different workflow) can write to a socket the trigger owns.
Trigger output
{
"event": "data", // connect | data | disconnect
"connectionId": "a0273830-a8c0-…",
"deviceId": "dev01", // null until the Device ID Pattern matches
"data": "dev01: temp=21.5",
"bytes": 16,
"client": { "remoteAddress": "203.0.113.10", "remotePort": 2697 },
"server": { "host": "0.0.0.0", "port": 8150 },
"timestamp": "2026-09-23T17:12:03.120Z"
}
Trigger settings
- Framing: Newline (default, and
\r\nworks too), Null Byte, or Raw Chunks. TCP is a byte stream, so framing decides where each message ends. - Device ID Pattern: a regex tried on each message until one matches. Capture group 1 becomes the device ID. For example,
^([\w.-]+)[: ]turnsdev01: hellointodev01. If a device reconnects, its ID moves to the new connection. - TCP Keepalive: default 60s. Keeps NAT and NLB flows alive (an AWS NLB drops idle flows after about 350s) and detects dead peers.
- Idle Timeout: closes connections that stay silent too long. Off by default.
Examples
Echo on the same connection: TCP Duplex Trigger → IF event = data → TCP Duplex (Send, Connection ID {{ $json.connectionId }}, Message echo: {{ $json.data }})
Push a command from anywhere: Webhook → TCP Duplex (Send, Target By Device ID, {{ $json.body.deviceId }})
See test/test-workflow.json for both examples, plus a List webhook.
Limits
- Single n8n process only. The registry lives in n8n's memory. It works in the default (regular) execution mode. In queue mode (
EXECUTIONS_MODE=queue), Send runs on a worker that doesn't have the sockets and fails with "No open connection". - Redeploying drops every connection. Deactivating, re-saving or restarting the workflow (or n8n) closes all device connections, so devices must reconnect with backoff.
- No ordering guarantee: each message starts its own execution, and executions run concurrently, so replies to back-to-back messages can arrive out of order.
- One trigger per port. Two active workflows can't both listen on 8150.
Install
From the n8n web UI (needs an owner or admin login): go to Settings → Community Nodes → Install, enter n8n-nodes-tcp-duplex, accept the risk notice, and install. The nodes then show up as TCP Duplex Trigger and TCP Duplex.
From a shell on the n8n Docker host, without npm:
npm install && npm pack # -> n8n-nodes-tcp-duplex-<version>.tgz
docker cp n8n-nodes-tcp-duplex-*.tgz <n8n-container>:/tmp/
docker exec -u node <n8n-container> sh -c 'mkdir -p ~/.n8n/nodes && cd ~/.n8n/nodes && npm install /tmp/n8n-nodes-tcp-duplex-*.tgz'
docker restart <n8n-container>
~/.n8n has to be on a persistent volume (it is in the standard n8n setup).
n8n-workflow is an optional peer dependency. n8n supplies it at runtime, so npm doesn't install its own copy.
Publish a new version
npm version patch # or minor; bumps package.json
npm publish # builds first (prepublishOnly)
Then click Update next to the package in n8n's Community Nodes settings.
Develop and test
npm install && npm run build
python3 test/e2e.py [tcp_host tcp_port n8n_base_url] # defaults: 127.0.0.1 18150 http://localhost:5679
test/e2e.py expects test/test-workflow.json to be active. It checks echo on the same connection, framing across split and merged TCP writes, listing, pushing by device ID, the unknown-device error, and registry cleanup on disconnect.