At a glance
ttyd bashserves a terminal on port 7681, and each browser tab gets its own process.- It is read-only by default since 1.7.4;
-Wlets visitors type. - It listens on all interfaces with no password, so use
-i 127.0.0.1,-cand TLS. - To show a job that is already running, point ttyd at
tmux attach -r.
Install ttyd
The current release is ttyd 1.7.7, from March 30, 2024. On macOS run brew install ttyd; on Windows, winget install tsl0922.ttyd or scoop install ttyd.
On Ubuntu, sudo apt install ttyd installs it from universe: Ubuntu 26.04 ships 1.7.7 and 24.04 ships 1.7.4. Debian 13 stable has no ttyd package (testing and unstable do), so use sudo snap install ttyd --classic or a static binary from the GitHub releases page, then check it with ttyd --version.
Share a terminal in the browser
ttyd bash serves bash at http://SERVER:7681, and every browser connection gets a fresh bash of its own: in our test, two tabs got two different process IDs. Port 7681 is the default; -p 8080 picks another and -p 0 a random free one, which ttyd prints at startup.
Since ttyd 1.7.4 (October 2023) the page is read-only by default. Visitors see the output but cannot type, and ttyd logs The --writable option is not set, will start in readonly mode; add -W (--writable) to let them type.
ttyd bash # read-only, http://SERVER:7681
ttyd -W bash # visitors can type
ttyd -p 0 htop # random free portttyd keeps running until you stop it. -o makes it exit after one client disconnects, and -q when the last client leaves.
Lock it down: address, password and TLS
ttyd 1.7.7 listens on all interfaces (0.0.0.0) with no password, so anyone who can reach the port can open the page. ttyd -W bash on a public address hands a shell as your user to whoever finds it, and even read-only mode shows every secret printed to the screen.
Bind it to localhost with -i 127.0.0.1 (or -i lo, or a UNIX socket path) and reach it through an SSH tunnel. -c user:password adds HTTP basic auth and the page returns 401 without it, but basic auth is only base64 encoding, so add TLS with -S, -C and -K.
ttyd -i 127.0.0.1 bash
ssh -L 7681:127.0.0.1:7681 user@server # then open http://localhost:7681
ttyd -c alice:s3cret -S -C cert.pem -K key.pem bashWith TLS on, plain HTTP requests got a redirect to HTTPS in our test, and only the right credentials got a 200. -A with a CA file requires client certificates, and -O rejects WebSocket connections from other origins.
| Flag | What it does | Default |
|---|---|---|
-p, --port | Port to listen on; 0 picks a random port | 7681 |
-i, --interface | Interface or address to bind, or a UNIX socket path | All interfaces |
-W, --writable | Let visitors type | Off (read-only) |
-c, --credential | Basic auth as user:password | None |
-S, -C, -K | TLS on, certificate file, key file | Off |
-A, --ssl-ca | CA file for client certificates | None |
-H, --auth-header | Trust a header set by an authenticating proxy | None |
-b, --base-path | Path prefix a proxy passes through, up to 128 characters | None |
-O, --check-origin | Reject WebSocket connections from other origins | Off |
-m, --max-clients | Maximum number of clients | 0 (no limit) |
-o, --once | Accept one client, exit when it disconnects | Off |
-q, --exit-no-conn | Exit when the last client disconnects | Off |
-a, --url-arg | Let the URL pass arguments to the command, risky with a shell | Off |
-P, --ping-interval | WebSocket ping interval in seconds | 5 |
Put ttyd behind a reverse proxy
With nginx or another proxy in front, keep ttyd on 127.0.0.1 and let the proxy handle TLS. This is the ttyd wiki's nginx example; the Upgrade and Connection headers carry the WebSocket the terminal runs over.
location / {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://127.0.0.1:7681;
}If the proxy serves ttyd under a path such as /ttyd/ and passes that path through, start ttyd with -b /ttyd. If the proxy already logs users in, -H X-Auth-User (or whichever header it sets) makes ttyd require that header: in our test requests without it got 407. Anyone who reaches ttyd directly can send the header too, so keep it on 127.0.0.1.
Watch a long job without giving anyone a shell
Because each browser tab gets its own process, ttyd bash cannot show a job you already started. Run the job in tmux and serve a read-only tmux client instead.
tmux new -d -s train
tmux send-keys -t train 'python3 -u train.py' Enter
ttyd -i 127.0.0.1 tmux attach -r -t trainIn our test the browser showed the job's output live, typed input never reached it, and the session kept running after the tab closed. The wiki's ttyd tmux new -A -s ttyd vim shares one session between every tab and your own terminal instead. The tmux detach and attach guide covers the session side, and tmate alternatives compares other ways to share a terminal.
Skip the port, the proxy and the shell
A browser terminal means an open port, a password and a certificate to look after, even when all you want is to see how one job is doing.
FAQ
Is ttyd safe to expose to the internet?
Not with its defaults: ttyd 1.7.7 listens on every interface with no password and no TLS. Keep it on 127.0.0.1 behind a reverse proxy or an SSH tunnel, or turn on -c with -S, -C and -K, and leave -W off unless visitors need to type.
What port does ttyd use by default?
Port 7681. Change it with -p, or pass -p 0 for a random free port, which ttyd prints when it starts.
How do I make ttyd writable?
Start it with -W (--writable), as in ttyd -W bash. Since 1.7.4, ttyd starts read-only unless that flag is set.
Can several people watch the same ttyd terminal?
Not with ttyd bash, because each browser connection starts its own process. Serve a tmux session instead: ttyd tmux attach -r -t train to watch a running job, or ttyd -W tmux new -A -s ttyd to share one writable session.
What is the difference between ttyd and GoTTY?
Both serve a terminal over the web. The original GoTTY (yudai/gotty) has had no commits since December 2017; the maintained fork sorenisanerd/gotty released v1.10.0 in October 2026, listens on port 8080 and is read-only unless you pass -w.
Check on long runs from your phone
Change one import and every tqdm bar prints a link you can open on any device. Get a Discord or Slack message when the run finishes, fails or stalls.
pip install tqdm-liveSources
- ttyd on GitHub github.com
- ttyd 1.7.4 release notes github.com
- ttyd 1.7.7 release github.com
- ttyd wiki: Nginx reverse proxy github.com
- ttyd wiki: Example usage github.com
- Ubuntu ttyd package launchpad.net
- GoTTY fork by sorenisanerd github.com