I need a flipchart or a whiteboard while teaching, but during online sessions I resort to using a terminal and a text editor and quickly got tired of having to swap sharing different windows in Zoom, Big Blue Button, & co. (I share individual windows, not the whole screen.) I share a window of a Web browser with my presentation in one tab and an open terminal using gotty in the second, and can flip between the two with CMD-1 / CMD-2.

screenshot of browser with two tabs

I stumbled over gotty some years ago, and have used it to enable users to login to systems if they can’t SSH in (think corporate firewalls). It works well and is reliable. During the new setup I’m doing for training machines I need one gotty per user which is easily accomplished using distinct TCP port numbers. My first attempt in launching them was by templating out an rc.local script, but I wanted something more elegant.

I recalled that systemd services can take an argument via the service@argument syntax which fits in well with my user numbering, and after a bit of experimentation came up with a solution which works well.

Description=GoTTY Web Terminal %i


ExecStart=/usr/local/sbin/gotty -p 91%i tmux new-session -A -s user%i


The unit file’s WorkingDirectory is needed or gotty starts in /, and the tmux invocation is to automatically start or attach to tmux in an SSH session. Users can SSH into the machine and/or use gotty and see the same screen. More importantly, I can, once given consent by a student, login and see their screen, help with error messages, give tips, etc.

Each user’s home directory has a ~/.gotty preferences file containing

address = ""
credential = "username:password"
title_format = "Ansible:username"
permit_write = true
preferences {
    font_size = 20
    background_color = "rgb(255, 255, 255)"
    foreground_color = "rgb(0, 0, 0)"

The required number of gotty sessions are launched during setup of the machine with Ansible. (The sequence lookup produces a list of numbers 00, 01, 02 based on the number of users I expect to welcome.)

- name: "Gotty: start gotty services"
  systemd: name="gotty@{{ item }}" enabled=true state=started
  with_sequence: '{{ nusers }}'

This is the resulting process list:

$ ps aux|grep g[o]tty
user01      1209  ... /usr/local/sbin/gotty -p 9101 tmux new-session -A -s user01
user00      1244  ... /usr/local/sbin/gotty -p 9100 tmux new-session -A -s user00

Each gotty listens on the loopback interface. A templated-out configuration lets a TLS-protected nginx reverse proxy talk to the gottys.

location /tty00/ {
	proxy_set_header X-Real-IP $remote_addr;
	proxy_set_header X-Forwarded-For $remote_addr;
	proxy_set_header Host $http_host;
	rewrite ^/tty00/?$ / break;
	rewrite ^/tty00/(.*)$ /$1 break;
	proxy_http_version 1.1;
	proxy_set_header Upgrade $http_upgrade;
	proxy_set_header Connection "upgrade";

It’s only just now while writing this, that I realize GoTTY, which means “tty thing written in Go” actually reminds me of getty(8). :-)


  • There’s a newer and maintained version at https://github.com/sorenisanerd/gotty. The only way I can get it to understand my font/colour preferences is if I configure term = "hterm", but that, precisely, is what this fork appears to be dropping in favor of xterm.

terminals :: 03 May 2022 :: e-mail