<div align="right">
<span style="color:#999;">🇺🇸 English</span> ·
</div>
<h1 align="center"><code>tino</code></h1>
<p align=center>
tino is a tiny init process (PID 1) for Docker/Kubernetes containers, written in Rust —
a modern alternative to <a href="https://github.com/krallin/tini">tini</a>.
</p>
<div align="center">
[](https://crates.io/crates/tino)
[](https://github.com/lvillis/tino)
[](https://github.com/lvillis/tino/actions)
[](https://hub.docker.com/r/lvillis/tino)
[](https://hub.docker.com/r/lvillis/tino)
[](mailto:lvillis@outlook.com?subject=Thanks%20for%20tino!)
</div>
---
## ✨ Features
| **Pure Rust, static** | No runtime deps, musl-linked binary ≤ 60 kB |
| **Sub-reaper mode** | `-s` flag enables `PR_SET_CHILD_SUBREAPER`, reaps orphaned children |
| **Parent-death signal** | `-p <SIG>` mirrors `tini -p` (`PR_SET_PDEATHSIG`) |
| **Signal forwarding** | Forwards most signals; `-g` mode falls back gracefully if PGID can't be assigned |
| **Graceful shutdown** | `SIGTERM → configurable wait → SIGKILL`; timeout set via `-t/--grace-ms` |
| **Exit-code remap** | `-e <code>` maps specific child exit codes to zero for health-checks |
| **Verbosity control** | `-v/-vv/-vvv` or `TINI_VERBOSITY=1..3` via `tracing` |
| **Security-audited** | `#![deny(unsafe_op_in_unsafe_fn)]`, minimal unsafe surface, no dynamic allocation in hot paths |
| **Cross-platform** | Linux glibc / musl; works as PID 1 in Docker, LXC, Podman, Kubernetes, fire-cracker, etc. |
| **Env overrides** | `TINI_SUBREAPER`, `TINI_KILL_PROCESS_GROUP`, `TINI_VERBOSITY` act as defaults (CLI wins) |
| **Landlock sandbox** | `--landlock` restricts filesystem writes to allowlisted directories (Linux; may need seccomp) |
## 📦 Installation
```bash
# Install locally with Cargo
cargo install tino
# Build a static binary (e.g. for PID 1 in Docker)
cargo build --release --target x86_64-unknown-linux-musl
# Docker image (includes /sbin/tino)
docker pull lvillis/tino
```
## 🚀 Quick Start
```bash
# Replace tini in your Dockerfile
ENTRYPOINT ["/sbin/tino", "-g", "-s", "--"]
# Run locally
tino -- echo "hello from child"
```
## 🧭 Runtime Notes
- `-g/--pgroup-kill` logs a warning and falls back to single-process signalling when process-group
creation fails (for example inside constrained PID namespaces).
- tino's internal signalfd is opened with `CLOEXEC`, ensuring child workloads do not inherit extra
file descriptors.
- Logging setup is idempotent: repeated initialisation (tests, embedding) no longer panics.
- `TINI_*` env vars only apply when the corresponding CLI flag is not set (CLI wins).
- Landlock (optional, Linux): `--landlock --landlock-writable /path` (repeatable) or
`--landlock-profile file` (one path per line) denies filesystem writes outside allowlisted
directories; default is strict (use `--landlock-warn-only` to continue).
- Landlock keeps `/dev` writable for TTY/stdout by default (disable with `--landlock-no-dev`).
- Docker: if Landlock syscalls are blocked, use `--security-opt seccomp=./seccomp-landlock.json`
(or `seccomp=unconfined` for testing).
## 🛡️ Landlock + Docker (seccomp)
Docker's default seccomp profile often blocks `landlock_*` syscalls. This repo includes
`seccomp-landlock.json`, based on `moby/profiles` (see `seccomp-landlock.upstream.sha`).
```bash
docker run --rm -it \
--security-opt seccomp=./seccomp-landlock.json \
<image> \
/sbin/tino --landlock --landlock-writable /data -- <cmd> ...
```
To make this the default for all containers, set Docker's daemon config:
```json
{ "seccomp-profile": "/etc/docker/seccomp-landlock.json" }
```
Refresh the profile with `python scripts/update-seccomp-landlock.py`.
## 🧪 Testing
```bash
cargo fmt --all -- --check
cargo clippy --all-targets --all-features -- -D warnings
cargo test --all --verbose
```
On Unix targets an integration suite in `tests/unix_behaviour.rs` covers the CLI licence output,
missing-command error path, and exit-code remapping flow.