Part I: The Premise — The Cattle Fallacy & Single-Host Reality
Do you ever catch yourself wishing that Docker containers simply behaved like lightweight virtual machines rather than disposable, single-process microservices?
For over a decade, the cloud-native norm has mandated the pets vs. cattle orthodoxy. Under this model, containers are expected to be immutable, ephemeral, and single-purpose. If you want to adjust an Apache virtual host, test a C compile toolchain, or tweak an environment configuration, the standard answer is heavy: rewrite a Dockerfile, trigger a rebuild, orchestrate multi-container topologies with Compose, and destroy the running instance.
While that paradigm thrives in auto-scaling cloud clusters, it introduces immense operational friction for single-node servers, homelabs, and self-hosted utilities. When hosting stable infrastructure—like a personal Git forge running Forgejo—full hypervisors (KVM, QEMU) carry steep memory and CPU translation overhead. Conversely, the microservice model turns standalone tools into multi-container orchestration sprawl.
Tux-Dock was built to explore the pragmatic middle ground: leveraging the near-zero overhead of Linux namespaces and cgroups, but approaching container lifecycle management through an interactive, persistent system-container model inspired by LXC/LXD.
| Repository & Mirrors | Access Link |
|---|---|
| GitHub Primary | github.com/MARKMENTAL/tuxdock |
| MentalNet Self-Hosted Git | mentalnet.xyz/forgejo-v2/markmental/tuxdock |
Architecture Reality & The Name: Tux + Terminal User Xperience
Before diving into the code, an important architectural clarification: Tux-Dock does not run hardware hypervisors or true virtual machines.
There is no QEMU, KVM, or hardware emulation running underneath. Tux-Dock provisions standard containers that share the host Linux kernel directly. Resource boundaries and process isolation are handled entirely through native kernel primitives: cgroups, namespaces (PID, mount, network, IPC, UTS), and chroot/pivot_root environments. The "micro-VM" terminology reflects the operational user experience—treating the container as a persistent, stateful micro-host rather than a throwaway process—with near-bare-metal performance.
What's in a Name?
When the project started, Tux-Dock was something I made up as an affectionate portmanteau of Tux (the classic Linux penguin mascot) and Docker—a nod to writing lean, native utilities for Linux sysadmins.
As the project evolved into a full functional-reactive terminal interface and I thought more about the name, it naturally took on a fitting dual identity:
- Tux + Dock: Rooted in core Linux systems engineering principles without extraneous abstractions.
- T.U.X. Dock: Terminal User Xperience Docker—a keyboard-driven, modal interface designed for direct agency in an SSH session without heavy web dashboards or browser overhead.
Tux-Dock Interface Preview
Figure 1: The FTXUI-powered terminal modal interface navigating active containers and image lifecycles.
Part II: The Journey — From a 77-Line Script to Native C++17 Socket IPC
Tux-Dock began in June 2024 as a humble 77-line Bash script (tux-dock.sh). It presented an interactive CLI menu wrapping standard docker commands to create containers, attach shells, and configure port mappings. It proved the workflow was fast and ergonomic for daily tasks, but quickly hit the hard performance and stability limits of shell scripting.
The Bottlenecks of Shelling Out
As features grew through late 2025 and 2026, executing commands via system() and popen() became unsustainable. Spawning subprocesses for every refresh loop added noticeable fork/exec latency, caused screen flicker, and required fragile string parsing of CLI stdout.
Eliminating Fork/Exec: Direct UNIX Domain Socket IPC
The entire application was rewritten in C++17 using FTXUI for terminal rendering and nlohmann/json for payload serialization. Instead of relying on the official Go SDK or shelling out to the docker-cli binary to check on a container's status, Tux-Dock communicates directly with the Docker Engine API over /var/run/docker.sock, while the docker-cli functionality is reserved to only where it is needed, for container creation and container interaction commands.
- Zero Subprocess Overhead: Bypasses CLI binary invocations, eliminating argument parsing and process initialization cycles.
- Bypassing the TCP Stack: Avoids
127.0.0.1loopback network stack traversal, routing tables, and socket port allocation. Communication is pure in-memory POSIX IPC buffer streaming. - The Daemon as the Database: Tux-Dock stays completely stateless. It does not need SQLite or a cache layer; the Docker daemon serves as the real-time, authoritative single source of truth.
| Evolutionary Phase | Core Stack | Communication Channel | State Tracking Strategy |
|---|---|---|---|
| Phase 1 (June 2024) | Bash Shell Script | Subprocess calls to docker-cli |
Ad-hoc text scraping from CLI stdout |
| Phase 2 (Late 2025) | Monolithic C++17 CLI | popen() and system() wrappers |
Stringstream parsing of stdout |
| Phase 3 (Early 2026) | C++17 + FTXUI TUI | fork() and execvp() process runners |
Modal orchestration over C++ structs |
| Phase 4 (v0.1.1-beta) | C++17 + Direct UNIX Socket | Raw HTTP/1.1 client over /var/run/docker.sock |
Authoritative JSON payloads deserialized into native models |
Under the Hood: Low-Level C++ Components
main.cpp (TuxDockApp modal orchestration & FTXUI rendering loop)
src/
├── docker_manager.cpp Container & image lifecycle management
├── docker_engine_client.cpp Raw HTTP/1.1 socket client over /var/run/docker.sock
├── http_response_parser.cpp Handles chunked transfer encoding, 204/304 statuses, & timeouts
├── container_parser.cpp Docker Engine API JSON -> ContainerInfo mapping
├── process_runner.cpp POSIX fork/exec with pipe capture & interactive TTY handoffs
├── operation_state.cpp Thread-safe busy modal state tracking & animation frames
└── stop_waiter.cpp Polled container shutdown verification with retry logic
tests/
├── Unit tests (HTTP parser, container JSON parser, stop sequences)
└── Integration tests (Automated Docker socket smoke tests & TUI transcript playback)
Part III: Operational Reality — Manual Maintenance vs. Unattended Automation
Adopting the system-container model shifts operational trade-offs. It gives you the longevity of a persistent environment without the hypervisor tax, but requires understanding how interactive sysadmin maintenance and background automation work together.
| Operational Aspect | Traditional Cloud-Native Containers | Tux-Dock Shared-Kernel Micro-VM Model |
|---|---|---|
| Kernel Boundary | Shared host kernel (ephemeral process namespace). | Shared host kernel (persistent system namespace). |
| PID 1 Architecture | Direct application binary (terminates container on exit). | Persistent keepalive (sleep infinity), keeping container active. |
| State & Persistence | Strictly immutable; all state offloaded to external volumes/DBs. | Stateful rootfs; installed packages and local configs persist. |
| Maintenance Burden | Re-run automated CI pipelines and rebuild base images. | Standard sysadmin upkeep (package upgrades, log rotation, backups). |
| Process Execution | Single declared entrypoint. | Interactive shell attachment or background detached command execution. |
Manual Sysadmin Upkeep
Because the container rootfs is persistent, you treat the container like a lean virtual private server. You shell in (Attach Shell to Running Container), run distribution package updates (e.g. apt-get update && apt-get upgrade), configure files in /etc, and manage running services. While this requires manual sysadmin responsibility, it eliminates the overhead of managing complex multi-stage Dockerfiles for simple, stable workloads.
Unattended Automation: Running Detached Scripts
Manual care does not preclude automation. Tux-Dock provides first-class support for executing background workloads via Menu Action 10: Run Detached Command in Container.
You can write initialization, backup, or maintenance scripts on your host or inside the container, triggering them asynchronously via non-blocking exec sessions:
# Example: Triggering a background maintenance script via Tux-Dock detached execution
/bin/sh -c "/app/scripts/backup-forgejo.sh > /var/log/backup.log 2>&1 &"
# The command executes inside the isolated namespace without blocking the TUI or holding a shell open.
This provides a clean operational balance: interactive terminal access when configuring services, and detached execution when long-running jobs need to run unattended.
Part IV: The Pipeline — Hardened POSIX Tooling & Zero Dependencies
A minimalist systems tool should not rely on heavy CI/CD frameworks. Tux-Dock's compilation, testing, and reporting infrastructure is implemented entirely within a portable POSIX /bin/sh script (compile.sh).
Hardware & Cgroup-Aware Resource Throttling
Compiling C++ templates on resource-constrained 512MB–1GB VPS nodes frequently triggers the Linux Out-Of-Memory (OOM) killer. compile.sh inspects both cgroup v1 and v2 memory limits (/sys/fs/cgroup/memory.max, memory.limit_in_bytes) alongside /proc/meminfo. If low RAM is detected, it automatically restricts build concurrency to -j1 and applies -DCMAKE_BUILD_TYPE=MinSizeRel.
Live Netcat CI Test Reports in HTML 3.2 Final
When run with --web-test-view, the build driver executes the full ctest suite, parses test transcripts using standard awk and sed, generates a structured HTML 3.2 Final report, and streams it over a raw socket using standard netcat / nc / ncat:
./compile.sh --web-test-view 8095
# Test report: http://0.0.0.0:8095/
# Zero external web servers, zero Python/Node dependencies — pure Unix pipeline
Test Suite Web View Preview
Figure 2: The HTML 3.2 test report served directly via Netcat, displaying CTest metrics and integration transcripts.
Building and Getting Started
Tux-Dock requires only a standard C++17 compiler, CMake 3.16+, and read/write access to /var/run/docker.sock.
Step 1 — Clone the Repository
git clone https://github.com/MARKMENTAL/tuxdock.git
cd tuxdock
Step 2 — Compile and Test
Compile the binary and execute unit and integration test suites:
./compile.sh
Compile the binary along with the HTML 3.2 web view of the testing suite:
./compile.sh --web-test-view
Or build the binary directly without running the test suite:
./compile.sh --no-test
Step 3 — Run Tux-Dock
sudo ./build/tux-dock
Tip: To run Tux-Dock without sudo, ensure your Linux user belongs to the docker group.