Orchestral

Overview

Orchestral is a Docker container orchestrator written in Rust. It has four parts: a daemon, an HTTP API, a CLI, and a terminal UI in the style of k9s or lazydocker. The daemon schedules containerized tasks on local resource capacity. The HTTP API controls the daemon. The terminal UI shows the work in real time.

Phase 1 covers one node and the local Docker daemon only. This scope lets the project get the task lifecycle, the scheduler, and the persistence model right before it adds multi-node coordination.

Motivation

Large orchestrators such as Kubernetes solve problems that most local and small-scale workflows do not have — multi-node clusters, distributed consensus, pluggable networking.

Orchestral tests a different question: what does a minimal, correct, single-node orchestrator look like? The project builds four parts from first principles: a task lifecycle state machine, a scheduler with explicit resource accounting, a runtime layer over Docker, and a live event stream. It does this without the operational weight of a full cluster manager.

Building it also gives practice with real distributed-systems problems at a small scale — crash-consistent persistence, reconciliation after a restart, push-based state sync. At this scale, each problem stays small enough to reason about fully.

Features

Implementation Notes

The project models the task lifecycle as an explicit state machine:

Pending -> Scheduled -> Starting -> Running -> Stopping -> Stopped

Two states branch off this main path. Cancelled can follow Pending or Scheduled. Failed can follow Starting or Running. This one state machine is the source of truth. The manager’s transition logic and the TUI’s rendering both follow it.

The project separates the manager, the worker, and the daemon into distinct parts:

store is the persistence layer below these parts. manager writes to the sled database at each point where task state already changes. daemon loads saved state and reconciles it with Docker on startup.

The CLI and the TUI share one api_client. This keeps the HTTP logic for talking to the daemon in one place. As the API grows, this shared client keeps the CLI and the TUI consistent with each other.

The project has 37 automated tests. None of these tests need Docker.

The docker_runtime and worker code paths need a real container runtime, so they have no automated tests yet. To test this code, run the daemon and submit real tasks by hand.

Roadmap