|
|
||
|---|---|---|
| .claude | ||
| .forgejo/workflows | ||
| docker | ||
| docs | ||
| embedded_scripts | ||
| etc/zinit | ||
| examples | ||
| scripts | ||
| src | ||
| tests | ||
| .gitignore | ||
| build.rs | ||
| Cargo.lock | ||
| Cargo.toml | ||
| EMBEDDED_RHAI_SCRIPTS.md | ||
| Makefile | ||
| README.md | ||
zinit
A lightweight process supervisor with dependency management, similar to systemd but simpler.
Quick Install
Get started in one command:
curl -fsSL https://raw.githubusercontent.com/threefoldtech/zinit/main/scripts/install.sh | bash
Or download and run the installer script directly:
cd /tmp && curl -O https://raw.githubusercontent.com/threefoldtech/zinit/main/scripts/install.sh
chmod +x install.sh
./install.sh
This will:
- Detect your OS and architecture
- Download pre-built binaries (Linux amd64, macOS arm64)
- Install to
$HOME/hero/bin - Configure your shell PATH automatically
- Start
zinit-serverin background (macOS/Windows only)
Then use: zinit list, zinit status, zinit start <service>, etc.
For more options, see Quick Start section below.
Documentation
- Getting Started Tutorial: A step-by-step guide for new users.
- User Guide: Comprehensive guide to features, concepts, and configuration.
- Service Specification: Detailed reference for all TOML fields.
- Rhai Scripting Guide: Automation and orchestration with scripts.
- Internal Architecture: For developers interested in Zinit's internals.
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 -cchild 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-assignedclass=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
Installation (Recommended)
Download and install pre-built binaries:
./scripts/install.sh
This script:
- Detects your OS and architecture
- Downloads binaries from Forgejo registry
- Installs to
$HOME/hero/bin - Configures your PATH automatically
- On macOS/Windows, automatically starts the server in the background
Building from Source
# Full build with Makefile
make build
# Or manual build
cargo build --release --features full
cargo build --release --features client # CLI only
cargo build --release --features server # Server only
# 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
See scripts/README.md for detailed information about installation scripts and the Makefile for build targets.
Architecture
zinit-pid1 (PID 1 shim)
| spawns/monitors
v
zinit-server (daemon)
| unix socket
v
zinit (CLI/TUI)
Crate Structure
zinit is a single crate with feature flags:
src/
lib.rs # Library entry point
bin/
zinit.rs # CLI binary
zinit-server.rs # Server binary
zinit-pid1.rs # PID 1 binary (Linux only)
sdk/ # Shared types, config, protocol, clients
server/ # Supervisor, dependency graph, process management
client/ # CLI interface
Features
| Feature | Description |
|---|---|
client |
CLI interface |
server |
Supervisor daemon |
pid1 |
PID 1 init shim (Linux only) |
tui |
Terminal UI for client |
rhai |
Rhai scripting support |
async |
Async client support |
full |
Build everything |
Default features: client, server
Configuration
Service configs are TOML files in the config directory (default: /etc/zinit/services/).
For detailed service configuration defaults and specifications, see docs/SERVICE_SPECS.md
[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 runningstop: 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*_allcommandssystem: 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)
# Xinet socket activation proxy commands
zinit xinet set <name> # Create or update xinet proxy (replaces existing)
--listen <addr> # Listen address: host:port or unix:/path (repeatable)
--backend <addr> # Backend address: host:port or unix:/path
--service <name> # Zinit service to start on connection
[--connect-timeout <secs>] # Timeout for backend connect (default: 30)
[--idle-timeout <secs>] # Stop service after idle seconds (default: 0=never)
[--single-connection] # Allow only one connection at a time
zinit xinet delete <name> # Delete xinet proxy
zinit xinet list # List all xinet proxies
zinit xinet status [name] # Show proxy status (all if no name given)
# Debug commands
zinit debug-state # Full graph state dump
zinit debug-procs <name> # Process tree for a service
Xinet Socket Activation Proxy
Xinet enables on-demand service startup through socket activation. When a client connects to the proxy's listening socket, the proxy starts the backend service and forwards traffic.
Use Cases
- Databases: Start postgres on first query
- Development Servers: Start on HTTP request
- Backup Services: Start on trigger
- Rarely-Used Services: Reduce memory footprint
Example: PostgreSQL with Socket Activation
Create the backend service:
# /etc/zinit/services/postgres.toml
[service]
name = "postgres"
exec = "/usr/bin/postgres -D /var/lib/postgres"
status = "stop" # Don't autostart
[lifecycle]
start_timeout_ms = 5000
stop_timeout_ms = 10000
Register the proxy (starts postgres on first connection):
zinit xinet set postgres-proxy \
--listen tcp:localhost:5432 \
--backend unix:/tmp/postgres.sock \
--service postgres \
--connect-timeout 10 \
--idle-timeout 300 # Stop after 5 minutes idle
Now clients connect to localhost:5432 and postgres starts automatically.
Example: Multiple Listen Addresses
zinit xinet set postgres-proxy \
--listen tcp:0.0.0.0:5432 \
--listen unix:/run/postgres.sock \
--backend unix:/tmp/postgres.sock \
--service postgres
Proxy Features
- Bidirectional Forwarding: TCP ↔ TCP, Unix ↔ Unix, TCP ↔ Unix
- Auto-Start Backend: Starts service on first connection
- Idle Timeout: Automatically stops service after inactivity
- Connection Limits: Optional single-connection mode
- Replace Mode:
xinet setreplaces existing proxy (stops old one first) - Connection Stats: Track active connections and total traffic
Path Configuration
zinit uses platform-specific default paths:
Linux (System/PID1 mode)
- Config directory:
/etc/zinit/services - System services:
/etc/zinit/system(PID1 mode only) - Socket:
/run/zinit.sock
macOS / Windows (Standalone mode)
- Config directory:
$HOME/hero/cfg/zinit - Socket:
$HOME/hero/var/zinit.sock
You can override these with environment variables (see below).
See docs/PATHS.md for detailed path configuration documentation.
Environment Variables
| Variable | Default | Description |
|---|---|---|
ZINIT_LOG_LEVEL |
info |
Log level: trace, debug, info, warn, error |
ZINIT_CONFIG_DIR |
Platform-specific (see above) | Service config directory |
ZINIT_SOCKET |
Platform-specific (see above) | Unix socket path |
ZINIT_CONTAINER |
unset | If set, zinit-pid1 runs in container mode |
Example: Custom Paths
# Use custom config and socket directories
export ZINIT_CONFIG_DIR=/opt/my-services
export ZINIT_SOCKET=/tmp/my-zinit.sock
# Start server
zinit-server
# Connect with CLI
zinit list
Library Usage
zinit can be used as a library:
use zinit::{ZinitClient, ServiceConfig};
// Blocking client
let mut client = ZinitClient::connect_unix("/var/run/zinit.sock")?;
let services = client.list()?;
// Async client (requires "async" feature)
use zinit::AsyncZinitClient;
let mut client = AsyncZinitClient::connect_unix("/var/run/zinit.sock").await?;
let status = client.status("my-service").await?;
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 databasestops worker, then app, then database- Dependencies are NOT auto-stopped (other services may need them)
Development
cargo check --features full # Verify builds
cargo test --features full # Run unit tests
cargo build --release --features full # Build all binaries
cargo clippy --features full # Lint
# Run all tests (unit + integration + bash + rhai)
make test-all
# Run specific integration tests
make test-bash # Legacy bash-based tests
make test-rhai # New Rhai-based integration tests
# Playground
make play-tui # Launch TUI with sample services for manual testing
License
See LICENSE file.