In a previous tutorial, I described how to use TMUX and tmuxinator to create basic configurable TUIs for docker compose projects which display service logs and open shell sessions in separate windows/panes, as an alternative to the standard concantenated compose service log output.
One drawback of using TMUX for this is that it isn't particularly user friendly without messing with the TMUX configuration, even with tmuxinator helping to configure the layout.
This tutorial will show how to achieve a similar result (multiplexing docker compose up service logs, container shell sessions, and whatever else) using Zellij.
Zellij is a "Batteries Included" terminal multiplexer like TMUX which is both easy to configure and provides a much better user experience out-of-the-box.
Article Index
- Install Zellij and Clone the Tutorial Repo
- Starting the Docker Compose Zellij Session
- Configuring the Zellij Layout with KDL
- Fixing mouse selection copy-to-clipboard for some terminal emulators
- Resources and Links
Install Zellij and Clone the Tutorial Repo
Install Zellij. I used homebrew:
brew install zellijClone the tutorial repo and open it with
git clone https://github.com/moofoo/compose-zellij-tutorial.git && cd compose-zellij-tutorial
compose-zellij-tutorial should have the following files/directories:
|- apps
| |- web
| |- api
|- db
| |- 01_schema.sql
| |- 02_data.sql
|- docker-compose.yml
|- Dockerfile
|- zellij.kdl
|- README.md
|- up.sh
The tutorial Docker Compose project defines four services
- "web" runs a Vite dev server on http://localhost:5173
- "api" runs a NestJS dev server on http://localhost:3000
- "db" runs a Postgresql service
- "redis" runs (surprise) a Redis service.
The Zellij session layout has two tabs, each with four panes
-
Tab "Log"
- Logs from "web"
- Logs from "api"
- Logs from "db"
- Logs from "redis"
-
Tab "Shell"
- Shell session in "web" container
- Shell session in "api" container
- PSQL session connected to "db"
- NestJS REPL running in "api" container
Starting the Docker Compose Zellij Session
Here's the contents of the script up.sh, which starts docker compose and the Zellij session. Go ahead and run it with bash up.sh, if you like.
#!/bin/bash
# Start the docker compose project in detached mode, waiting until services are running before continuing script execution.
docker compose up --wait -d
# Run Zellij with the layout defined in `zellij.kdl`
zellij --layout zellij.kdl
#Stop docker compose after Zellij exits
docker compose stop
Simple enough.
Configuring the Zellij Layout with KDL
Zellij uses KDL for configuration.
Here's the contents of zellij.kdl, which defines the layout for the Compose/Zellij session:
/*
The following three settings ensure only a single Zellij session is created for the docker compose project
and that it won't stick around after exiting (normally or abnormally).
*/
session_name "compose-zellij-tutorial"
session_serialization false
on_force_close "quit"
layout {
/*
This defines the default layout for the two tabs in the layout ('Log' and 'Shell'), as well as any tabs created during the Zellij session.
Top-down it shows the tab bar, the currently active tab's contents with 'children' (the panes), and lastly the (very helpful) Zellij status bar.
*/
default_tab_template {
pane size=1 borderless=true {
plugin location="zellij:compact-bar"
}
children
pane size=1 borderless=true {
plugin location="zellij:status-bar"
}
}
// Log tab
tab name="Log" split_direction="horizontal" {
pane split_direction="vertical" {
// Web pane: docker compose logs --since=1s -f web
pane name="Web" command="docker" {
args "compose" "logs" "--since=1s" "-f" "web"
}
// API pane: docker compose logs --since=1s -f api
pane name="API" command="docker" {
args "compose" "logs" "--since=1s" "-f" "api"
}
}
pane split_direction="vertical" {
// DB pane: docker compose logs --since=1s -f db
pane name="DB" command="docker" {
args "compose" "logs" "--since=1s" "-f" "db"
}
// Redis pane: docker compose logs --since=1s -f redis
pane name="Redis" command="docker" {
args "compose" "logs" "--since=1s" "-f" "redis"
}
}
}
//Shell tab
tab name="Shell" split_direction="horizontal" {
pane split_direction="vertical" {
// Web pane: docker compose exec -w /usr/src/app web sh
pane name="Web" command="docker" {
args "compose" "exec" "-w" "/usr/src/app" "web" "sh"
}
// API pane: docker compose exec -w /usr/src/app api sh
pane name="API" command="docker" {
args "compose" "exec" "-w" "/usr/src/app" "api" "sh"
}
}
pane split_direction="vertical" {
// PSQL pane: docker compose exec db psql -U postgres -d tutorial_db
pane name="PSQL" command="docker" {
args "compose" "exec" "db" "psql" "-U" "postgres" "-d" "tutorial_db"
}
// NestJS Repl pane: docker compose exec -w /usr/src/app api npm run repl
pane name="NestJS Repl" command="docker" {
args "compose" "exec" "-w" "/usr/src/app" "api" "npm" "run" "repl"
}
}
}
}
And here's the result:
The "Log" tab:
The "Shell" tab:
And in action:
(Note that split_direction refers to the orientation of the gap between child panes. In other words, if you want the child panes within a container pane/tab to be arranged horizontally, you would set split_direction to vertical on the container)
Fixing mouse selection copying for some terminal emulators
Zellij has mouse-drag-select-to-copy functionality similar to TMUX, but it doesn't work with some terminal emulators. This was the case for me using GNOME terminal.
The way I fixed it was to first install xclip with sudo apt update && sudo apt install xclip
And then I added copy_command "xclip -selection clipboard" to the top of zellij.kdl (outside of the layout block)
For more info, see https://zellij.dev/documentation/faq.html#copy--paste-isnt-working-how-can-i-fix-this
Resources
Zellij Screencasts and Tutorials (the first two tutorials cover the basics of Zellij keyboard commands and interactivity)


Top comments (0)