Skip to main content
Madhukar
All Articles

Linux File System Hunting — Complete Guide

June 15, 20258 min read
LinuxDevOpsSystem Admin
Linux File System Hunting — Complete Guide

Most people learn Linux through commands — ls, cd, chmod — and stop there. But commands are just the interface. The real story of how Linux actually behaves is written directly into its file system: configuration files that quietly decide how the machine resolves a name, kernel data exposed as if it were an ordinary folder, device files that aren't files at all. I spent time treating a live Linux environment less like a tool to operate and more like a scene to investigate — poking into /etc, /proc, /dev, and a few corners most tutorials skip. Here's what I found, and why each discovery actually matters.

Finding 1: /etc/resolv.conf and /etc/nsswitch.conf — DNS Isn't One File, It's a Decision Chain

What it does: /etc/resolv.conf tells the system which DNS server to actually query when a hostname needs resolving:

nameserver 8.8.8.8

But that’s only half the story. /etc/nsswitch.conf controls the order of lookup sources the system checks before it even reaches DNS:

hosts:          files dns

Why it exists: Name resolution isn’t a single mechanism — it’s a configurable pipeline. Some names should resolve from a local file, some from DNS, some from other sources entirely (like NIS, in older enterprise setups). Splitting this into two files separates “where do I ask” (resolv.conf) from "in what order do I ask around" (nsswitch.conf).

Problem it solves: Without this layering, every application would need to independently decide how to resolve names — check a local file first? Query DNS directly? nsswitch.conf centralizes that policy once, system-wide, so every program behaves consistently.

The insight: The line hosts: files dns means /etc/hosts is always consulted before DNS. That's precisely why adding an entry directly to /etc/hosts — say, pinning a specific hostname to a specific address for local testing — takes priority over whatever the internet's DNS actually says. It's a simple, file-based override sitting quietly ahead of the entire DNS system, and it's the exact mechanism behind local development tricks like redirecting a domain to 127.0.0.1 for testing.

Finding 2: /proc/net/route — The Routing Table, Hiding in Plain Text

What it does: /proc/net/route exposes the kernel's actual routing table as a plain text file:

Iface   Destination   Gateway   Flags   ... Mask
eth0 00000000 010200C0 0003 ... 00000000
eth0 000200C0 00000000 0001 ... 00FFFFFF

Why it exists: /proc as a whole isn't a real disk-backed directory — it's a virtual file system, generated live by the kernel, exposing internal kernel data structures as if they were ordinary files you could cat.

Problem it solves: Without /proc, inspecting kernel-level state (routing decisions, memory usage, running processes) would require specialized system calls or dedicated tools for every single thing you wanted to know. /proc instead exposes almost all of it uniformly, as text, readable with tools every Linux user already knows.

The insight: Those destination and gateway values are stored in little-endian hexadecimal, not human-readable dotted-decimal — 010200C0 actually decodes to 192.0.2.1 reversed byte by byte. It's a small reminder that /proc isn't a friendly abstraction layer built for humans; it's raw kernel data with a text-file costume on, and tools like route or ip route exist specifically to parse and present this exact file in a readable form.

Finding 3: /proc/1/cgroup — Proof of Containment, Written Into the File System

What it does: /proc/<pid>/cgroup shows which control groups (cgroups) a process belongs to — the kernel mechanism responsible for limiting and accounting for a process's CPU, memory, and I/O usage:

7:pids:/
6:blkio:/
5:freezer:/
4:devices:/
3:memory:/
2:cpuacct:/
1:cpu:/

Why it exists: Cgroups are how Linux enforces resource boundaries — capping how much memory or CPU a group of processes can consume, regardless of what those processes think they’re allowed to do.

Problem it solves: Without cgroups, one runaway process could starve an entire machine of memory or CPU. Cgroups let the kernel enforce hard limits per group, which is the exact underlying mechanism that makes containers (Docker, and similar) possible in the first place — a container is largely a process wrapped in cgroup limits and namespace isolation, not a separate virtual machine.

