# detect-container
[](https://crates.io/crates/detect-container)
[](https://docs.rs/detect-container)
[](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.