Linux & macOS Filesystem Explained: Important Folders, Config Files, Logs, and Troubleshooting
Published on September 19, 2026 by Muhammad Raza Bangi · 27 min read
A developer deploys a website through a hosting dashboard. Everything works — until it doesn't. The site starts returning a 502 error, disk usage hits 100%, a domain resolves to the wrong server, or a background job silently stops running. The dashboard that made deployment easy now hides exactly the information needed to fix the problem: where the configuration lives, where the logs are written, which process is actually running, and who owns which file.
Modern hosting platforms — cPanel, Plesk, Vercel, managed WordPress hosts — are good at making deployment fast. They're not designed to teach you what's happening underneath. The moment something breaks in a way the dashboard can't explain, or you move to a VPS where there is no dashboard, you need to know the filesystem: what each important directory is for, where logs and configuration actually live, and how to inspect a running server safely without guessing.
This guide covers that filesystem in practical depth, for both Linux (with an emphasis on Ubuntu/Debian, the most common choice in web hosting) and macOS, which is Unix-based but meaningfully different in several places. Every section is labeled by platform, and every command is marked as either safe to run or state-changing, so you can follow along on a real machine without breaking anything.
What You Will Learn
What each major Linux and macOS directory actually contains and why. How /etc/hosts and DNS resolution really work, and how to edit both safely. Where logs live for Nginx, Apache, PHP-FPM, MySQL, Laravel, Node.js, and Docker — and how to read them without leaking secrets. How Linux permissions and ownership work, and why chmod 777 is not a fix. The difference between systemd (Linux) and launchd (macOS). How to diagnose a 502 error, a full disk, and a broken permission chain step by step. A safe, read-only inspection checklist for both operating systems.
1. What Is a Filesystem?
A filesystem is the way an operating system organizes and finds data on disk. Every piece of stored data — a program, a photo, a configuration file — lives inside a file, and files are organized into directories (folders), which can contain other directories. The location of a file, written out from the top of that structure, is its path.
On Linux and macOS there is exactly one directory tree, and it starts at a single root, written as a forward slash: /. Windows assigns a separate letter to each disk (C:, D:); Unix-like systems instead mountevery additional disk, partition, or network share onto some folder inside that one tree. A second hard drive doesn't become a new root — it becomes, say, /mnt/backup, and once mounted, files under it are addressed exactly like any other path. The folder a filesystem is attached to is called its mount point (more in section 13).
A path is absolute when it starts from the root, like /etc/nginx/nginx.conf — it means the same thing no matter where you currently are. A path is relativewhen it doesn't start with a slash, like logs/error.log— it's resolved against your current directory, so the same relative path can point to different files depending on where you run it from.
Don't confuse the root directory (/, the top of the tree) with the root user— the administrative account, sometimes called the superuser, that can read and write anything on the system. They share a name by convention, not because they're the same thing; every regular user's files still live somewhere under / too.
Both Linux and macOS filesystems are, by default, case-sensitive: Report.txt and report.txtare different files. macOS is a partial exception — its default APFS volumes are usually formatted case-insensitive but case-preserving, so two files differing only in case can't normally coexist, which occasionally surprises developers moving a project from Linux. Any file or directory whose name starts with a dot, like .env or .gitignore, is a hidden file— the shell and file managers skip it by default, but it's an ordinary file in every other respect.
The tilde, ~, is shorthand for the current user's home directory — /home/alex on Linux or /Users/alex on macOS. You'll see it constantly in documentation and shell prompts.
You'll often hear "everything is a file" used to describe Unix-like systems. It's a useful simplification — a running process's state, a hardware device, and a network socket can all be represented and interacted with through something that looks like a file — but it isn't perfectly literal. Some of these (covered in section 12) are virtual: they don't hold bytes on disk, they're a live window the kernel generates on demand when you read them.
Safe, read-onlypwd # print the absolute path of your current directory
ls # list files in the current directory
ls -la # list all files, including hidden ones, with permissions/owner/size
cd /etc # change into an absolute path
cd ~ # change into your home directorypwd("print working directory") confirms exactly where you are before you run anything else — useful before trusting a relative path. ls -la adds two things to a plain listing: -a reveals dotfiles, and -l switches to the "long" format, which is where you'll read permissions and ownership (see section 14).
2. A Visual Tour of the Filesystem
This is the shape of a typical Linux root directory. macOS shares most of these names (it's Unix-based) but reserves several for its own use — see the caveats after the table.
/
├── bin → essential command binaries
├── boot → boot loader files, kernel images
├── dev → device files (virtual)
├── etc → system-wide configuration
├── home → regular users' personal files
├── opt → optional third-party software
├── proc → live process/kernel info (virtual)
├── root → the root user's home directory
├── run → runtime state since last boot (virtual)
├── sbin → essential admin binaries
├── srv → data served by this system (less common)
├── tmp → temporary files, cleared periodically
├── usr → installed programs and shared resources
└── var → logs, caches, and other changing data| Path | Analogy | Contains | Developer uses it for | Safe to inspect? | Linux/macOS |
|---|---|---|---|---|---|
| /etc | City hall | Machine-wide configuration files | Web server, SSH, DNS, cron config | Yes, read-only | Both (contents differ) |
| /home or /Users | Residents' homes | Per-user files and dotfiles | SSH keys, shell config, app data | Yes (your own) | Linux: /home · macOS: /Users |
| /var | City records office | Logs, caches, spool, site data | Reading logs, finding /var/www | Yes, mostly read-only | Both (structure differs) |
| /var/log | Incident reports | Application and system logs | Debugging errors and crashes | Yes — watch for secrets | Both (macOS also has unified log) |
| /tmp | Shared workbench | Short-lived temporary files | Scratch space, upload staging | Yes, don't rely on persistence | Both |
| /usr | Tool warehouse | Installed programs, libraries, docs | which php, which node | Yes, read-only | Both |
| /bin, /sbin | Toolbelt | Essential command binaries | Rarely browsed directly | Yes, read-only | Both (often symlinked into /usr) |
| /opt | Annex building | Optional third-party applications | Manually installed software (e.g. some DB engines) | Yes, read-only | Both |
| /proc | Live security-camera feed | Running process and kernel state (virtual) | Diagnosing CPU/memory/process info | Yes, read-only | Linux only |
| /dev | Utility hookups | Device files (virtual) | Rarely touched directly | Look, don't write | Both (contents differ) |
| /run | Whiteboard notes | Runtime state since boot (virtual) | PID files, sockets | Yes, read-only | Linux (modern) |
| /mnt, /media | Loading dock | Manually or auto-mounted external storage | Attached disks, USB drives | Yes | Linux (macOS uses /Volumes) |
3. /etc: System Configuration
/etcis where a Unix-like system traditionally keeps machine-wide configuration — the closest thing to a government office holding the rules everyone on the machine follows. That's a convention, not a hard rule: plenty of applications (databases, language runtimes, containerized services) keep their own configuration somewhere else entirely, often inside their own installation directory or a project folder. Treat /etc as the first place to look for system-level settings, not the only place.
Not every file below exists on every machine — presence depends on your distribution and what's installed.
| Path | Purpose | Platform |
|---|---|---|
| /etc/hosts | Manual hostname-to-IP mappings, checked before DNS (see section 4) | Both |
| /etc/hostname | This machine's configured hostname | Linux |
| /etc/resolv.conf | DNS resolver configuration (see section 5) | Both |
| /etc/passwd | User account records (no passwords stored here despite the name) | Both |
| /etc/group | Group definitions and membership | Both |
| /etc/shadow | Hashed user passwords, root-readable only | Linux |
| /etc/ssh/sshd_config | SSH server (sshd) configuration | Linux (server); macOS has its own path under /etc/ssh |
| /etc/nginx | Nginx configuration if installed | Both, if Nginx is installed |
| /etc/apache2 | Apache configuration (Debian/Ubuntu naming) | Linux (RHEL family uses /etc/httpd) |
| /etc/php | PHP configuration (version-specific subfolders) | Linux, distro-dependent layout |
| /etc/systemd/system | Custom and overridden systemd service unit files | Linux only |
| /etc/cron.d, /etc/crontab | System-wide scheduled task definitions | Linux; macOS deprecated cron in favor of launchd |
| /etc/environment | System-wide environment variables | Linux |
Sensitive file — do not expose: /etc/shadowstores hashed passwords and is readable only by root by design. Its purpose is authentication, not something to view, copy, or paste anywhere — including into a support ticket, a chat window, or an AI tool — even in a "just checking" moment.
4. Deep Dive: /etc/hosts
First, the correction that matters most for searchability: the file is /etc/hosts, plural — never /etc/host. It stores mappings for potentially many hosts, hence the name.
The hosts file is a plain-text table your operating system checks to translate a hostname into an IP address, before it ever asks a DNS server. Each meaningful line pairs an IP address with one or more hostnames:
127.0.0.1 92nodes.testThat single line tells your machine — and only your machine — that requests for 92nodes.test should go to 127.0.0.1, the loopback address that always means "this computer." Open a browser afterward and 92nodes.test resolves to whatever is running locally, without touching the public internet at all.
This is different from DNS in one crucial way: DNS is a distributed, public system that answers the same question for anyone in the world; the hosts file is a private override that exists only on the machine where it was edited. Developers use it to point a friendly local domain at a project running on localhost, to test a staging environment under its real production hostname before DNS is switched over, or to temporarily redirect a domain during a server migration without touching public records. None of that creates, changes, or previews a real DNS record — anyone else visiting that domain still gets whatever the actual DNS records say.
Because both the operating system and the browser cache DNS lookups, a hosts-file edit can appear not to work immediately even though it's correct — the old answer is just still cached somewhere. Restarting the browser or flushing the OS DNS cache usually resolves the apparent delay.
Inspecting and editing safely
Requires administrator privileges: Editing /etc/hosts requires sudo on Linux and an admin account plus sudo on macOS. Before changing it, view the current contents and make a backup copy — never use a command that overwrites the whole file.
cat /etc/hosts # view current contents (Linux & macOS)
sudo cp /etc/hosts /etc/hosts.bak.$(date +%F) # back up before editing (Linux & macOS)
sudo nano /etc/hosts # open in an editor and add one line at a timeAdd a new line under the existing entries rather than replacing the file's contents — the file typically already contains required entries such as a loopback line for localhost, and removing those can break local tools that depend on them.
To verify resolution after editing:
LinuxmacOSSafe, read-onlyping 92nodes.test # both — confirms an IP was resolved
getent hosts 92nodes.test # Linux — shows exactly what the resolver returned
dscacheutil -q host -a name 92nodes.test # macOS — queries the local Directory Service cachepingonly proves that a hostname resolved to an IP address and that address replied to an ICMP packet — it says nothing about whether a web server is actually listening, whether it's returning errors, or whether TLS is configured correctly. Treat a successful ping as one data point, not a website health check.
5. DNS and /etc/resolv.conf
When a hostname isn't found locally, the system asks a nameserver — a DNS server responsible for translating names into IP addresses. On Linux, /etc/resolv.conf traditionally lists which nameservers to use and any search domains (suffixes automatically appended to short, unqualified hostnames).
On most current Linux distributions, this file is no longer meant to be hand-edited: it is generated and rewritten automatically by systemd-resolved, NetworkManager, or a DHCP client hook, often ending up as a symlink. A manual edit can appear to work for a moment and then silently revert on the next network event or reboot. If you need a persistent change, configure it through the resolver service itself.
Safe diagnostic commands:
LinuxmacOSSafe, read-onlydig example.com # both (if installed) — detailed DNS query and response
nslookup example.com # both — simpler DNS query tool
resolvectl status # Linux (systemd-resolved) — shows active DNS config per interface
scutil --dns # macOS — shows the effective resolver configurationOn macOS, don't try to manage DNS long-term by editing /etc/resolv.conf directly — macOS builds its resolver configuration dynamically and expects DNS changes through System Settings → Network, or networksetup on the command line, not a hand edit that the system will overwrite.
6. /var: Changing Application Data
/var stands for variable data — anything the system expects to keep growing or changing while the machine runs, as opposed to /etc's mostly-static configuration.
| Path | Typically holds |
|---|---|
| /var/log | Application and system logs (section 7) |
| /var/www | A common convention for website files on Debian/Ubuntu — not mandatory |
| /var/lib | Persistent application state, e.g. database data directories |
| /var/cache | Regenerable cached data (package manager caches, etc.) |
| /var/spool | Queued work waiting to be processed, e.g. cron or mail queues |
| /var/tmp | Temporary files meant to survive a reboot, unlike /tmp |
/var/wwwis a packaging convention on Debian/Ubuntu-based Nginx and Apache installs, not a filesystem requirement — plenty of real servers don't have it. Laravel, Node.js, Docker, databases, and web servers each decide their own default locations for logs and state, and any of them can be reconfigured to write elsewhere; the safest way to find the real path is to check the relevant configuration file rather than assume a convention holds.
7. Logs: Where Errors Actually Live
Linux logging generally comes in two forms today: traditional plain-text files under /var/log, and the systemd journal, a structured, indexed log store read with journalctl. Many services write to both; some, especially ones that log only via systemd, only exist in the journal. macOS uses its own unified logging system instead, read with log show and log stream.
| Log | Typical path / command | Platform |
|---|---|---|
| System messages | /var/log/syslog (Debian/Ubuntu) or /var/log/messages (RHEL family) | Linux, distro-dependent |
| Authentication attempts | /var/log/auth.log (Debian/Ubuntu) or /var/log/secure (RHEL family) | Linux, distro-dependent |
| Nginx | /var/log/nginx/access.log, /var/log/nginx/error.log | Linux, if Nginx installed |
| Apache | /var/log/apache2/ (Debian/Ubuntu) or /var/log/httpd/ (RHEL family) | Linux, distro-dependent |
| PHP-FPM | Pool-specific, often /var/log/php*-fpm.log | Linux, version-dependent |
| MySQL / MariaDB | Often /var/log/mysql/ or set by the server's own config | Linux, config-dependent |
| Laravel application | storage/logs/laravel.log inside the project | Both (it's app-level, not OS-level) |
| Docker container | docker logs <container>, not a plain file you browse directly | Both, wherever Docker runs |
| Everything under systemd | journalctl, optionally -u <service> | Linux only |
| macOS system-wide | log show / log stream | macOS only |
Useful commands:
LinuxSafe, read-onlytail -f /var/log/nginx/error.log # follow a file live as new lines are written
journalctl -u nginx # all journal entries for the nginx service
journalctl -u nginx --since "30 minutes ago"
sudo systemctl status nginx # quick health snapshot + recent log linesmacOSSafe, read-onlylog show --predicate 'process == "sshd"' --last 30m
log stream --predicate 'process == "nginx"'A few tools you'll lean on constantly: tail prints the end of a file (add -f to keep watching it live); less opens a file for scrollable, searchable reading without loading it all into your terminal history; grep filters lines matching a pattern, e.g. grep "500" access.log. journalctl's -u flag filters by service, and --since filters by time — both make a large journal actually searchable.
Before you paste a log anywhere: Production logs routinely contain email addresses, IP addresses, session tokens, API keys, internal hostnames, and other personal or sensitive data. Read and redact before pasting a log into a public chat, a GitHub issue, or an AI assistant — treat it the same way you'd treat a database export.
Scenario: a website returns 502 Bad Gateway
A 502 means the web server (Nginx or Apache) is up and reachable, but the application it forwards requests to — the upstream — didn't answer correctly. Work through it in order:
- Confirm the response with a direct request rather than trusting the browser cache.
- Check the web server's error log for the specific upstream failure reason.
- Check whether the application process (PHP-FPM, Node.js, a queue worker) is actually running.
- Check the upstream service's own logs — PHP-FPM, Node.js, or whatever sits behind the proxy.
- Check that the port or Unix socket the web server expects to reach is the one the app is actually listening on.
- Check file and socket permissions between the web server user and the application user.
- Check disk space and memory — an OOM-killed process or a full disk can cause exactly this.
- Review whatever configuration changed most recently — a deploy, a config reload, a certificate renewal.
8. /home, /root, and macOS /Users
Every regular user gets a personal directory — /home/alex on Linux, /Users/alex on macOS — holding their files and personal configuration. The root user is the one exception: its home directory is /root, a separate, dedicated location rather than a folder inside /home.
User-specific configuration usually lives as dotfiles directly in the home directory: ~/.ssh for SSH keys and known hosts, ~/.config for many modern applications' settings, and shell startup files like .bashrc, .profile, or .zshrc (macOS has used zsh as its default shell since Catalina).
A common server-management habit worth avoiding: It's common on a quickly-set-up server to log in as root and put everything — application code, deploy scripts, cron jobs — directly under /root. It works, but it means every process the application runs also runs with full root privileges, so a bug or a compromised dependency has the run of the entire machine. Applications should run under a dedicated, non-root user with access only to what they actually need.
Inside ~/.ssh you'll typically find:
| File | Purpose |
|---|---|
| authorized_keys | Public keys allowed to log in as this user via SSH |
| id_ed25519 / id_rsa (no extension) | A private key — proves your identity; never share or publish this |
| id_ed25519.pub / id_rsa.pub | The matching public key — safe to share, this is what goes in authorized_keys |
| known_hosts | Fingerprints of remote hosts you've previously connected to |
Never publish a private key: A private key file (no .pub extension) proves your identity to every server that trusts its matching public key. Anyone who obtains it can authenticate as you. Keep its permissions restrictive (commonly 600, owner read/write only — SSH itself will refuse to use an overly permissive key file) and never commit it, paste it, or attach it anywhere.
9. /usr, /bin, /sbin, and PATH
/usrholds the bulk of a system's installed programs, libraries, and shared resources — /usr/bin for user commands, /usr/sbin for administrative commands, and /usr/local (with its own bin subfolder) reserved for software installed manually or outside the distribution's package manager. That separation is exactly why manually built scripts and tools are conventionally placed in /usr/local/bin — the package manager will never overwrite anything there.
On many current Linux distributions, /bin and /sbin are symbolic links into their /usr equivalents — a "merged /usr" layout that simplifies having a separate /usr partition. You can usually still use either path; they resolve to the same files.
Which actual program runs when you type a command name is decided by PATH, an environment variable listing directories to search, in order, for a matching executable. When two installs of PHP or Node.js exist on the same machine — a system package and a manually installed version, for example — whichever directory appears first in PATHwins, which is a common source of "it works in my terminal but the wrong version runs in production."
which php # path to the php binary that would actually run
which node
command -v nginx # POSIX-portable equivalent of which
type php # shows whether it's a binary, alias, or shell built-in
echo "$PATH" # the ordered list of directories being searchedmacOS Homebrew note — don't assume it's installed: If Homebrew is installed, Apple Silicon Macs use /opt/homebrew as its prefix, while Intel Macs traditionally use /usr/local. Neither path implies Homebrew is present — check with command -v brew rather than assuming.
10. /opt: Optional Third-Party Software
/optis conventionally used for self-contained, optional third-party applications that don't integrate into the distribution's normal package layout — for example, a vendor-supplied database engine or a commercial application often installs under /opt/vendor-name. The practical difference from /usr/local is mostly about self-containment: /usr/local expects software to slot into the standard bin/lib/share layout, while /opt packages usually bundle their own internal directory structure. Not every third-party application uses /opt — plenty install through the normal package manager into /usr instead.
11. /tmp and Temporary Files
/tmp is shared scratch space that any user or process can write to. Many systems clear it automatically on reboot or on a schedule, and permissions are commonly world-writable with the sticky bit set — a special permission flag (shown as a tat the end of the permission string) meaning that even though everyone can create files here, only a file's owner (or root) can delete or rename it. That's what stops one user from deleting another user's temporary files out from under them.
/var/tmp serves a similar purpose but is conventionally expected to survive across a reboot, whereas /tmpoften does not — check your specific distribution's configuration rather than assuming either behavior.
Because it's world-writable and routinely cleared, never treat /tmpas durable storage for anything that matters — a generated report a user needs to download later, an upload awaiting processing, or any file your application can't afford to lose. On a live server, don't manually clear /tmp either; another process may currently depend on a file sitting there.
12. /proc, /sys, /dev, and /run
Linux-specificThese aren't ordinary folders holding bytes on disk — they're virtual filesystemsthe kernel generates on the fly. Reading a "file" here doesn't read stored data; it asks the kernel a live question and gets back a live answer.
| Path | What it is |
|---|---|
| /proc/cpuinfo | Details about the CPU(s) the kernel sees |
| /proc/meminfo | Live memory usage statistics |
| /proc/<PID> | A directory of live information about one specific running process |
| /sys | Kernel and device state, organized around the kernel's internal device model |
| /dev/null | Discards anything written to it; reading it returns nothing (end of file immediately) |
| /dev/random, /dev/urandom | Sources of random bytes used for cryptographic and general randomness |
| /run | Runtime state created since the last boot — PID files, sockets — cleared on reboot |
cat /proc/cpuinfo
cat /proc/meminfo
ls /procLook, don't write: Writing into /proc, /sys, or device files under /dev can change live kernel or hardware behavior immediately, with no confirmation prompt. These sections are for reading; leave writing to tools specifically designed for it.
13. Mount Points: /mnt, /media, and Drives
Mounting is the act of attaching a filesystem — a disk partition, a USB drive, a network share — onto a folder in the tree, so its files become reachable at that path. /mnt is the conventional location for manually mounted filesystems; /media is typically used for removable media mounted automatically by the desktop environment. A disk can be physically connected and fully functional but invisible in the place you expect, simply because nothing has mounted it yet.
mount # list currently mounted filesystems
findmnt # a more readable, tree-formatted view of mounts
lsblk # list block devices and their mount points
df -h # disk usage per mounted filesystem, human-readable/etc/fstabdefines which filesystems should be mounted automatically at boot and where. It's read very early in the boot process, so a malformed entry can prevent the system from booting normally.
Configuration-changing — back up first: Before editing /etc/fstab, back up the current file, and test a new entry with a manual mount command before adding it permanently. On a remote server, keep a separate access path (a rescue console, a second SSH session) open while you test, in case a bad entry affects the next boot.
macOS mounts external and network volumes under /Volumes, each appearing as its own named folder there rather than under /mnt.
14. File Ownership and Permissions
Every file and directory has an owner (a user) and a group, and grants three kinds of access — read, write, and execute— separately to three parties: the owning user, the owning group, and everyone else ("others"). On a directory, execute means something slightly different than on a file: it controls whether you can enter or look inside that directory at all, not whether you can "run" it.
Safe, read-onlyls -l
# -rw-r--r-- 1 alex developers 1240 Sep 12 10:03 example.txt
# drwxr-xr-x 2 alex developers 4096 Sep 12 10:03 scriptsReading the ten-character string from the left: the first character is - for a regular file or d for a directory. The remaining nine come in three groups of three — owner, group, others — each showing r, w, x, or - for "not granted." -rw-r--r-- means the owner can read and write, and the group and everyone else can only read.
The same permissions are also written as a three-digit numeric mode, adding 4 (read), 2 (write), and 1 (execute) for each of owner/group/others:
| Mode | Meaning | Typical use |
|---|---|---|
| 644 | Owner: read/write · Group & others: read only | A normal file, e.g. an uploaded asset |
| 755 | Owner: read/write/execute · Group & others: read/execute | A script or a directory that needs to be entered/listed |
| 600 | Owner: read/write · Group & others: nothing | A private key or credentials file |
chmod 644 example.txt # set permissions by numeric mode
chmod 755 scripts # directories generally need the execute bit to be enterable
chown user:group example.txt # change owner and group (usually requires sudo on files you don't own)777grants read, write, and execute to everyone — owner, group, and others alike. It's rarely the right fix: a "permission denied" error almost always means the wrong user or group owns the file, or the process is running as the wrong user, not that the permission bits need to be maxed out. On a shared or internet-facing server, 777also means any user or compromised process on that machine can modify the file. Diagnose the actual owner and the actual running user first, and grant only what's needed — the principle of least privilege.
Be careful with recursive changes: A recursive chmod -R or chown -Rtouches every file and subdirectory underneath the path you give it. Pointed at the wrong directory — or run from the wrong current directory with a relative path — it can silently break an entire application's file permissions, or worse, system directories. Double-check the exact path before running either recursively, and prefer scoping to the smallest directory that actually needs the change.
A common real example: a Laravel application needs its web-server process to be able to write inside the storage and bootstrap/cache directories, because that's where it writes logs, compiled views, and framework cache. The concept is: the user your web server actually runs as (commonly something like www-dataon Debian/Ubuntu, but this varies by distribution and hosting setup) needs write access there, through ownership or group membership — not through opening the directory to every user on the system. There's no single universal user/group to prescribe here because server setups differ; check what user your web server and PHP-FPM pool are actually configured to run as before deciding on ownership.
15. Services: systemd vs launchd
A service(or daemon) is a program meant to run continuously in the background, independent of any logged-in user — a web server, a database, an SSH daemon. That's distinct from just having the application's files on disk: files are static until something starts a process from them, and a service manager is what starts, stops, restarts, and supervises that process automatically, including across reboots.
Linux-specificMost current major distributions (Ubuntu, Debian, Fedora, RHEL-family, and others) use systemd as their service manager, controlled with systemctl, driven by unit files that describe how to start a service, what it depends on, and how to restart it if it fails. Arch Linux also uses systemd by default. Editing a vendor-shipped unit file directly is usually a bad idea — package updates can overwrite it — so systemd provides an override mechanism (systemctl edit) instead.
systemctl status nginx # safe, read-only — current state and recent log lines
systemctl restart nginx # state-changing — restarts the service, needs sudo
systemctl enable nginx # state-changing — makes it start automatically on boot, needs sudo
journalctl -u nginx # safe, read-only — this service's journal entriesmacOS-specificmacOS does not use systemd — systemctl simply doesn't exist there. Instead it uses launchd, configured through XML property-list files: LaunchAgents run on behalf of a logged-in user, while LaunchDaemons run system-wide, independent of any user session — the closest macOS equivalent to a Linux system service.
16. Processes, Ports, and Sockets
Every running program is a process, identified by a numeric process ID (PID). A port is a numbered endpoint a network service listens on (Nginx commonly listens on 80 and 443); a process bound to a port is said to be listening there. A socket is the underlying communication endpoint — most commonly a TCP socket over the network, or a Unix socket, a file-based communication channel used for fast local-only connections, which is exactly how Nginx often talks to PHP-FPM on the same machine.
This connects directly back to the 502 scenario in section 7: Nginx returns 502 specifically when it can reach the upstream address it's configured with, but nothing is listening there — the application process crashed, hasn't started yet, or is listening on a different port or socket path than Nginx expects.
LinuxmacOSSafe, read-onlyps aux # both — list running processes
top # both — live, updating process view
lsof -i :3000 # both, if installed — which process owns port 3000
ss -ltnp # Linux only — listening TCP sockets with owning process
curl -I http://localhost:3000 # both — just the response headers, quick liveness checkss is a Linux-specific tool (the modern replacement for the older netstat). lsofis useful on both Linux and macOS wherever it's installed, and is the standard choice on macOS specifically since ssisn't available there.
17. Where Web-Development Files Live
Nginx
Path varies by distributionThe main configuration file is typically /etc/nginx/nginx.conf, with individual site configs conventionally split into /etc/nginx/sites-available/ and symlinked into /etc/nginx/sites-enabled/ on Debian/Ubuntu, though not every install uses that split. Access and error logs default to /var/log/nginx/. Always test a configuration before reloading it:
sudo nginx -t # validates syntax without applying anything
sudo systemctl reload nginx # state-changing — applies a valid config with no dropped connectionsApache
Path varies by distributionOn Debian/Ubuntu, configuration lives under /etc/apache2/, with virtual hosts in sites-available/ and logs in /var/log/apache2/. RHEL-family distributions instead use /etc/httpd/ and /var/log/httpd/. Enabled modules are configured separately from virtual hosts in both layouts.
PHP and PHP-FPM
PHP's CLI and FPM (FastCGI Process Manager) modes commonly load different php.ini files, and FPM adds its own pool configuration controlling how many worker processes run and what user they run as. Find exactly which configuration is actually loaded rather than assuming:
php --ini # shows which php.ini (and any scanned additional .ini files) is loaded
php -i # full, detailed PHP configuration dumpLaravel
Within a Laravel project:
| Path | Purpose |
|---|---|
| .env | Environment-specific config: database credentials, app key, service secrets |
| storage/logs/laravel.log | The application's own log file |
| storage/framework | Compiled views, sessions, and framework-generated cache |
| bootstrap/cache | Cached configuration and route files for performance |
| public | The actual web-server document root — the only folder meant to be exposed |
Never expose .env or commit it: .env holds database credentials, the application encryption key, and often third-party API secrets. It should never be committed to version control, exposed through a misconfigured web root, or pasted anywhere outside a secrets manager. The scheduler and queue workers (typically run via cron or a supervised process, respectively) also read from it, so a leaked .envcan expose background jobs' access too, not just the web-facing app.
Node.js and Next.js
Source files are just files until a process runs them — the code on disk and the currently running Node.js process are two different things, and editing the source doesn't change the running process until it's restarted or a watcher reloads it. Common conventions: .env, .env.local, and other .env.* variants for environment configuration; a build output directory (Next.js uses .next); and logs that depend entirely on how the process is run — captured by a process manager (like PM2), by the container runtime, or by whatever system service wraps it, rather than a single fixed path.
Docker
Docker manages its own internal storage for images, containers, and volumes — don't reach into Docker's internal data directory by hand. Use Docker's own commands instead:
Safe, read-onlydocker ps # running containers
docker logs <container> # a container's stdout/stderr logs
docker volume ls # named volumes Docker managesEditing files under Docker's internal storage location directly can corrupt image and container state in ways that are hard to diagnose — everything you need is reachable through the Docker CLI.
Databases
A database engine's data directory is not meant to be browsed or edited by hand — its internal file format is specific to that engine and version, and manual changes can corrupt it beyond recovery. Use the database's own client tools, proper dumps, and tested backup/restore procedures for anything beyond read-only curiosity about where the files live.
18. Linux vs macOS Quick Comparison
| Area | Linux | macOS |
|---|---|---|
| User home directory | /home/<user> | /Users/<user> |
| System configuration | /etc, distro-dependent contents | Mostly under /etc and /Library, plus System Settings |
| Hosts file | /etc/hosts | /etc/hosts (same path and format) |
| Temporary files | /tmp, /var/tmp | /tmp, /var/tmp (same convention) |
| Logs | /var/log files + systemd journal (journalctl) | Unified logging (log show / log stream) |
| Services | systemd (systemctl) | launchd (LaunchAgents/LaunchDaemons) |
| External drives | /mnt, /media | /Volumes |
| Package-manager locations | Distro-dependent (apt, dnf, pacman, etc.) | /opt/homebrew (Apple Silicon) or /usr/local (Intel), if Homebrew is installed |
| Application settings | ~/.config, /etc | ~/Library/Preferences, /Library |
| Common shell | bash or zsh, distro-dependent default | zsh (default since macOS Catalina) |
| DNS inspection | resolvectl status, dig, nslookup | scutil --dns, dig, nslookup |
Exact defaults vary by distribution and by macOS version — verify on the specific machine rather than assuming any single row applies everywhere.
19. Five Real-World Troubleshooting Exercises
Scenario 1: A local custom domain does not open
Symptom: Visiting 92nodes.test in the browser fails to load or shows the wrong site.
Likely layer: Hostname resolution (/etc/hosts) or the local web server's own virtual-host configuration.
cat /etc/hosts— confirm the line actually exists and points to the right IP.getent hosts 92nodes.test(Linux) ordscacheutil -q host -a name 92nodes.test(macOS) — confirm what the resolver returns right now.- Check the local web server's site configuration for a matching
server_name/ virtual-host entry.
How to interpret results: If the hosts file entry is correct but the resolver still returns nothing, a cache is likely stale. If resolution is correct but the wrong content loads, the web server has no virtual host matching that name and is falling back to a default one.
Common incorrect fix: Repeatedly refreshing the browser or trying a different browser without checking the hosts file or resolver at all.
Correct approach: Verify the hosts entry, flush the relevant DNS cache (browser and/or OS), then confirm the web server has a virtual host bound to that exact hostname.
Scenario 2: Nginx returns 502 Bad Gateway
Symptom: The site loads Nginx's own error page instead of the application.
Likely layer: Nginx is fine; the upstream application process is the suspect.
tail -n 50 /var/log/nginx/error.log— the exact upstream failure reason.systemctl status php8.3-fpm(or the relevant service) — is it actually running?ss -ltnporlsof -i :3000— is anything listening on the expected port/socket?
How to interpret results: "Connection refused" means nothing is listening there at all — the process is down. "Permission denied" on a Unix socket usually means an ownership mismatch between the Nginx user and the socket's permissions.
Common incorrect fix: Restarting Nginx repeatedly — Nginx itself isn't the broken component here.
Correct approach: Restart or fix the actual upstream service, confirm it's listening on the exact address Nginx is configured to proxy to, and check the socket/port permissions match.
Scenario 3: The server reports 'No space left on device'
Symptom: Writes fail, uploads fail, or logs stop being written entirely, even though df shows free space in some views.
Likely layer: Filesystem capacity — either raw disk space or, less obviously, inode exhaustion.
df -h— free space per mounted filesystem.df -i— free inodes per filesystem (a separate limit from raw space).du -shon suspect directories (e.g. log or cache folders) to see what's actually consuming space.
How to interpret results: df -h showing available space while writes still fail points at inode exhaustion (df -i near 100%) — usually caused by millions of tiny files, not large ones. df -h itself near 100% is the more common plain capacity issue.
Common incorrect fix: Deleting unfamiliar files or directories you don't recognize to free space quickly.
Correct approach: Identify what's actually consuming space or inodes with du and targeted inspection, then remove or rotate specifically identified, understood files — log rotation and old build/cache artifacts are common legitimate culprits.
Scenario 4: Laravel cannot write to logs or cache
Symptom: Application errors reference storage/logs/laravel.log or bootstrap/cache not being writable.
Likely layer: Ownership and permissions mismatch between the web-server/PHP-FPM user and the directory owner.
ls -la storage bootstrap/cache— current owner, group, and mode.- Check which user your PHP-FPM pool is configured to run as (in its pool config file).
- Compare the two — is the running user the owner, or in the owning group, of those directories?
How to interpret results: If the directory is owned by your deploy user (e.g. yourself, via SSH) but PHP-FPM runs as a different user (commonly www-data on Debian/Ubuntu), that mismatch is the entire problem.
Common incorrect fix: chmod -R 777 storage bootstrap/cache — it works around the symptom while leaving the underlying ownership wrong and opening the directory to every user on the box.
Correct approach: Set the directory's group to match the PHP-FPM user's group (or its owner, depending on your setup) and grant that group write access, rather than opening access to everyone.
Scenario 5: The application works in Terminal but not after reboot
Symptom: Running the app manually from a terminal session works fine; after a server reboot, it isn't running at all, or behaves differently.
Likely layer: Environment differences between an interactive shell session and how the service manager starts the process automatically.
systemctl status your-service— is it enabled to start on boot, and did it actually start?journalctl -u your-service --since today— what error, if any, appeared at startup.- Compare
echo "$PATH"in your interactive shell against the environment defined in the service's unit file.
How to interpret results: A working manual run plus a failing automatic start almost always means the service's environment (PATH, working directory, environment variables) differs from your interactive shell's — or the service was simply never enabled to start on boot in the first place.
Common incorrect fix: Concluding the application code itself is broken and starting to debug or rewrite application logic.
Correct approach: Confirm the service is enabled (systemctl enable), and make the unit file explicit about its working directory, PATH, and any required environment variables instead of relying on values only present in an interactive login shell.
20. Safe Server-Inspection Checklist
A read-only walkthrough to run on an unfamiliar or misbehaving server before changing anything.
LinuxSafe, read-onlypwd # where you are
whoami # which user you're running as
uname -a # kernel and architecture info
ls -la # what's in this directory, including hidden files
df -h # disk space per filesystem
df -i # inode usage per filesystem
free -h # memory usage
ps aux # running processes
ss -ltnp # listening ports and owning processes (may need sudo)
systemctl --failed # any services currently in a failed state
journalctl -p err --since today # today's error-level journal entriesss -ltnp, systemctl --failed, and full process ownership detail may need sudo to show complete information. free, ss, systemctl, and /proc are not standard macOS tools — use the macOS checklist below instead on a Mac.
pwd
whoami
uname -a
ls -la
df -h
top -l 1 | head -n 15 # one-shot process/resource snapshot
ps aux
lsof -i -P -n | grep LISTEN # listening ports and owning processes
log show --last 1h --predicate 'eventMessage contains "error"'Even read-only inspection can expose data: Process lists, port listings, and logs can reveal internal hostnames, usernames, and service details. Screenshotting or pasting raw output from these commands into a public place carries the same exposure risk as pasting a log file — review before sharing.
21. Dangerous Shortcuts to Avoid
None of the commands below are shown as copyable code blocks on purpose — they're listed here as things to recognize and avoid, not to run.
- Running a
sudocommand copied from a forum or chat without reading exactly what it does first. - Reaching for a recursive world-writable permission change (the pattern often written as
chmod -R 777) as a generic fix for any "permission denied" error. - Deleting unfamiliar files or folders under
/var,/etc,/usr,/Library, or/Systemjust because you don't recognize them. - Overwriting the entire
/etc/hostsfile instead of adding a line to it. - Hand-editing an automatically generated DNS configuration file and expecting the change to persist.
- Publishing a
.envfile, a private key, or a raw production log anywhere public. - Running an application process as the root user "to make a permissions error go away."
- Killing a process by PID without first confirming what it actually is and what depends on it.
- Directly modifying files inside Docker's internal storage or a database engine's data directory.
- Recursively changing ownership starting from
/or another very broad path. - Testing a destructive command for the first time directly on a production server instead of a disposable environment.
22. Quick-Reference Cheat Sheet
| Goal | Linux | macOS | Watch out for |
|---|---|---|---|
| Edit local hostname mapping | sudo nano /etc/hosts | sudo nano /etc/hosts | Add a line, don't overwrite the file |
| Find web-server logs | /var/log/nginx/ or /var/log/apache2/ | Depends on how the server was installed | Path varies by distro and install method |
| Check service status | systemctl status <service> | launchctl list, or the app's own status | macOS has no systemctl |
| Find a process using a port | ss -ltnp | lsof -i :<port> | ss is Linux-only |
| Check disk usage | df -h and df -i | df -h | df -i (inodes) is the one people forget |
| Find PHP configuration | php --ini | php --ini | CLI and FPM can load different files |
| Inspect DNS configuration | resolvectl status | scutil --dns | Don't hand-edit auto-generated resolv.conf |
| Find SSH configuration | /etc/ssh/sshd_config | /etc/ssh/sshd_config | Changes need a service reload to take effect |
| Inspect application logs | journalctl -u <service> or the app's own log file | log show --predicate ... | Redact before sharing |
For authoritative detail beyond this guide: the Filesystem Hierarchy Standard defines the Linux directory conventions covered above, systemd's own documentation covers service units in full, and Apple's launchd documentation covers LaunchAgents and LaunchDaemons.
23. Frequently Asked Questions
Q: What is the difference between /etc/host and /etc/hosts?
A: There is no /etc/host on Linux or macOS — the file is always named /etc/hosts, plural, because it stores mappings for multiple hosts. If a command or tutorial references /etc/host, that's a typo; editing a nonexistent file will do nothing.
Q: Is /etc/hosts used before DNS?
A: On most default configurations, yes. The system's name service switch (controlled by /etc/nsswitch.conf on Linux) typically checks local sources like /etc/hosts before falling through to DNS resolvers. That's exactly why editing /etc/hosts can override a real domain's DNS record on your machine alone.
Q: Where are Linux logs stored?
A: Traditional text logs live mainly under /var/log (syslog or messages, auth.log or secure, and per-service folders like nginx or mysql). Most modern distributions also keep a structured, indexed copy in the systemd journal, readable with journalctl, which can cover services that don't write their own log files.
Q: Where are macOS logs stored?
A: macOS uses the unified logging system instead of plain text files in most cases. Use log show or log stream from Terminal to read them; some older or third-party tools still write plain files under /var/log or ~/Library/Logs.
Q: What is the difference between /home and /root?
A: /home/<username> directories hold regular users' personal files and configuration. /root is a separate, dedicated home directory that belongs only to the root superuser account — it is not a subfolder of /home.
Q: What is the difference between /bin, /usr/bin, and /usr/local/bin?
A: /bin traditionally held essential system binaries needed even with /usr unmounted; on modern merged-/usr distributions it's a symlink into /usr/bin. /usr/bin holds the bulk of standard installed programs. /usr/local/bin is reserved for software installed manually or outside the distribution's package manager, which is why it's searched but never touched by the package manager itself.
Q: Why should I not use chmod 777?
A: 777 grants every user on the system full read, write, and execute access, which usually only masks a permissions problem rather than fixing its actual cause (wrong owner, wrong group, or a process running as the wrong user). It also creates a real security exposure on any shared or internet-facing server.
Q: Where should website files be stored on Linux?
A: There's no single mandatory path. /var/www is a common convention on Debian/Ubuntu-based Nginx and Apache setups, but many stacks use /srv, a home directory, or a path entirely defined by a deployment tool, container image, or hosting platform. Check your web server's configuration file rather than assuming a fixed location.
Q: Why is /var/www missing on my server?
A: It's a packaging convention, not a filesystem requirement. A minimal server image, a different distribution, or a non-default web server install may never create it — check your Nginx or Apache config's root directive for the real path instead of assuming it exists.
Q: How do I find which process is using a port?
A: On Linux, ss -ltnp lists listening TCP sockets with the owning process (may need sudo for full details). lsof -i :3000 works on both Linux and macOS when lsof is installed. On macOS specifically, lsof -i :3000 is the standard approach since ss isn't a native macOS tool.
Q: Where is the hosts file on macOS?
A: The same path as Linux: /etc/hosts. macOS is Unix-based and uses the identical file format and location, though it also layers its own DNS caching on top, which is why changes can sometimes take a moment to visibly apply.
Q: Can I edit /etc/hosts without administrator access?
A: No. It's owned by root and writable only by an administrator, by design — on Linux you need sudo, and on macOS you need an admin account and sudo as well. This prevents a regular user or unprivileged script from silently redirecting where your system thinks a domain lives.
Q: Why did my /etc/resolv.conf changes disappear?
A: On many modern Linux systems, resolv.conf is auto-generated by systemd-resolved, NetworkManager, or DHCP client hooks and gets rewritten on every network event or reboot. Manual edits are overwritten unless you configure the underlying resolver service (for example resolvectl) instead of the file directly.
Q: Does macOS use systemd?
A: No. systemd is Linux-specific. macOS uses launchd to manage services, daemons, and startup items, configured through property-list files rather than systemd unit files.
Q: Where are Laravel logs stored?
A: By default, storage/logs/laravel.log inside the Laravel project itself — not in /var/log — unless the application's logging configuration has been changed to point somewhere else (for example, to syslog or an external log service).
Conclusion
A hosting dashboard is genuinely useful, but it's an interface on top of the same filesystem covered here — the same /etc for configuration, the same /var/log for logs, the same ownership and permission rules underneath. Knowing where those things live, and which commands are safe to run versus which ones change system state, is what turns "the dashboard says something's wrong" into an actual diagnosis: a specific log line, a specific process, a specific permission mismatch.
If you need help deploying, securing, or troubleshooting a production website, the 92 Nodes engineering team can help — from a one-off server issue to ongoing support and maintenance. If you're building or hardening a Linux server, our chmod permission generator and cron expression builder are free tools built around exactly the topics in this guide.