The insight: Checking /proc/1/cgroup is a fast, reliable way to tell whether you're inside a constrained environment (a container, a sandboxed service) versus a full, unrestricted machine — the presence of active memory, cpu, and devices cgroup entries on PID 1 itself is a strong signal that something above the OS is deliberately fencing in what this environment can consume or access.

Finding 4: PID 1 Isn’t Always systemd — And That Changes Everything

What it does: On a traditional Linux server, PID 1 is the very first process the kernel starts at boot — almost always systemd on modern distributions, responsible for starting every other service afterward.

Why it exists: Something has to be the ancestor of every other process, and something has to take responsibility for starting, stopping, and supervising system services in an organized, dependency-aware order — that’s systemd's entire job.

Problem it solves: Without an init system managing service startup order and dependencies, boot would mean manually starting every daemon in the right sequence by hand, every single time.

The insight: Running systemctl status inside this environment returned:

System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

Checking /proc/1/comm (PID 1's own process name) confirmed PID 1 here wasn't systemd at all — it was an entirely different supervising process. This is one of the clearest fingerprints for telling a container or lightweight sandboxed environment apart from a full, independently-booted machine: a real, traditionally-booted Linux server almost always has systemd as PID 1; a container frequently doesn't, because it's not performing a real hardware boot sequence — it's just starting a process tree under something else's supervision.

Finding 5: /etc/passwd vs /etc/shadow — Two Files, One Deliberate Security Split

What it does: /etc/passwd holds basic account information (username, UID, home directory, shell) and is world-readable:

-rw-r--r-- 1 root root   1111 /etc/passwd
-rw-r----- 1 root shadow 609 /etc/shadow

/etc/shadow, by contrast, holds actual password hashes — and is readable only by root and the shadow group.

Why it exists: Lots of ordinary tools and commands need basic account information (resolving a UID to a username, for instance) constantly, and safely — but almost nothing needs access to password hashes themselves.

Problem it solves: This split exists specifically so that account metadata can stay conveniently, safely world-readable, while the actually sensitive material — password hashes, which could otherwise be attacked offline if ever exposed — stays locked down to a minimal, deliberately narrow set of readers.

The insight: The permission bits themselves (-rw-r--r-- vs -rw-r-----) tell this entire security story without reading a single line of documentation — a genuinely elegant example of Linux's permission system being used as a real access-control boundary, not just a formality.

Finding 6: /dev — Where "Everything Is a File" Gets Taken Completely Literally

What it does: /dev contains device files — entries that represent actual hardware and virtual devices, not regular data:

crw-r--r--  1 root root   1, 11 kmsg
crw-rw-rw- 1 root root 1, 7 full
brw------- 1 root root 7, 0 loop0

Why it exists: Linux follows a design philosophy where devices are represented as files specifically so ordinary tools — cat, read, write — can interact with hardware using the exact same interface used for regular files, without needing specialized device-specific commands for everything.

Problem it solves: Without this abstraction, every piece of hardware would need its own bespoke interface and tooling. Representing devices as files means a huge amount of existing file-handling logic (permissions, redirection, piping) works on hardware “for free.”

The insight: The leading c versus b in each entry's permissions (crw-... vs brw-...) distinguishes character devices (data handled one stream of bytes at a time, like kmsg for kernel messages) from block devices (data handled in fixed-size chunks, like loop0, a loopback device used to mount a file as if it were a disk). Two single letters, quietly encoding a fundamental difference in how the kernel talks to two completely different classes of hardware.

Finding 7: An Empty /boot — When the Boot Process Isn't Actually Happening

What it does: /boot normally holds the Linux kernel image and bootloader configuration — the files actually read the moment a machine powers on, before the rest of the operating system even exists yet.

Why it exists: The kernel has to be loaded from somewhere before anything else can run — /boot is that dedicated, predictable location bootloaders (like GRUB) are configured to look in.

Problem it solves: Keeping boot-critical files in one clearly separated location makes bootloader configuration simpler and reduces the chance of an unrelated cleanup process accidentally touching files the machine needs simply to start.

The insight: In this particular environment, /boot existed as a directory — but was completely empty. No kernel image, no bootloader config, nothing. Combined with Finding 4 (no systemd as PID 1), this is a second, independent confirmation of the same underlying reality: this environment never actually went through a traditional boot process at all. Its file system was assembled and its process tree started directly by something else entirely — it's a filesystem shaped like a full Linux install, minus the parts that only matter if you're the one actually turning the hardware on.

Finding 8: /var/log — The System's Memory, Even Without a Full Init System

What it does: /var/log holds a running history of what's happened on the system — package installations, authentication attempts, and application-specific logs:

dpkg.log
journal/
btmp
wtmp

Why it exists: Systems fail, misbehave, or get investigated after the fact far more often than anyone would like — logs exist so there’s an actual record to look back through, instead of relying on memory or guesswork.

Problem it solves: Debugging “what happened right before this broke” is nearly impossible without a persistent, chronological record — logs turn an invisible sequence of past events into something searchable.

The insight: Even in an environment missing a full systemd service stack (Finding 4), a journal/ directory and individual log files like dpkg.log still existed and were actively written to — proof that basic logging infrastructure doesn't strictly require a full init system to function; it's layered in as its own concern, not bolted rigidly to any one specific service manager.

Finding 9: The Root Filesystem Type Itself Told a Story

What it does: Checking /proc/mounts for the root filesystem revealed it was mounted as a real ext4 filesystem, backed by a device named /dev/vda — the kind of device name typically associated with a lightweight virtual machine's virtual disk, rather than the overlay filesystem most people expect from a typical container.

Why it matters: Not every isolated, sandboxed Linux environment is a classic Docker-style container built on a layered overlay filesystem — some are lightweight virtual machines instead, with their own dedicated virtual disk, offering stronger isolation boundaries than a shared-kernel container while still starting up in a similar, non-traditional way (no independent hardware boot, no systemd as PID 1).

The insight: This is exactly why “is it a container?” isn’t always a single yes/no answer — the honest answer often requires checking several independent signals (cgroups, PID 1’s identity, /boot's contents, and the root filesystem's actual type) rather than assuming from just one of them.

Why This Kind of Exploration Matters

Reading documentation tells you what a file is supposed to contain. Actually going in and reading it tells you what your specific system is really doing right now — and often reveals details documentation glosses over entirely, like exactly how DNS lookup order is decided, or what evidence actually distinguishes a container from a full machine.

None of these findings required anything exotic — just cat, careful reading, and a willingness to treat ordinary-looking files and folders as genuine evidence rather than background noise. That's really the whole method: Linux tells you almost everything about how it works, in plain text, if you're willing to go looking.

Frequently Asked Questions

Is /proc actually stored on disk?

> No — /proc is a virtual file system generated live by the kernel in memory. Its contents reflect the system's current state at the moment you read them, and nothing in /proc persists across a reboot the way a normal file would.

Why would a container not have systemd running?

> Because a container doesn't perform an independent hardware boot — it starts as a process (or small process tree) directly under a host's supervision. Since there's no full boot sequence to manage, many containers skip a full init system like systemd entirely, using a much simpler process as PID 1 instead.

Is it safe to explore /proc and /etc like this on a real system?

> Reading these files is safe and a normal part of system administration and diagnostics — the caution applies to modifying them, since files like /etc/nsswitch.conf or /etc/passwd directly affect how the whole system behaves.

What’s the easiest first file to explore for someone starting this kind of investigation?

> /etc/os-release and /etc/hosts are good starting points — small, human-readable, and immediately informative, before moving on to the more kernel-facing detail inside /proc.

Originally published by Mr Madhukar

Read the complete article on Medium with full formatting & reader responses.