Skip to main content

Command Palette

Search for a command to run...

----- Linux File System Hunting -----

Updated
23 min read
----- Linux File System Hunting -----
D

Computer Engineering student | Full-Stack Developer (in progress) | DSA in C++ | AI-era Tech Explorer 🚀 Building skills, mindset, and systems to create impactful technology.

Under the Hood of Linux: A Detective’s Journey Through the File System

The “Everything is a File” Philosophy: Why It Matters

Before we dive into the rabbit hole, let’s anchor ourselves. When Unix pioneers decided that almost every resource could be represented as a file, they weren’t being cute – they were solving a massive design problem. By funneling hardware, processes, and configurations through the same interface (open, read, write, close), they made system programming and administration radically simpler. You can examine a process just by reading a file. You can talk to a hard drive by writing to a file. You can change kernel behaviour by echoing a value into a file. There’s no special API, no secret handshake – just files.

This philosophy directly shapes the directory structure we’ll explore. The Linux Filesystem Hierarchy Standard (FHS) may sound dull, but it’s the blueprint of the city we’re about to wander through. Once you see the why behind every folder, you’ll never stare blankly at /etc again.

Discovery #1: /etc – The Brain of Your System

What it is: /etc (pronounced “et-see”, short for et cetera) is the central nervous system of your Linux machine. It holds configuration files – plain‑text recipes that tell every piece of software how to behave. No binaries, no libraries, just text.

Why it exists: Early Unix systems put everything executable in /bin and /usr/bin, and leftover configs landed in /etc. Over time, it became the standard location for system‑wide settings. Today it contains everything from network setups to user login definitions, from cron schedules to the message of the day.

The problem it solves: Without a single, predictable place to store settings, each program would invent its own storage – some in hidden dotfiles, some in a database, some in binary blobs. Sysadmins would lose their minds. By herding all configuration into one directory, Linux makes backups trivially easy (tar czf etc-backup.tar.gz /etc), centralised management possible, and system migration a breeze.

Interesting insight: The true beauty of /etc is its self‑documenting nature. Almost every file is human‑readable, often with explanatory comments. Want to know what a setting does? Open the file. Searching for “where is DNS configured?” or “how do I add a user’s home directory automount?” leads you here. In the Windows world, settings are scattered across a registry, a hive of binary keys that are nearly impossible to version‑control or read without special editors. Linux’s /etc is a triumph of simplicity over complexity.

Diagram: The /etc ecosystem at a glance

/etc
├── hostname          ← Machine's identity
├── hosts             ← Local hostname → IP overrides
├── resolv.conf       ← DNS resolver configuration
├── network/          ← Old‑school interface definitions (Debian)
│   └── interfaces
├── netplan/          ← Modern YAML‑based network config (Ubuntu)
│   └── 01-netcfg.yaml
├── ssh/
│   └── sshd_config   ← Secure Shell daemon rules
├── sudoers           ← Who can become root
├── passwd            ← User account info (world‑readable)
├── shadow            ← Hashed passwords (locked down)
├── group             ← Group memberships
├── systemd/          ← Service manager configurations
│   └── system/       ← Unit files that define services
├── cron.d/           ← Scheduled tasks
├── apt/              ← Package manager sources (on Debian)
│   └── sources.list
└── default/          ← Default settings for many packages
     └── grub

Figure: A snapshot of /etc’s organizational genius. Every critical subsystem has a well‑known file or subdirectory.

Action‑packed example: Let’s say your machine suddenly refuses to resolve domain names. You’d immediately suspect /etc/resolv.conf. And we’ll dissect that next.


Discovery #2: /etc/resolv.conf – Where DNS Lives (and Dies)

What it is: /etc/resolv.conf tells your system which DNS servers to query and how to construct domain search paths. It’s a tiny file, often no more than five lines, yet it controls one of the most fundamental internet operations – name resolution.

