No description
Find a file
Jan De Landtsheer 9f95053460
Some checks failed
Build Linux (musl) / build-linux (linux-amd64-musl, x86_64-unknown-linux-musl) (push) Successful in 1m3s
Test / test (push) Failing after 1m25s
style: apply cargo fmt formatting
2026-01-10 12:07:23 +01:00
.forgejo/workflows fix(ci): source cargo env before rustup components 2026-01-09 10:57:38 +01:00
docker Set default log level to debug in Docker image 2026-01-05 12:56:43 +01:00
docs feat: add bulk operations (start_all, stop_all, delete_all) to clients and Rhai 2026-01-09 14:00:41 +01:00
etc/zinit feat: add @builtin: service support and integrate sysvol 2026-01-07 16:03:02 +01:00
scripts docs: reorganize documentation structure 2026-01-05 23:53:37 +01:00
sysvol fix: resolve clippy warnings 2026-01-09 13:27:40 +01:00
tests/integration fix: add BulkStartResult/BulkStopResult types and fix bulk ops tests 2026-01-09 13:40:43 +01:00
zinit-client feat: add syslog receiver for capturing logs from traditional daemons 2026-01-09 19:31:16 +01:00
zinit-common feat: add syslog receiver for capturing logs from traditional daemons 2026-01-09 19:31:16 +01:00
zinit-pid1 style: apply cargo fmt formatting 2026-01-09 13:22:31 +01:00
zinit-server style: apply cargo fmt formatting 2026-01-10 12:07:23 +01:00
.gitignore Add .gitignore for Rust project 2026-01-02 16:14:32 +01:00
Cargo.lock feat: add integration tests and CI workflow 2026-01-09 10:18:13 +01:00
Cargo.toml fix: correct ratatui dependency name typo in workspace 2026-01-09 10:03:48 +01:00
CLAUDE.md docs: update CLAUDE.md with current architecture and doc structure 2026-01-05 23:58:29 +01:00
originalgap.txt fix: prevent services with MissingDependency from starting + fix debug shell 2026-01-06 13:29:29 +01:00
README.md docs: add clear deployment modes section to README 2026-01-05 20:28:19 +01:00

zinit

A lightweight process supervisor with dependency management, similar to systemd but simpler.

Features

  • Dependency Graph: Services declare dependencies (requires, after, wants, conflicts)
  • State Machine: 7 explicit states (Inactive, Blocked, Starting, Running, Stopping, Exited, Failed)
  • Process Groups: Signals sent to process groups, handling sh -c child processes correctly
  • Health Checks: TCP, HTTP, and exec-based health checks with retries
  • Ordered Shutdown: Dependents stop before their dependencies
  • Hot Reload: Reload configuration without full restart
  • Multi-Environment: Works in containers, VMs, and bare-metal

Deployment Modes

zinit adapts its behavior based on deployment environment:

Container Mode

Use zinit-pid1 as your container entrypoint:

ENTRYPOINT ["/usr/bin/zinit-pid1", "--container"]

Or set the environment variable:

ZINIT_CONTAINER=1 zinit-pid1

Behavior:

  • Loads services from /etc/zinit/services/
  • Clean exit on shutdown (no reboot syscall)
  • No system services directory

VM / Bare-Metal Mode

Use zinit-pid1 as your init system (PID 1):

# In /sbin/init or kernel cmdline: init=/usr/bin/zinit-pid1

Behavior:

  • Loads system services from /etc/zinit/system/ first (auto-assigned class=system)
  • Loads user services from /etc/zinit/services/ second
  • Handles reboot/poweroff via syscalls (SIGINT=reboot, SIGTERM=poweroff)
  • Never exits (kernel panic prevention)

Standalone Mode

Run zinit-server directly (not as PID 1):

zinit-server --config-dir /etc/zinit/services

Optionally enable system services directory:

zinit-server --config-dir /etc/zinit/services --pid1-mode

Quick Start

# Build
cargo build --release --workspace

# Run the server
zinit-server --config-dir /etc/zinit/services

# Use the CLI
zinit list
zinit status my-service
zinit start my-service
zinit stop my-service

Architecture

