tcp-duplex

n8n TCP server trigger that keeps connections open, plus a node to write back on them (true two-way TCP).

Package Information

Released: this month
Downloads: 0 weekly / 0 monthly
Latest Version: 0.2.1
Author: Myxar

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\n works 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.-]+)[: ] turns dev01: hello into dev01. 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.

Discussion