Inspect Linux Namespaces Safely with lsns

lsns lists every namespace your shell can see, in one read-only pass. It is the first thing to reach for when a container looks isolated in ways you did not expect, and it can also narrow down to the namespaces held by a single process. Nothing here creates, joins, destroys or reconfigures a namespace.

Allow about ten minutes. You need a Linux shell with lsns installed. The normal checks are read-only and do not need sudo. Root access can reveal more of /proc, but it is not a substitute for checking which process or namespace you meant to inspect in the first place.

1. Confirm which lsns you are running

Start with the executable, version and help text. This matters because util-linux releases add options and columns over time, and the default display is explicitly not a stable interface.

$ command -v lsns
/usr/bin/lsns
$ lsns --version
lsns from util-linux 2.39.3
$ lsns --help

Your paths and version may differ. On this host, the distribution package is util-linux 2.39.3. If another installation appears first in PATH, its version and supported options are the ones that apply. Check every candidate before you rely on anything:

$ type -a lsns
lsns is /usr/bin/lsns
$ dpkg-query -W -f='${Package} ${Version}\n' util-linux
util-linux 2.39.3-9ubuntu6.6

Checkpoint: stop here if the command is missing, or if its version does not match the documentation you are using. Run lsns --help on the actual binary before copying an option into a script.

2. Take a first read-only listing

With no namespace argument, lsns lists the currently accessible namespaces. Each row is a namespace instance, identified by an inode number in the NS column.

$ lsns | head -9
        NS TYPE   NPROCS     PID USER COMMAND
4026531834 time       87    1998 alice tmux -u -2 ...
4026531835 cgroup     87    1998 alice tmux -u -2 ...
4026531836 pid        87    1998 alice tmux -u -2 ...
4026531837 user      132    1998 alice tmux -u -2 ...
4026531838 uts        87    1998 alice tmux -u -2 ...
4026531839 ipc        87    1998 alice tmux -u -2 ...
4026531840 net        87    1998 alice tmux -u -2 ...
4026531841 mnt        87    1998 alice tmux -u -2 ...

The exact rows are host-specific. The namespace types supported by this release are mnt, net, ipc, user, pid, uts, cgroup and time. NPROCS is a count, while PID is the lowest process ID found in that namespace. Do not treat the displayed command as a unique namespace name.

Two defaults cause avoidable confusion: text can be truncated to fit the terminal, and the default columns or layout can change between releases. Fine for a glance. Not fine for a report or a script, where you should pick the layout and columns yourself.

3. Select columns for a stable report

Use --list and --output when you want one row per namespace with a small, explicit set of fields:

$ lsns --list --output NS,TYPE,PATH,COMMAND | head -8
        NS TYPE   PATH                    COMMAND
4026531834 time   /proc/1998/ns/time      tmux -u -2 ...
4026531835 cgroup /proc/1998/ns/cgroup    tmux -u -2 ...
4026531836 pid    /proc/1998/ns/pid       tmux -u -2 ...
4026531837 user   /proc/1998/ns/user      tmux -u -2 ...
4026531838 uts    /proc/1998/ns/uts       tmux -u -2 ...
4026531839 ipc    /proc/1998/ns/ipc       tmux -u -2 ...
4026531840 net    /proc/1998/ns/net       tmux -u -2 ...

Ask the installed command for the complete column vocabulary with lsns --list-columns. Useful fields include NS, TYPE, PATH, NPROCS, PID, USER, COMMAND, NSFS, PNS and ONS. A column list is not a guarantee every value will be present: a namespace can be visible while its process information is limited.

If truncation would hide evidence, add --notruncate. If you need machine-readable data, --json is available, but still choose the columns you depend on and test the exact util-linux release in your environment.

4. Inspect the namespaces of one process

Use --task with a PID when the question is about one process rather than the whole host. The shell variable below supplies the current shell's own PID, so you are not guessing a host-specific number:

$ lsns --task "$$" --list --output NS,TYPE,PATH,COMMAND
        NS TYPE   PATH                 COMMAND
4026531834 time   /proc/1998/ns/time   tmux -u -2 ...
4026531835 cgroup /proc/1998/ns/cgroup tmux -u -2 ...
4026531836 pid    /proc/1998/ns/pid    tmux -u -2 ...
4026531837 user   /proc/1998/ns/user   tmux -u -2 ...
4026531838 uts    /proc/1998/ns/uts    tmux -u -2 ...
4026531839 ipc    /proc/1998/ns/ipc    tmux -u -2 ...
4026531840 net    /proc/1998/ns/net    tmux -u -2 ...

Compare the NS values for two processes to see which namespace instances they share. A different process can have the same inode for one type and a different inode for another. That is normal: namespace isolation is per type, not one all-or-nothing switch.

To inspect one type across the host, repeat --type as needed:

$ lsns --type net --list --output NS,TYPE,PATH,NSFS
        NS TYPE PATH                    NSFS
4026531840 net  /proc/1998/ns/net      /run/docker/netns/default

Network namespace rows can contain multi-line NSFS cells. Add --nowrap when a single-line, comma-separated representation is easier to capture:

$ lsns --type net --nowrap --list --output NS,TYPE,NSFS,PATH

5. Handle incomplete visibility without guessing

lsns reads directly from /proc. An unprivileged user can therefore see incomplete information, and the current /proc view can itself be shaped by a PID namespace. If a process you expect is absent, confirm the PID and the proc mount you are viewing before doing anything else:

$ test -r /proc/PROCESS_ID/ns/net && echo namespace-link-readable
$ readlink /proc/PROCESS_ID/ns/net
net:[4026531840]

Replace PROCESS_ID with digits from a trusted process listing. Reading these paths is safe. Do not restart a service or alter mounts just because one listing came back shorter than expected. If policy allows it, compare the result with an elevated read-only invocation:

$ sudo lsns --task PROCESS_ID --list --output NS,TYPE,PATH
# enter your normal administrative password only if prompted

Using sudo changes who reads /proc, not what namespaces exist. A difference between the two runs is evidence of a visibility or permission boundary, and should be recorded as such rather than explained away.

Persistent namespaces are another trap. --persistent shows namespaces without processes, created by bind-mounting a /proc/PID/ns/TYPE file to a filesystem path. A namespace held by such a mount may not show up in lsns unless you have a process or accessible mount path tying it to the current /proc view.

6. Use the namespace inode as an identifier, not a command

The number in NS is an inode number: not a PID, and not a durable name to carry between boots. It is useful for comparing rows within one investigation. Do not build a long-lived inventory that assumes a particular inode will always mean the same isolation boundary.

For a script, avoid parsing the default output. Request a layout and columns explicitly, suppress headings if you like, and handle a non-zero exit status:

$ lsns --list --noheadings --output NS,TYPE,PID
$ status=$?
$ test "$status" -eq 0 || printf 'lsns failed with status %s\n' "$status" >&2

The documented exit statuses are 0 for success, 1 for a general error and 2 when an ioctl is unknown to the kernel. Keep the command's stderr and status in your diagnostics; an empty result on its own is not proof the host has no namespaces.

Done means