Why it exists: Before networked systems, a machine only knew the handful of hosts listed in /etc/hosts. As the internet exploded, a centralised phonebook was needed. The resolver library (glibc) was designed to read /etc/resolv.conf so that every piece of software – browsers, curl, mail servers – could automatically find the DNS servers without re‑implementation.

The problem it solves: DNS is complicated, but applications don’t need to know the messy details. The resolver abstracts all of that behind a simple file interface. The same program code works whether you’re on a corporate laptop that changes networks or a server with static IPs.

Interesting insight: On modern systems, /etc/resolv.conf is often a symlink to a dynamically‑generated file. Why? Because desktop environments, VPN clients, and systemd‑resolved all fight over who gets to set your DNS. Run ls -l /etc/resolv.conf – you might see it pointing to /run/systemd/resolve/stub-resolv.conf or /run/NetworkManager/resolv.conf. This little redirection prevents programs from clobbering each other’s changes. Here’s a real find:

$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 39 Feb 10 09:23 /etc/resolv.conf -> ../run/systemd/resolve/stub-resolv.conf

This means systemd‑resolved is managing your DNS. Directly editing /etc/resolv.conf would be pointless because it gets regenerated at next network event. Instead, you’d edit /etc/systemd/resolved.conf or use NetworkManager’s tools. Knowing this saves hours of head‑scratching when your changes mysteriously vanish.

Diagram: The DNS resolution chain

 Application
     ↓   gethostbyname()
 glibc resolver
     ↓  reads
 /etc/nsswitch.conf → "hosts: files dns"
     │
     ├─ /etc/hosts (first)
     └─ /etc/resolv.conf (if not found)
         ↓
      nameserver 8.8.8.8

Figure: The resolver’s step‑by‑step logic. Always consult nsswitch.conf before tinkering with resolv.conf.


Discovery #3: /proc/net/route – The Kernel’s Routing Table Exposed

What it is: /proc/net/route is a read‑only virtual file that dumps the kernel’s IP routing table in a raw, hex‑encoded format. It’s part of the magical /proc filesystem.

Why it exists: The kernel maintains a routing table in memory to decide where packets should go next. While tools like ip route or the old route -n display the table in a human‑friendly format, /proc/net/route exposes the exact data structures the kernel uses. It’s a direct window into the packet‑forwarding brain.

The problem it solves: System programs and monitoring daemons can parse this file without spawning expensive subprocesses. For example, a lightweight network dashboard can read /proc/net/route once a second, compute statistics, and display changes instantly. It also allows debugging when the usual tools aren’t available – in a minimal container or recovery shell, /proc is always mounted.

Interesting insight: The table is presented in hexadecimal, with columns for destination, gateway, mask, flags, and interface. Let’s decode a line:

$ cat /proc/net/route
Iface   Destination     Gateway         Flags   RefCnt  Use     Metric  Mask            MTU     Window  IRTT                                                       
eth0    00000000        0101A8C0        0003    0       0       100     00000000        0       0       0                                                                              
eth0    0000FEA9        00000000        0001    0       0       1000    0000FFFF        0       0       0                                                                              
lo      0000007F        00000000        0001    0       0       0       000000FF        0       0       0
  • Destination 00000000 with Mask 00000000 is the default route (0.0.0.0/0).

  • Gateway 0101A8C0 interpreted in little‑endian hex becomes C0.A8.01.01 → 192.168.1.1.

  • The second line Destination 0000FEA9 → 169.254.0.0, a link‑local route.

Without this file, you’d have to call ioctl or Netlink sockets to peek inside the kernel’s forwarding information base (FIB). /proc/net/route makes it accessible with a simple cat. This is an exquisite illustration of how “everything is a file” turns complicated kernel internals into something a script can digest.

Text‑based ER Diagram: Relationship between routing entities

+-------------------+       +-------------------+
|    Network Stack  |       |  /proc/net/route  |
| (Kernel Memory)   |←──────|   (virtual file)  |
+--------+----------+       +-------------------+
         | reads
+--------v----------+
|  ip route command |  (uses Netlink to retrieve same data)
+-------------------+

