Inspect a Process's Linux Namespaces with nsenter
A service is running inside a container, and nsenter is how you see what it sees without guessing. It opens a shell or runs one command using the namespaces of an existing process, so ordinary host commands stop showing you the wrong network or process view. The local manpage is from util-linux 2.39.3, installed here as package version 2.39.3-9ubuntu6.6. This shell also has a Homebrew nsenter 2.41.3 earlier in $PATH, so check which binary your commands actually use.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about fifteen minutes. You need a Linux shell, procfs mounted at /proc, and a target process you are allowed to inspect. Most examples here are read-only, but entering another process's namespaces is security-sensitive: do not mount, unmount, change a hostname, alter firewall rules or modify files until you understand the namespace and have a recovery path.
1. Check the installed command
Start by confirming the binary and its version. These are ordinary, read-only commands and do not need elevated privileges:
$ command -v nsenter
/home/linuxbrew/.linuxbrew/bin/nsenter
$ nsenter --version
nsenter from util-linux 2.41.3
$ /usr/bin/nsenter --version
nsenter from util-linux 2.39.3
The command takes namespace options followed by a program and its arguments. If you supply no program, it starts the shell named by $SHELL, or /bin/sh by default. Supplying an explicit command is easier to review and less likely to leave an interactive shell stranded in the wrong context. Use /usr/bin/nsenter in the examples if you need the binary that matches the local 2.39.3 manpage; otherwise substitute the path reported by command -v.
Checkpoint
Record the version and binary path before copying an example. Option details can differ between package versions.
2. Choose and inspect a target process
Pick a process whose namespace view you need to examine. For a harmless first test, use the current shell as the target: its PID is available as $$.
$ TARGET_PID=$$
$ printf 'target PID: %s\n' "$TARGET_PID"
target PID: 27184
Your number will differ. To inspect another process, replace 27184 with its numeric PID after checking it with a command such as ps -p 27184 -o pid,comm,args. Access to another user's process or its namespace files may need elevated privileges: use sudo only when your account is authorised and the target is one you intend to inspect.
The namespace handles live under /proc/PID/ns/. List them without entering anything:
$ ls -l /proc/"$TARGET_PID"/ns/{mnt,uts,ipc,net,pid,user,cgroup,time}
lrwxrwxrwx 1 user user 0 ... /proc/27184/ns/cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 user user 0 ... /proc/27184/ns/ipc -> ipc:[4026531839]
lrwxrwxrwx 1 user user 0 ... /proc/27184/ns/mnt -> mnt:[4026531841]
lrwxrwxrwx 1 user user 0 ... /proc/27184/ns/net -> net:[4026531840]
lrwxrwxrwx 1 user user 0 ... /proc/27184/ns/pid -> pid:[4026531836]
lrwxrwxrwx 1 user user 0 ... /proc/27184/ns/time -> time:[4026531834]
lrwxrwxrwx 1 user user 0 ... /proc/27184/ns/user -> user:[4026531837]
The inode numbers are host-specific. This listing only confirms the proc entries exist: it does not grant permission to enter them.
3. Run one command in the target's network namespace
Use --target with --net, then give nsenter an explicit program. This asks readlink to print the network namespace link from inside the entered context:
$ nsenter --target "$TARGET_PID" --net -- readlink /proc/self/ns/net
net:[4026531840]
Compare it with the target's own link:
$ readlink /proc/"$TARGET_PID"/ns/net
net:[4026531840]
The values should match. With a real container or service PID, the network namespace may instead show different interfaces and routes. This command does not change the target process: it changes the namespace context of the short-lived readlink process, then exits.
If you see Operation not permitted, check the target PID, your permissions and the kernel's namespace restrictions before reaching for sudo. Elevated privileges are not a substitute for selecting the correct process.
Tip
Some hosts, including restricted containers, deny even a same-namespace reassociation. nsenter may then print reassociate to namespaces failed: Operation not permitted, which is an execution-environment limit, not evidence that the namespace link is wrong. Repeat the check on the host that owns the target process.
4. Inspect several namespaces at once
Use --all when you deliberately want every applicable namespace of the target. The following prints identity and namespace links without changing system state:
$ nsenter --target "$TARGET_PID" --all -- /bin/sh -c 'printf "pid=%s uid=%s\n" "$$" "$(id -u)"; readlink /proc/self/ns/{mnt,uts,ipc,net,pid,user,cgroup,time}'
pid=27185 uid=1000
mnt:[4026531841]
uts:[4026531838]
ipc:[4026531839]
net:[4026531840]
pid:[4026531836]
user:[4026531837]
cgroup:[4026531835]
time:[4026531834]
Output order and identifiers vary. The user namespace is skipped when it is already the caller's current user namespace.
Warning
By default, nsenter sets UID and GID to 0 when entering a user namespace, so do not assume an entered shell carries the same credentials as your original shell.
5. Understand PID namespace forking
When changing the PID namespace, nsenter forks by default before executing the program. That lets the new program and its children share the entered PID namespace. A shell started with --pid therefore has a process view that can differ from the shell that launched nsenter.
For a read-only comparison, print the PID list:
$ nsenter --target "$TARGET_PID" --pid -- /bin/sh -c 'printf "inside PID namespace: %s\n" "$$"; ps -e -o pid,comm'
inside PID namespace: 1
PID COMMAND
1 sh
2 ps
The exact list depends on the target. --no-fork suppresses nsenter's PID namespace fork, but it changes the process semantics and is not a general fix for a confusing result. Leave it out unless the program specifically needs exec without that fork.
6. Use a target root or working directory carefully
--root can use the target's root directory and --wd can use its working directory. These options change where path lookups happen, but they do not make a process safe or reproduce every property of a container. Test with a harmless command first:
$ nsenter --target "$TARGET_PID" --root -- /bin/pwd
/
The target root must contain the requested program, or nsenter can fail with a file-not-found error even though the command exists on the host. The specified directory for --root or --wd is opened before the namespace switch; --wdns opens the working directory afterwards, and it cannot be combined with --wd.
Safety boundary
Do not combine these options with shell commands that write files until you have checked the path with pwd and readlink -f. A relative path can point at a different filesystem after the switch. If you accidentally start an interactive shell, type exit: no persistent change is undone by exiting, so review any command that already ran.
Common traps
- A disappearing target. Processes exit and PIDs get reused, so an error can appear even when the original PID was valid. Repeat the
psand namespace-link checks immediately before a sensitive inspection. - Wrong option spelling. Check it against the local help text and confirm the target relationship with a harmless command, shown below.
- A confusing zero status. It only means the requested program returned zero, not that you inspected the namespace you intended.
$ nsenter --help | sed -n '1,35p'
$ nsenter --target "$TARGET_PID" --net -- /bin/true
$ printf 'exit status: %s\n' "$?"
exit status: 0
Record the target PID, compare /proc/PID/ns/* with /proc/self/ns/*, and keep the command explicit.
Done means
- Version confirmed: you checked the installed nsenter version and read its local option contract.
- Target selected: you picked a live target PID and checked its namespace links.
- Command run: you ran an explicit, read-only command in the required namespace.
- Risks understood: you know that
--all, user namespaces and PID namespace forking can change credentials or process visibility. - Write paths checked: you confirmed the working directory or root before running anything that writes, mounts or changes network state.