#podman-container #docker #podman #detect #linux

detect-container

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

1 stable release

new 1.0.0 May 15, 2026

#13 in #podman-container

BSD-2-Clause

19KB
196 lines

detect-container

Crates.io Documentation 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

[dependencies]
detect-container = "0.1"

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

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.) 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(). 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:

[dependencies]
detect-container = { version = "1", features = ["diagnostics"] }
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:

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 for the full text.

No runtime deps

Features