Figure: The kernel routing table exists only in RAM. /proc/net/route provides a file‑based snapshot, while ip route parses and formats it for humans.


Discovery #4: /etc/network/interfaces and /etc/netplan – The Duel of Network Configs

What they are: These are two completely different configuration systems for network interfaces. /etc/network/interfaces is used by Debian‑based systems with the ifupdown framework (legacy yet still alive). /etc/netplan/*.yaml is used by newer Ubuntu releases (17.10+) and employs systemd‑networkd or NetworkManager as the backend renderer.

Why they exist: Networking is complicated: Ethernet, WiFi, bridges, bonds, VLANs. Each requires a clear definition of IP addresses, gateways, DNS, and routing. By providing a declarative configuration file, the system can bring up interfaces consistently at boot and handle changes gracefully. The coexistence of two systems reflects Linux’s evolution – ifupdown was simple and worked for decades, but as systems became more dynamic (cloud instances, containers, hot‑plugged USB Ethernet), a more flexible system like Netplan (with YAML) became necessary.

The problem they solve: Imagine manually plugging in ip addr add, ip link set up, and ip route add every time you reboot. The config files automate that state. They also separate the what (my interface has a static IP 10.0.0.10) from the how (which daemon applies it). This abstraction means an admin can replace the backend without touching the configuration.

Interesting insight: Many servers still run on legacy ifupdown. If you blindly copy an /etc/network/interfaces onto a new Ubuntu server, it will be completely ignored because Netplan takes precedence. The system might boot with no network, leaving you locked out. The lesson? Always check which system is active:

$ networkctl status

or look for the presence of /etc/netplan/ and /etc/network/interfaces. A proper investigator knows which rulebook the system is reading.

Diagram: Network config stack on modern Ubuntu

/etc/netplan/01-netcfg.yaml  ← declarative YAML (You write this)
           │
           ▼
   netplan generate  ← compiles to backend config
           │
    ┌──────┴──────┐
    ▼             ▼
networkd .network  NetworkManager .nmconnection
 files in /run     (or keyfile)
    │
    ▼
  kernel’s netlink
    │
    ▼
  physical NIC (eth0)

Figure: Netplan acts as an abstraction layer, letting you choose your backend without changing your configuration vocabulary.


Discovery #5: /var/log – Chronicles of Every Breath Your System Takes

What it is: /var/log is the journal of your Linux life. It stores logs from the kernel, system services, authentication events, web servers, and almost anything that writes to syslog.

Why it exists: Systems are complex and things fail. Without logs, an admin would be blind. The log files provide a timestamped, searchable history of what happened, allowing root cause analysis, security auditing, and capacity planning.

The problem it solves: When SSH refuses your key, a cron job doesn’t fire, or a hard drive starts throwing I/O errors, where do you look? The logs. Instead of scattering diagnostic information into random files, the system funnels messages into a standardized directory that tools like logrotate can manage automatically – compressing, rotating, and deleting old logs to keep disk usage under control.

Interesting insight: Not all logs are plain text anymore. On systemd‑based distros, journald stores binary logs in /var/log/journal/. You read them with journalctl. However, many traditional syslog‑like services still output to files like /var/log/syslog and /var/log/auth.log. The coexistence reveals an ongoing transition. Here’s a fun find: /var/log/wtmp is a binary file that records every login and logout; use last -f /var/log/wtmp to see who’s been sneaking onto the server. /var/log/btmp records failed login attempts, a goldmine for security nerds.

Sampling the treasure trove:

  • /var/log/kern.log – Kernel messages (hardware, drivers, OOM killer).

  • /var/log/auth.log – Every authentication attempt, including sudo.

  • /var/log/apt/history.log – Every package installed, upgraded, or removed.

  • /var/log/nginx/access.log – Every HTTP request (if nginx is installed).

Diagram: Log flow from applications to disk

 Applications  ──► syslog() ──► rsyslog/syslog-ng ──► /var/log/syslog
 Systemd services ──► journald ──► /var/log/journal/
 Kernel ring buffer ──► dmesg ──► /var/log/kern.log (via rsyslog)

Figure: Different paths converge into /var/log, but the ultimate goal is the same – persistent audit trail.


Discovery #6: /etc/passwd, /etc/shadow, /etc/group – The Holy Trinity of User Management

What they are: These three files collectively define every user and group on the system. /etc/passwd lists usernames, UIDs, home directories, shells – but no longer holds the password hash. /etc/shadow stores salted, hashed passwords and account expiry information, accessible only by root. /etc/group maps group names to GIDs and member lists.

Why they exist: Multi‑user systems must authenticate users and enforce permissions. Instead of embedding this logic in a database, Linux uses simple colon‑delimited text files that can be edited, scripted, and version‑controlled. Tools like useradd actually modify these files behind the scenes.

The problem it solves: In the early days, encrypted passwords were stored in /etc/passwd, which had to be world‑readable (so tools like ls -l could map UID to username). That was a security disaster – anyone could copy the hashes and attempt offline cracking. The solution was to split the password into /etc/shadow, which is readable only by root. This is a classic example of evolution through refactoring without breaking compatibility.

Interesting insight: The order of fields in /etc/passwd is a piece of Unix archaeology: username:x:UID:GID:comment:home:shell. The x in the password field is a placeholder that screams “the real password is in shadow”. Look at the shell field: if set to /usr/sbin/nologin or /bin/false, the user cannot log in interactively – a common trick for service accounts. Exploring /etc/shadow reveals password aging policies (fields for last change, minimum days between changes, maximum days before expiry, warning period). It’s a full account lifecycle management engine in a few bytes.

Sample from /etc/shadow:

john:\(6\)saltsalt$hashedpassword:19321:0:99999:7:::

19321 is the number of days since Jan 1, 1970, when the password was last changed. This kind of nitty‑gritty detail is why forensic investigators always image /etc.

ER Diagram: Relationship between user identity files

+----------+       +----------+       +----------+
| /etc/passwd |   | /etc/shadow |   | /etc/group |
+-----+------+   +-----+------+   +-----+------+
|     UID     |───(UID)──| UID   |       |
| username    |       password hash |   GID  |
| home        |       expiry info   |   members...
+------------+       +------------+       +--------+

Figure: /etc/passwd and /etc/shadow are linked by UID (username can change, UID stays). /etc/group uses GID and lists members by username.


Discovery #7: The setuid Bit and /etc/sudoers – The Delicate Art of Power

What they are: Not a directory but a concept deeply embedded in the file system’s permission structure. The setuid (Set User ID) bit is a special permission on an executable that allows it to run with the privileges of the file’s owner, not the user who launched it. /etc/sudoers is the configuration file for sudo, defining who can run what commands as which user.

Why they exist: The Unix permission model is based on the current user. But certain tasks – like changing your own password (which modifies /etc/shadow) or pinging a host (which requires raw socket access) – need elevated privileges. The setuid mechanism lets regular users execute specific privileged binaries safely. sudo provides a more granular, auditable way to delegate superuser powers.

The problem it solves: Without setuid, every user who needed to change their password would need root access – chaos. Without sudo, you’d either give out root passwords (bad) or login as root directly (worse). sudo, governed by /etc/sudoers, allows temporary privilege escalation with logging and fine‑grained rules.

Interesting insight: Explore /usr/bin/passwd (the password change program). Its permissions are -rwsr-xr-x. That little s in the owner execute field is setuid to root. The program runs as root, reaches into /etc/shadow, updates the hash, and exits – all without the user ever being root. Find more setuid binaries with find / -perm -4000 -type f 2>/dev/null. Every one is a potential attack surface, so system hardening focuses on auditing this list.

Now, look at /etc/sudoers. Never edit it directly with a normal editor; use visudo which locks the file and runs a syntax check. Some gems:

%admin ALL=(ALL) ALL          # Group admin can do anything
john ALL=(root) NOPASSWD: /usr/bin/systemctl restart apache2  # Password‑less restart

The insight? Linux gives you security that’s a scalpel, not a sledgehammer. You can delegate exactly the commands needed.

Diagram: How passwd uses setuid

User runs /usr/bin/passwd
   │
   ▼
Kernel sees setuid root → effective UID=0
   │
   ▼
passwd process (root) modifies /etc/shadow
   │
   ▼
process exits, privileges dropped

Figure: The ephemeral root power ensures /etc/shadow stays secure while allowing users to manage their passwords.


Discovery #8: /proc – The Window into Your Kernel’s Soul

What it is: /proc is a pseudo‑filesystem that doesn’t exist on disk. It is an interface provided by the kernel to expose internal data structures about processes, hardware, and system configuration.

Why it exists: Instead of creating dozens of new system calls for every kernel metric, the developers exploited the file abstraction. By mounting proc (and later sysfs at /sys), the kernel can present a hierarchical, human‑readable view of its internals. Every tool like ps, top, free, lspci simply reads files from /proc.

The problem it solves: Inspection and debugging become trivial. A shell script can check available memory by reading /proc/meminfo, find CPU details in /proc/cpuinfo, or even alter kernel parameters in /proc/sys/. No special libraries needed.

Interesting insight: The directory structure per process (/proc/[PID]/) is a goldmine. For any running process, you can see:

  • /proc/[PID]/cmdline – exact command that started it (null‑separated).

  • /proc/[PID]/environ – environment variables of the process (regenerated at start).

  • /proc/[PID]/fd/ – symbolic links to all open file descriptors. Ever wondered which log file a misbehaving service writes to? ls -l /proc/$(pidof myservice)/fd/ tells you.

  • /proc/[PID]/cwd – a symlink to the process’s current working directory.

  • /proc/[PID]/exe – a symlink to the executable binary itself.

This means you can recover a deleted binary that’s still running! If a malicious user replaces /usr/bin/myapp with a trojan, the in‑memory version is still accessible via /proc/[PID]/exe. Copy it out before the process dies.

Deep dive into /proc/sys/: This is where runtime kernel tuning happens. For example, to enable IP forwarding (turn your machine into a router), you can echo 1 > /proc/sys/net/ipv4/ip_forward. No reboot. The file system becomes a configuration panel.

ER Diagram: A process as seen through /proc

+-------------------+
|   /proc/[PID]/    |
+--------+----------+
         |
  +------+--------+--------+--------+----...
  |               |        |        |
  v               v        v        v
cmdline        environ     fd/     maps
 "sleep 1000"   HOME=/root  0→/dev/pts/0   (memory mapping)

Figure: Every aspect of a live process is a file. Forensic analysis becomes file reading.


Discovery #9: /dev – Where Devices Become Files

What it is: /dev contains device files – special files that represent hardware devices or kernel drivers. There are two main types: block devices (buffered, like hard drives /dev/sda) and character devices (unbuffered, like serial ports /dev/ttyS0).

Why it exists: How do you talk to a disk without a standard interface? By representing the disk as a file, the kernel can route reads and writes to the appropriate driver. This is the ultimate embodiment of the Unix philosophy.

The problem it solves: Programs don’t need to know whether the storage is SATA, NVMe, or a USB stick. They open /dev/sda1 and read; the kernel handles the rest. This abstraction allowed the same tools (dd, mkfs, mount) to work across dramatically different hardware for decades.

Interesting insight: /dev is no longer a static collection of manually created nodes. The udev system (device manager) dynamically populates /dev in a tmpfs filesystem at boot and whenever hardware is hot‑plugged. Want to see your system’s interpretation? Check /dev/disk/by-id/, /dev/disk/by-uuid/ – these symbolic links are created by udev rules, making it easy to always refer to the same disk even if its device name changes (/dev/sda vs /dev/sdb on next boot). And then there are the fun “virtual” devices:

  • /dev/null – the black hole that discards all data.

  • /dev/zero – infinite source of zeros.

  • /dev/random and /dev/urandom – entropy for cryptography.

One non‑intuitive discovery: /dev/tty always points to the current terminal, regardless of whether you’re on a console, SSH, or a serial line. This is why echo "hello" > /dev/tty always reaches the user, no matter the redirection.

Diagram: udev creating device nodes

 Hardware event (USB stick inserted)
          │
          ▼
    kernel uevent  →  udev daemon
          │
          ▼
   udev rules apply (/etc/udev/rules.d/, /lib/udev/rules.d/)
          │
          ▼
   creates /dev/sdb, /dev/sdb1
   creates symlinks /dev/disk/by-id/usb-...
   sets permissions, runs scripts

Figure: /dev is now a dynamic filesystem, making plug‑and‑play a natural extension of the file abstraction.


Discovery #10: /boot – The Launchpad of the Kernel

What it is: /boot contains the files necessary to start the operating system: the Linux kernel image (vmlinuz-*), the initial RAM disk (initrd.img-* or initramfs-*), and the bootloader configuration (GRUB).

Why it exists: The boot process is a delicate chain: firmware (BIOS/UEFI) → bootloader → kernel → userspace. The bootloader lives in its own special area, but the kernel and initramfs are ordinary files stored in /boot. This separation allows the kernel to be easily updated, patched, or replaced without touching the bootloader.

The problem it solves: Keeping the kernel on a standard filesystem (usually ext4 or XFS) means apt upgrade can drop in a new kernel, update GRUB, and you’re ready to reboot. If kernels were hidden in firmware, updates would be far riskier.

Interesting insight: Take a peek at /boot/grub/grub.cfg. It’s an auto‑generated file that can look terrifying, but it’s built from snippets in /etc/grub.d/ and variables in /etc/default/grub. This design means you can add custom boot entries by placing scripts in /etc/grub.d/ instead of manually editing a monolithic file. For example, to add a kernel parameter nomodeset permanently, you set GRUB_CMDLINE_LINUX_DEFAULT="quiet splash nomodeset" in /etc/default/grub and run update-grub. The system regenerates the boot configuration safely.

Also, the presence of vmlinuz and initrd.img as separate files reveals the two‑stage boot: kernel mounts a temporary root in RAM (initrd) that loads drivers, assembles RAID/LVM, then pivots to the real root filesystem. Without /boot, this handover can’t happen.

Diagram: Boot sequence file involvement

 UEFI/BIOS → /boot/efi/EFI/grub/grubx64.efi (or MBR)
                │
                ▼
 GRUB reads /boot/grub/grub.cfg → loads kernel & initrd from /boot
                │
                ▼
 Kernel boots, runs initrd, pivots to real root /

Figure: /boot is a small, usually separate partition, ensuring the bootloader can read the kernel even if the main filesystem uses exotic features.


Discovery #11: /etc/systemd/system – The Service Universe Orchestrated

What it is: This directory (along with /lib/systemd/system/) holds unit files that define services, sockets, timers, and more for systemd, the modern init system and service manager.

Why it exists: Before systemd, SysV init used cryptic shell scripts in /etc/init.d/ with symlinks in rc?.d to control start order. It was brittle and offered minimal dependency management. Systemd replaced that with declarative unit files and a central manager that parallelizes service startup, monitors processes, and handles dependencies intelligently.

The problem it solves: Defining a service in a unit file is cleaner and more powerful than a shell script. You specify the ExecStart command, the Restart policy, environment variables, resource limits, and dependency on other services, all in a simple INI‑syntax file. Systemd then ensures the service starts at the right time and can automatically restart it if it crashes.

Interesting insight: The real magic is in the override mechanism. You should never edit unit files directly in /lib/systemd/system/ because package updates will overwrite your changes. Instead, create an override directory: sudo systemctl edit myservice.service. This creates /etc/systemd/system/myservice.service.d/override.conf, where you can add or override settings. When the service starts, systemd merges the base unit with all drop‑in files. It’s a version‑control‑friendly, conflict‑free customization method. Discovering this pattern after years of patching init scripts feels like enlightenment.

Another gem: the systemctl list-unit-files command shows you the enable state of every unit. But the actual file system location is determined by the search path. A local admin can put a unit in /etc/systemd/system/ with the same name as a vendor unit in /lib/systemd/system/, and the one in /etc takes precedence. This layered approach keeps the system pristine while allowing infinite customization.

Diagram: systemd unit file precedence

/etc/systemd/system/multi-user.target.wants/  ← symlinks to enabled services
/etc/systemd/system/sshd.service              ← admin override (highest priority)
/run/systemd/system/sshd.service              ← runtime override (temporary)
/lib/systemd/system/sshd.service              ← package default (do not touch)

Figure: The directory hierarchy ensures that package updates never destroy local configuration, solving a decades‑old sysadmin headache.


Discovery #12: /etc/environment, ~/.bashrc, /etc/profile – The Environment Configuration Labyrinth

What they are: These files control the environment variables that govern the behaviour of shells and programs. /etc/environment sets system‑wide variables for every process. /etc/profile and ~/.bashrc are executed by bash to set per‑user session variables.

Why they exist: Environment variables are the global state of Unix – they define your PATH, HOME, EDITOR, locale, and a thousand other things. Without a standardized initialization mechanism, every user would have to manually set them in every shell, or programs would break because they can’t find executables.

The problem it solves: By separating login shells (which read /etc/profile and ~/.profile) from interactive non‑login shells (which read ~/.bashrc), the system can perform heavy, one‑time setup on login (like checking mail, starting an ssh‑agent) but keep subsequent bash invocations fast. It’s a carefully choreographed dance.

Interesting insight: Many developers get tripped up by the execution order. If you set an environment variable in ~/.bashrc and then expect it to be available in a cron job or a systemd service, it won’t be, because those environments don’t source your bashrc. The proper place for system‑wide, always‑available variables is /etc/environment, which is read by pam_env.so and applied to all logins independent of shell. Meanwhile, /etc/profile.d/ contains small scripts that are sourced by /etc/profile – this is how packages like Java set JAVA_HOME system‑wide without editing a single master file.

The discovery of how layered and event‑driven the environment is explains why sometimes env shows variables you never set – they’re injected by PAM, systemd, or profile scripts. You can trace your own shell’s inheritance:

$ cat /etc/environment
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
$ cat /etc/profile.d/myapp.sh
export MYAPP_CONFIG=/etc/myapp

Understanding this avoids the “it works in my terminal but not in my IDE” mystery.

Diagram: Shell variable sourcing order for a login shell

 login → /etc/profile
          ├── /etc/profile.d/*.sh
          ├── ~/.bash_profile (or ~/.bash_login, or ~/.profile)
          └── eventually ~/.bashrc (if bash)

Figure: The cascading initialization is robust but can be bewildering. Knowing the sequence lets you place variables in the correct file for the desired scope.


Putting the Detective Hat On: Why All This Matters

Congratulations, investigator. You’ve walked through config brains, DNS backdoors, kernel windows, hardware files, and bootloaders. Each discovery may seem like an isolated oddity, but they form a coherent tapestry. The Linux file system is not just a place to store files; it is the API of the operating system. Once you internalize that, everything changes.

When a server misbehaves, you no longer panic. You go to /var/log first. When a network glitch occurs, you check /proc/net/route and /etc/resolv.conf. When you need to audit security, you examine /etc/shadow, setuid binaries, and /etc/sudoers. When debugging a daemon, you inspect its /proc/[PID]/fd and its systemd unit overrides. This is system administration at the investigator level – curiosity driven, file oriented, and endlessly rewarding.

Final ER Diagram: The Investigator's Cheatsheet

          System Behavior
               │
   ┌───────────┼───────────┐
   │           │           │
   ▼           ▼           ▼
/etc          /proc       /var/log
(config)    (runtime     (history)
             data)
   │           │
   ├─ resolv.conf    ├─ net/route
   ├─ passwd         ├─ [PID]/cmdline
   ├─ shadow         └─ sys/kernel
   ├─ sudoers
   ├─ systemd/
   └─ environment