One of the first things I do every morning at work is open a few terminals. I use Terminator, a terminal emulator for Linux that splits one window into several terminals. A saved arrangement of splits is called a layout, and Terminator can open a layout directly at startup.
The first time, I built my layout from the preferences window. It works, but the generated configuration is full of fields you don't need (uuid, size, position, last_active_term). Writing layouts by hand turned out to be shorter and easier to change, and everything can be done from the terminal. Here are the layouts I tested with Terminator 2.1.6, from two to five terminals.
How a layout works
A layout is a tree:
- a
Windowat the root; - splits inside it:
HPanedputs two children side by side,VPanedstacks them one above the other; -
Terminalas the leaves.
Each element has a name (any name you like, as long as it is unique within the layout), a type and a parent. The two children of a split also have an order: 0 is the left or top one, 1 the right or bottom one. Without other options each split is 50/50, and ratio changes it.
Layouts live in ~/.config/terminator/config, under the [layouts] section. If you have never changed Terminator's preferences, the file doesn't exist yet:
sudo apt install terminator
mkdir -p ~/.config/terminator
nano ~/.config/terminator/config
The layouts below go under [layouts]. If the file already exists, add them to that section instead of creating a second one.
Two terminals side by side
+--------+------------------+
| | |
| left | right |
| | |
+--------+------------------+
[layouts]
[[two]]
[[[window]]]
type = Window
parent = ""
[[[split]]]
type = HPaned
parent = window
ratio = 0.3
[[[left]]]
type = Terminal
parent = split
order = 0
[[[right]]]
type = Terminal
parent = split
order = 1
With ratio = 0.3 the left terminal takes 30% of the width. Remove the line for two equal halves.
Two columns, the first split in two
+-------------+-------------+
| top left | |
+-------------+ right |
| bottom left | |
+-------------+-------------+
[[three]]
[[[window]]]
type = Window
parent = ""
[[[columns]]]
type = HPaned
parent = window
[[[left_split]]]
type = VPaned
parent = columns
order = 0
[[[top_left]]]
type = Terminal
parent = left_split
order = 0
[[[bottom_left]]]
type = Terminal
parent = left_split
order = 1
[[[right]]]
type = Terminal
parent = columns
order = 1
The first child of columns is not a terminal but another split, and that is all it takes to nest them.
A grid of four
+-------------+-------------+
| | |
+-------------+-------------+
| | |
+-------------+-------------+
[[grid]]
[[[window]]]
type = Window
parent = ""
maximised = True
[[[columns]]]
type = HPaned
parent = window
[[[left]]]
type = VPaned
parent = columns
order = 0
[[[right]]]
type = VPaned
parent = columns
order = 1
[[[t1]]]
type = Terminal
parent = left
order = 0
[[[t2]]]
type = Terminal
parent = left
order = 1
[[[t3]]]
type = Terminal
parent = right
order = 0
[[[t4]]]
type = Terminal
parent = right
order = 1
This is the layout I open every morning. maximised = True makes the window fill the screen.
A spiral of five
This one follows a geometric rule: split the screen in two side by side, then split the second half again, alternating the direction each time.
+-----------+-----------------+
| | 2 |
| +--------+--------+
| 1 | | 4 |
| | 3 +--------+
| | | 5 |
+-----------+--------+--------+
The terminals get smaller at each step, so the font gets smaller with them. The font belongs to a profile, and each terminal can use a different one. Profiles go under [profiles]:
[profiles]
[[large]]
font = Monospace 12
use_system_font = False
[[medium]]
font = Monospace 10
use_system_font = False
[[small]]
font = Monospace 8
use_system_font = False
[[spiral]]
[[[window]]]
type = Window
parent = ""
[[[s1]]]
type = HPaned
parent = window
[[[t1]]]
type = Terminal
parent = s1
order = 0
profile = large
[[[s2]]]
type = VPaned
parent = s1
order = 1
[[[t2]]]
type = Terminal
parent = s2
order = 0
profile = medium
[[[s3]]]
type = HPaned
parent = s2
order = 1
[[[t3]]]
type = Terminal
parent = s3
order = 0
profile = medium
[[[s4]]]
type = VPaned
parent = s3
order = 1
[[[t4]]]
type = Terminal
parent = s4
order = 0
profile = small
[[[t5]]]
type = Terminal
parent = s4
order = 1
profile = small
Each split is the second child (order = 1) of the previous one, and the type alternates between HPaned and VPaned. The first terminal, the largest, uses large, the next two medium and the last two small.
Fonts and colors per terminal
A profile can also change the font family and the colors, so the two smallest terminals can look different from the others. For them I use Terminus, a monospace font designed to stay sharp at small sizes, and a dark green background:
sudo apt install fonts-terminus
fc-list : family | grep -i terminus
On Debian the package installs the TTF version, and the family name is Terminus (TTF). With just Terminus, Terminator doesn't find the font and falls back to another one with badly spaced letters, so check the exact name with fc-list. The small profile becomes:
[[small]]
font = Terminus (TTF) 9
use_system_font = False
background_color = "#0f2a1d"
foreground_color = "#9fe8b0"
A different background helps to recognize a terminal at a glance, for example the ones following logs. foreground_color only changes plain text: the colors of the prompt (user in green, path in blue) come from bash's PS1, not from Terminator.
Opening a layout
terminator --list-layouts # check that Terminator reads them
terminator --layout=grid
If a Terminator window is already open, the new command doesn't start a new program: by default Terminator runs as a single instance, and the request goes to the instance already running. That instance still has the configuration it read at startup, so a layout added afterwards is unknown to it, and you get a window with a single terminal. Close all Terminator windows before trying a new layout, or start a separate instance with -u (--no-dbus):
terminator -u --layout=grid
A command in each terminal
Each terminal can start with a command, for example a connection to a server:
[[[t1]]]
type = Terminal
parent = left
order = 0
command = "ssh admin@server01; exec bash"
The ; exec bash part matters: without it the terminal closes as soon as the command ends, for example when the SSH session drops. With it, you are left with a local shell in the same terminal.
A launcher for GNOME
The command is already a single line, so in the terminal an alias is enough:
echo "alias tgrid='terminator --layout=grid'" >> ~/.bashrc
To open it from the GNOME menu or the dock, I create a .desktop file:
mkdir -p ~/.local/share/applications
cat > ~/.local/share/applications/terminator-grid.desktop <<'EOF'
[Desktop Entry]
Type=Application
Name=Terminator (grid)
Comment=Terminator with four terminals
Exec=terminator --layout=grid
Icon=terminator
Terminal=false
Categories=System;TerminalEmulator;
EOF
desktop-file-validate ~/.local/share/applications/terminator-grid.desktop checks the file. The launcher then appears in the activities search under "Terminator (grid)", and from there you can pin it to the dock. For more layouts, one file per layout with a different Name and Exec.
One launcher for all layouts
With several layouts, one file each gets tedious. A .desktop file can also declare actions: GNOME shows them when you right-click the icon, in the dock or in the applications list. So one launcher is enough, with an action for each layout, and a small script can generate it from Terminator's configuration:
#!/usr/bin/env bash
# Creates a GNOME launcher for Terminator with one action per configured layout.
# The actions appear when you right-click the icon (dock or applications list).
# Run it again after adding or removing a layout.
set -euo pipefail
dest="${XDG_DATA_HOME:-$HOME/.local/share}/applications/terminator-layouts.desktop"
mkdir -p "$(dirname "$dest")"
mapfile -t layouts < <(terminator --list-layouts 2>/dev/null | grep -v '^default$')
{
echo "[Desktop Entry]"
echo "Type=Application"
echo "Name=Terminator layouts"
echo "Comment=Terminator with a saved layout"
echo "Exec=terminator -u"
echo "Icon=terminator"
echo "Terminal=false"
echo "Categories=System;TerminalEmulator;"
# action identifiers may only contain letters, digits and hyphens
ids=()
for l in "${layouts[@]}"; do ids+=("$(printf '%s' "$l" | tr -c 'A-Za-z0-9-' '-')"); done
echo "Actions=$(printf '%s;' "${ids[@]}")"
for i in "${!layouts[@]}"; do
l="${layouts[$i]}"
echo
echo "[Desktop Action ${ids[$i]}]"
echo "Name=$l"
echo "Exec=terminator -u --layout=$l"
done
} > "$dest"
desktop-file-validate "$dest"
echo "Created $dest with ${#layouts[@]} layouts: ${layouts[*]}"
I saved it as ~/bin/terminator-layouts. A normal click opens a plain Terminator, a right-click lists the layouts. Every action uses -u, so the layout opens even when another Terminator window is already running with an older configuration.
A dashboard layout
The spiral turned out to be a good shape for a monitoring dashboard: one large terminal for the overview and smaller ones for details:
-
btop, the overview of CPU, memory, disks, network and processes, in the large terminal; -
atop, detailed system metrics, with one line per CPU core; -
wavemon, Wi-Fi link quality and signal level; -
nvtop, the GPU (AMD, Intel or Nvidia); -
nload, network traffic in and out.
sudo apt install btop atop wavemon nvtop nload fonts-unifont
Two more profiles. retro uses GNU Unifont, a pixel font that covers almost all of Unicode, so btop's graphs keep every symbol and get an old-computer look. It stays sharp at 12 points, a multiple of its pixel grid. tiny is the green Terminus profile at 7 points, for the two smallest terminals:
[[retro]]
font = Unifont 12
use_system_font = False
[[tiny]]
font = Terminus (TTF) 7
use_system_font = False
background_color = "#0f2a1d"
foreground_color = "#9fe8b0"
The layout is the spiral, with a title for each terminal and a command:
[[dashboard]]
[[[window]]]
type = Window
parent = ""
maximised = True
[[[s1]]]
type = HPaned
parent = window
[[[btop]]]
type = Terminal
parent = s1
order = 0
profile = retro
title = btop
command = "btop; exec bash"
[[[s2]]]
type = VPaned
parent = s1
order = 1
[[[atop]]]
type = Terminal
parent = s2
order = 0
profile = small
title = atop
command = "atop; exec bash"
[[[s3]]]
type = HPaned
parent = s2
order = 1
[[[wifi]]]
type = Terminal
parent = s3
order = 0
profile = small
title = wifi
command = "wavemon; exec bash"
[[[s4]]]
type = VPaned
parent = s3
order = 1
[[[gpu]]]
type = Terminal
parent = s4
order = 0
profile = tiny
title = gpu
command = "nvtop; exec bash"
[[[net]]]
type = Terminal
parent = s4
order = 1
profile = tiny
title = network
command = "nload wlp2s0; exec bash"
A few things I learned while putting it together:
- the font size decides whether a program starts at all:
atoprefuses to run in less than 60 columns by 24 lines, so it needs the larger top-right terminal or a small font; -
nloadwithout arguments picks the first network device, which on my laptop is an idle Docker bridge.wlp2s0is my Wi-Fi interface:ip -br linklists yours; - without
title, some terminals show "None" in their title bar; - not every console program behaves in Terminator.
s-tui, which I tried first for CPU temperature and frequency, filled its terminal with control sequences instead of graphs, so I left it out.
Other tools
Terminator splits a window on your desktop. If you also need splits on a remote server, or sessions that survive a disconnection, the tool is a terminal multiplexer like tmux or Zellij, which work inside any terminal, including over SSH. Zellij describes layouts in files, similar to what I did here.

Top comments (0)