zinit-pid1 (PID 1 shim)
    │ spawns/monitors
    ▼
zinit-server (daemon)
    │ unix socket
    ▼
zinit-client (CLI/TUI)
Crate Purpose
zinit-common Shared types, configs, RPC protocol, blocking client
zinit-server Main daemon: supervisor, dependency graph, process management
zinit-client CLI interface (optional TUI with --features tui)
zinit-pid1 PID 1 init shim for containers/VMs

Configuration

Service configs are TOML files in the config directory (default: /etc/zinit/services/):

[service]
name = "my-app"
exec = "/usr/bin/my-app --daemon"
dir = "/var/lib/my-app"     # optional working directory
oneshot = false              # exit after success (default: false)
status = "start"             # start | stop | ignore (default: start)
class = "user"               # user | system (default: user)

[dependencies]
requires = ["database"]      # must be running
after = ["logger"]           # start order only
wants = ["metrics"]          # soft dependency
conflicts = ["legacy-app"]   # mutual exclusion

[lifecycle]
restart = "on-failure"       # always | on-failure | never
stop_signal = "SIGTERM"
start_timeout_ms = 30000
stop_timeout_ms = 10000
restart_delay_ms = 1000
restart_delay_max_ms = 60000
max_restarts = 0             # 0 = unlimited

[health]
type = "http"
endpoint = "http://localhost:8080/health"
interval_ms = 10000
retries = 3

[logging]
buffer_lines = 1000

Targets

Virtual services for grouping:

[target]
name = "multi-user"

[dependencies]
requires = ["network", "logger", "database"]

Service Status

The status field controls supervisor behavior:

  • start (default): Automatically start and keep running
  • stop: Keep stopped (won't auto-start)
  • ignore: Supervisor ignores this service

Service Class

The class field protects critical services from bulk operations:

  • user (default): Normal service, affected by *_all commands
  • system: Protected service, skipped by bulk operations

System-class services are immune to start_all, stop_all, and delete_all commands.

CLI Commands

zinit list                    # List all services
zinit status <name>           # Show service status
zinit start <name>            # Start a service
zinit stop <name>             # Stop (cascades to dependents)
zinit restart <name>          # Restart a service
zinit kill <name> [signal]    # Send signal to service
zinit logs <name> [-n N]      # View service logs
zinit why <name>              # Show why service is blocked
zinit tree                    # Show dependency tree
zinit reload                  # Reload configuration
zinit add-service <toml>      # Add service at runtime
zinit remove-service <name>   # Remove a service
zinit start-all               # Start all user-class services
zinit stop-all                # Stop all user-class services
zinit delete-all              # Delete all user-class services
zinit shutdown                # Stop all services, exit daemon
zinit poweroff                # Power off system (signals init)
zinit reboot                  # Reboot system (signals init)

# Debug commands
zinit debug-state             # Full graph state dump
zinit debug-procs <name>      # Process tree for a service

Environment Variables

Variable Default Description
ZINIT_LOG_LEVEL info Log level: trace, debug, info, warn, error
ZINIT_CONFIG_DIR /etc/zinit/services Service config directory
ZINIT_SOCKET /var/run/zinit.sock Unix socket path
ZINIT_CONTAINER unset If set, zinit-pid1 runs in container mode

Docker Usage

# Build test image
docker build -t zinit-test -f docker/Dockerfile .

# Run (uses container mode automatically)
docker run -it --rm zinit-test

# With debug logging
docker run -it --rm -e ZINIT_LOG_LEVEL=debug zinit-test

# Explicit container mode
docker run -it --rm -e ZINIT_CONTAINER=1 zinit-test

Shutdown Ordering

Services are stopped in reverse dependency order:

Example: database <- app <- worker

Startup order:   database -> app -> worker
Shutdown order:  worker -> app -> database

When stopping a single service, dependents are stopped first:

  • zinit stop database stops worker, then app, then database
  • Dependencies are NOT auto-stopped (other services may need them)

Development

cargo check --workspace           # Verify builds
cargo test --workspace            # Run tests
cargo build --release --workspace # Build all binaries
cargo clippy --workspace          # Lint

License

See LICENSE file.