detect-container 1.0.0

Detect whether the current process is running inside a container (Docker, Podman, etc.).
Documentation
# detect-container

[![Crates.io](https://img.shields.io/crates/v/detect-container.svg)](https://crates.io/crates/detect-container)
[![Documentation](https://docs.rs/detect-container/badge.svg)](https://docs.rs/detect-container)
[![License](https://img.shields.io/crates/l/detect-container.svg)](https://github.com/ogital-net/detect-container/blob/main/LICENSE)

A small, dependency-free Rust library for detecting whether the current
process is running inside a Linux container (Docker, Podman,
containerd, Kubernetes, LXC, WSL1, …).

The detection avoids touching paths under `/run`, `/`, or any other
location an image author can populate at build time. Every signal it
consults comes from the kernel, the container runtime's cgroup
plumbing, the process's own PID, or `/proc/sys/kernel/osrelease`.

## Important assumption

**This crate must not be used to author an init system, embedded
init, initramfs, or anything else that legitimately runs as
PID 1 on the host kernel.** The algorithm treats `getpid() == 1` as a
positive container signal because, in every realistic application
context, a process being PID 1 means it is the entry point of a
container. If your code might run as PID 1 on bare metal it will
incorrectly report `true`.

## Features

- Zero runtime dependencies.
- Thread-safe; the result is computed once and cached.
- Detects Docker, Podman, containerd, Kubernetes, LXC, WSL1, and similar runtimes.
- Does not consult any user-writable filesystem path
  (`/.dockerenv`, `/run/.containerenv`, etc.) — those can be spoofed
  by a hostile or careless image.
- Platform-aware: full detection on Linux, graceful no-op (`false`) elsewhere.
- `#![forbid(unsafe_code)]`.
- Optional `diagnostics` feature for inspecting which checks fired.

## Installation

```toml
[dependencies]
detect-container = "0.1"
```

The library import path is `detect_container` (Rust crate names use
underscores):

```rust
use detect_container::is_container;

fn main() {
    if is_container() {
        println!("running inside a container");
    } else {
        println!("running on a regular host");
    }
}
```

## Detection algorithm

On Linux the following steps are performed in order. The first one
that produces a definite answer wins.

1. **Read `/proc/self/ns/pid`'s inode** and remember it.
   - If the inode equals `PROC_PID_INIT_INO` (`0xEFFFFFFC`, the
     hardcoded inode of the root PID namespace since Linux 3.8)
     → return `false`. We are in the host's root PID namespace and
     therefore not in a container.
2. **`getpid() == 1`** → return `true`.
   See the assumption above: PID 1 outside the host root namespace
   means we are the container's init.
3. **WSL1 probe** — read `/proc/sys/kernel/osrelease`; if it contains
   `Microsoft` or `WSL` **but not** `WSL2` → return `true`. (The
   [official WSL detection method]https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364.)
   WSL2 is intentionally excluded: it runs a real Linux kernel inside
   a lightweight Hyper-V VM, so it is a virtual machine rather than a
   container.
4. **cgroup v1 probe** — read `/proc/1/cgroup` and `/proc/self/cgroup`;
   if either references a known runtime substring (`docker`,
   `containerd`, `kubepods`, `lxc`, `podman`, `garden`) → return
   `true`. Useful on older kernels where cgroup paths still encode
   the runtime name.
5. **Fallback to the cached PID-namespace inode**: if it is anything
   other than `PROC_PID_INIT_INO` (i.e. we are in a child PID
   namespace) → return `true`.
6. Otherwise → return `false`.

This is intentionally narrower than systemd's
[`detect_container()`](https://github.com/systemd/systemd/blob/main/src/basic/virt.c).
Systemd has to identify *which* container manager is in use, so it
must consult `/.dockerenv`, `/run/.containerenv`,
`/run/host/container-manager`, `/run/systemd/container`, and the
process's `$container` environment variable. We only answer the
yes/no question, so we can rely on signals an image author can't
forge.

On any non-Linux target, `is_container()` always returns `false`.

## Caching

The result is computed on first use and cached for the lifetime of the
process in a single relaxed `AtomicU8`. The detection is pure and
idempotent, so the cache uses relaxed atomic operations without
locking; concurrent first calls may each run the detection but will
agree on the result. Subsequent calls are a single relaxed atomic
load.

## Diagnostics

Enable the `diagnostics` feature to inspect every step of the
algorithm without short-circuiting:

```toml
[dependencies]
detect-container = { version = "1", features = ["diagnostics"] }
```

```rust,ignore
let report = detect_container::diagnostics::report();
println!("in container: {}", report.is_container);
for c in &report.checks {
    println!("{:<24} matched={}  {}", c.name, c.matched, c.description);
}
```

A ready-to-run example is included:

```text
cargo run --example report --features diagnostics
```

This is intended for tests and debugging only; the `diagnostics`
module is **not** part of the stable API surface and may change
between minor versions.

## Minimum supported Rust version

`detect-container` requires Rust **1.64** or newer.

## License

Licensed under the BSD 2-Clause License. See
[LICENSE](https://github.com/ogital-net/detect-container/blob/main/LICENSE)
for the full text.