Mobile-first web terminal - a lightweight bastion with zsh and a full DevOps toolchain
938
A lightweight bastion you drive from your phone. A root shell in the browser with
zsh + oh-my-zsh, a hotkey bar built for touch, IME-safe input, and a full
DevOps toolchain preinstalled.
docker run -d --name web-terminal \
-p 127.0.0.1:7681:7681 \
-e WT_PASSWORD='choose-something-strong' \
-e WT_TZ=Asia/Ho_Chi_Minh \
-v wt-home:/root \
benakaben24/web-terminal
Then open http://127.0.0.1:7681. The port is bound to loopback on purpose —
put a VPN interface or a reverse proxy in front before reaching it from anywhere
else. $HOME lives in the wt-home volume, so shell history and dotfiles
survive a rebuild.
| Tag | What it is |
|---|---|
latest | newest build of master, manifest list covering both architectures |
amd64-latest / arm64-latest | pin a single architecture |
1.2.3, 1.2 | published from a v* git tag |
sha-<short> | one exact commit |
docker pull benakaben24/web-terminal resolves the right architecture by
itself, on an Ubuntu server and on an Apple Silicon Mac alike. Nothing 32-bit is
published.
Administering a server from a phone is normally miserable: no Ctrl, no Esc
and no arrow keys; scrolling back does nothing or walks your command history
instead; and IME keyboards mangle what you type, because they commit whole words
rather than keystrokes. This image owns the whole keyboard path — node-pty +
ws on the server, xterm.js driven directly in the browser — instead of
wrapping a terminal emulator and inheriting its limits.
vim/less/htop, or page
keys. Optional handling for shells running inside tmux.Put it on the one host allowed to reach your private network and the DevOps toolchain runs there, not on the phone. No credentials, no kubeconfig and no private keys reach the device, and there is no client to install beyond a browser — optionally saved to the home screen as a PWA.
Pair it with WireGuard or Tailscale. The terminal then listens on the VPN
interface only, so the login page is not reachable from the internet at all, and
authentication becomes two independent layers: the VPN's keys, then
WT_PASSWORD.
Terraform, Terramate, tflint, terraform-docs, kubectl, Helm, k9s, kubectx/kubens, aws-cli v2 with the Session Manager plugin, Go, Python, uv, Neovim, plus ripgrep, fd, bat, fzf, zoxide, yq, jq, nmap, tcpdump, tmux and the Docker CLI. Every tool is installed from its vendor's own release endpoint at a pinned version.
zsh with oh-my-zsh, autosuggestions, syntax highlighting and completions.
powerlevel10k is installed and a Nerd Font is bundled as a webfont, so a
powerline theme renders on a phone with no fonts installed.
The Docker CLI ships without an engine. Mount /var/run/docker.sock to drive
the host's daemon — and read the warning below before you do.
| Variable | Default | Meaning |
|---|---|---|
WT_PASSWORD | (empty) | Login password. Empty disables authentication entirely. |
WT_USERNAME | admin | Login name. |
WT_TITLE | Web Terminal | Tab title and login heading. |
WT_PORT | 7681 | Listen port. |
WT_HOST | 0.0.0.0 | Listen address. |
WT_BASE_PATH | (empty) | Serve under a sub-path, e.g. /terminal. |
WT_SHELL | /bin/zsh | Shell to spawn. |
WT_SHELL_ARGS | -l | Shell arguments; a login shell, so ~/.zshrc is read. |
WT_HOME | /root | Working directory for new sessions. |
WT_ZSH_THEME | robbyrussell | oh-my-zsh theme; powerlevel10k/powerlevel10k is bundled. |
WT_MAX_SESSIONS | 12 | Maximum concurrent shells. |
WT_SCROLLBACK_BYTES | 262144 | Replay buffer kept per session. |
WT_READONLY | false | Reject all input; view only. |
WT_ALLOW_EXEC | true | Allow creating new sessions. |
WT_TOKEN_TTL_MS | 604800000 | Login token lifetime, 7 days. |
WT_DEAD_SESSION_GRACE_MS | 300000 | How long an exited session stays listed. |
WT_PING_INTERVAL_MS | 25000 | WebSocket keepalive interval. |
WT_TRUST_PROXY | true | Honour X-Forwarded-* from a reverse proxy. |
WT_TZ | (unset) | Timezone inside the container, e.g. Asia/Ho_Chi_Minh. |
This is an unrestricted root shell. The supported deployment is behind a VPN. If you put it on a public address anyway:
WT_PASSWORD. An empty one hands a shell to anyone who reaches
the port./var/run/docker.sock if you need it. That socket is root on the
host and it escapes the container.Every push is scanned with Trivy, Hadolint, Gitleaks and npm audit. See
SECURITY.md,
which also explains why CVEs reported inside third-party Go binaries are not
fixable from this image.
Content type
Image
Digest
sha256:7aabc27ba…
Size
605.6 MB
Last updated
about 9 hours ago
docker pull benakaben24/web-terminal