MIT licensed · no agents · no persistence layer

Terminal output to the browser, in seconds.

rtail takes every line on stdin and broadcasts it over UDP. rtail-server, if it is listening, streams those lines straight into your browser — live, from as many machines as you like. That is the whole idea.

your machine
$ node server.js 2>&1 | rtail --id api.myproject.com

This is the app, running.

Not a screenshot. The frame below is the real rtail-server webapp — the same bundle, components and stylesheet the server ships — driven by a generated log source instead of a UDP socket, because this page is static. Click a stream, filter it, pause it, switch the theme.

localhost:8888

Three streams, a few lines a second. On a real server the streams are whatever you named your pipes, and the lines are whatever your processes printed.

Three moving parts.

There is no agent to install, no log format to adopt, and nothing to configure.

  1. 01

    Pipe

    Send any command's output into rtail. It passes every line through to stdout untouched, so redirects and existing log files keep working exactly as before.

  2. 02

    Broadcast

    Each line leaves as a single UDP datagram. Fire and forget: there is no handshake, no back-pressure, and if nothing is listening on the other end, your process neither blocks nor notices.

  3. 03

    Watch

    rtail-server receives the datagrams, groups them by stream id, and dispatches each one to the browser over socket.io. Open the page and the lines are already moving.

What you get.

ANSI colours survive the trip

Your grep --color output, your test runner's greens and reds, your framework's log levels — all rendered in the browser. Or strip them with --no-tty.

JSON and JSON5 lines

Print an object and it arrives parsed, pretty-printed and syntax highlighted, instead of as one long unreadable line. Unquoted keys and trailing commas are fine.

As many streams as you have machines

Name each pipe with --id and every host, service or environment gets its own entry in the sidebar. Star the ones you watch most.

A filter that understands the line

Terms are ANDed and - negates one. "quoted" matches a phrase, /regexp/ when you need one, and level:error or duration>250 address parsed JSON fields rather than the text around them. Scroll up and the stream pauses server-side, so a parked tab costs nothing.

Log-rotate safe

Pair it with tail -F and rotation is a non-event. Dates already in your lines are parsed out and used as the timestamp.

Small and forgettable

The webapp is Preact and TypeScript, about 33 KB gzipped. The server keeps a fixed ring buffer per stream and writes nothing to disk.

Get started.

Two pieces: a server that listens, and a client that pipes.

01 Run the server

It ships as a container image, which is the recommended way to run it. Port 8888 serves the webapp; 9999/udp receives the lines.

wherever you want to watch from
$ docker run -d --name rtail -p 8888:8888 -p 9999:9999/udp ghcr.io/kilianc/rtail

Then open localhost:8888.

02 Install the client

The client is a UNIX pipe, so it belongs on the host whose output you are tailing — not in the container. It needs Node.js 22.18 or newer: rtail ships as TypeScript and relies on Node stripping the types at load time, so what you read in cli/ is exactly what npm installs.

on each machine you tail
$ npm install -g rtail

03 Pipe something into it

Point the client at the host running the server with --host, name the stream with --id, and you are done.

anything that writes to stdout
$ node server.js 2>&1 | rtail --host logs.example.com --id api.myproject.com
$ tail -F log.txt | rtail --id nginx
$ echo "Server rebooted!" | rtail --id `hostname`