Use unshare to Test Linux Namespaces Without Touching the Host
You will run a command in a temporary Linux namespace, verify the boundary from inside it, and understand when the command needs extra privileges. This is useful for checking namespace behaviour and building small experiments; it is not, by itself, a complete container security boundary.
The route
Jump straight to the step you need, or tick off Done means at the end.
Allow about 10 minutes. You need util-linux, a Linux kernel with the requested namespace support, and a shell. The examples use util-linux 2.39.3, matching the system manpage at the time this guide was written. Run ordinary checks as your normal user. The PID and mount examples may require an administrator to permit the operation, and the persistent examples deliberately change mounts, so do not paste those onto a production host without adapting the paths.
1. Check the local command
Start by confirming which binary will run. This avoids a common distraction: a different copy of util-linux earlier in PATH can have different options.
$ command -v unshare
$ unshare --version
unshare from util-linux 2.39.3
Read the local manual if the version differs:
$ man 1 unshare
Checkpoint
Record the version before comparing an example with another machine. In particular, the mapping syntax changed in util-linux 2.39: current syntax uses a colon-separated inner:outer:count form, while older manuals describe a comma-separated form.
2. Create a user namespace as an ordinary user
A user namespace gives the process a separate view of user IDs, group IDs and capabilities. --map-root-user maps your current effective user and group to ID 0 inside that namespace. It does not make you root on the host.
$ unshare --user --map-root-user -- \
sh -c 'printf "inside uid=%s gid=%s\n" "$(id -u)" "$(id -g)"; cat /proc/self/uid_map /proc/self/gid_map'
inside uid=0 gid=0
0 1000 1
0 1000 1
Replace 1000 with the IDs shown by id -u and id -g on your machine. The first column is the ID seen inside the namespace and the second is the corresponding host ID. If the command reports Operation not permitted, the kernel or the execution environment has disabled unprivileged user namespaces. Do not work around that restriction blindly; it is an administrator or platform policy decision.
Checkpoint
Run id -u outside the command and compare it with the inner value. A zero inside this command is a namespace mapping, not host-wide privilege.
3. Give a PID namespace its own process view
A PID namespace is most useful when the program becomes the namespace's first child and therefore receives PID 1. Use --fork for that child, and --mount-proc so that /proc reports the new namespace rather than the caller's process view.
$ unshare --fork --pid --mount-proc -- \
sh -c 'printf "namespace pid=%s\n" "$$"; ps -o pid,ppid,comm'
namespace pid=1
PID PPID COMMAND
1 0 sh
The new mount namespace implied by --mount-proc keeps this proc mount private. When the shell exits, these temporary namespaces disappear. A failed command is usually a permission boundary, not evidence that the shell script is wrong. Check the exact error, then inspect whether your kernel, container runtime or security policy permits PID and mount namespaces.
Safety warning
Do not use --mount-proc in a command that also performs unreviewed mounts, writes to system paths, or starts a long-lived service. The new mount namespace isolates ordinary mount changes, but shared mount propagation and host permissions still matter.
4. Combine a private user and network namespace
--net creates an independent network namespace with its own interfaces, routes, firewall rules and sockets. Pairing it with a mapped user namespace is a useful harmless probe because it needs no interface configuration:
$ unshare --user --map-root-user --net -- \
sh -c 'printf "uid=%s\n" "$(id -u)"; printf "interfaces:\n"; cat /proc/net/dev'
uid=0
interfaces:
Inter-| Receive | Transmit
face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
lo: ...
The exact counters and interface list vary. A network namespace normally starts with loopback, which may need to be brought up before software can use it. Do not assume it has the host's network access, DNS setup or firewall policy. If you need to configure interfaces, that is a separate privileged networking task.
5. Keep a namespace only when you need one
Without a file argument, a namespace lasts only while it has member processes. Supplying a path to options such as --uts, --net or --mount makes it persistent by creating a bind mount for the namespace handle. The following UTS example changes only the persistent namespace's hostname:
# install -d -m 0755 /root/unshare-demo
# touch /root/unshare-demo/uts
# unshare --uts=/root/unshare-demo/uts hostname isolated-demo
# nsenter --uts=/root/unshare-demo/uts hostname
isolated-demo
# umount /root/unshare-demo/uts
# rmdir /root/unshare-demo
These commands need elevated privileges because they create a mount-backed handle and alter the namespace's hostname. The umount is the undo step. Keep persistent handles in a directory dedicated to this experiment: leaving them behind makes cleanup and later namespace discovery harder. Persistent PID namespaces also need --fork, and a running PID 1 is required if the namespace is to remain useful after the original command ends.
6. Diagnose the common traps
- "Operation not permitted": the kernel, container runtime or security policy rejected the namespace operation. Test a smaller user-namespace example and ask the host administrator before changing policy.
- Unexpected PIDs: add both
--forkand--mount-procfor a meaningful PID namespace demonstration. A stale or shared proc mount can show the caller's process list. - Mount changes leak farther than expected: check propagation with
findmnt -o+PROPAGATION. util-linux 2.39.3 makes a new mount namespace private by default, but--propagation unchangeddisables that adjustment. - "root" seems too powerful: namespace root has capabilities scoped to that namespace. It is not a substitute for host access control, filesystem ownership checks or a container runtime's policy.
- The shell appears to hang:
unsharewaits for its child. With--fork, the default behaviour ignores SIGINT and SIGTERM while waiting;--forward-signalschanges that behaviour and implies--fork.
Done means
- You checked the installed util-linux version and read the matching local manual.
- You verified a user-ID mapping without confusing namespace root with host root.
- You used
--forkand--mount-procwhen checking PID isolation. - You treated denied namespace creation as a policy boundary and did not weaken it casually.
- You removed any persistent namespace bind mounts with
umount.