Skip to main content
92 Nodes
Back to Blog

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-only
pwd          # 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 directory

pwd("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
PathAnalogyContainsDeveloper uses it forSafe to inspect?Linux/macOS
/etcCity hallMachine-wide configuration filesWeb server, SSH, DNS, cron configYes, read-onlyBoth (contents differ)
/home or /UsersResidents' homesPer-user files and dotfilesSSH keys, shell config, app dataYes (your own)Linux: /home · macOS: /Users
/varCity records officeLogs, caches, spool, site dataReading logs, finding /var/wwwYes, mostly read-onlyBoth (structure differs)
/var/logIncident reportsApplication and system logsDebugging errors and crashesYes — watch for secretsBoth (macOS also has unified log)
/tmpShared workbenchShort-lived temporary filesScratch space, upload stagingYes, don't rely on persistenceBoth
/usrTool warehouseInstalled programs, libraries, docswhich php, which nodeYes, read-onlyBoth
/bin, /sbinToolbeltEssential command binariesRarely browsed directlyYes, read-onlyBoth (often symlinked into /usr)
/optAnnex buildingOptional third-party applicationsManually installed software (e.g. some DB engines)Yes, read-onlyBoth
/procLive security-camera feedRunning process and kernel state (virtual)Diagnosing CPU/memory/process infoYes, read-onlyLinux only
/devUtility hookupsDevice files (virtual)Rarely touched directlyLook, don't writeBoth (contents differ)
/runWhiteboard notesRuntime state since boot (virtual)PID files, socketsYes, read-onlyLinux (modern)
/mnt, /mediaLoading dockManually or auto-mounted external storageAttached disks, USB drivesYesLinux (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.

PathPurposePlatform
/etc/hostsManual hostname-to-IP mappings, checked before DNS (see section 4)Both
/etc/hostnameThis machine's configured hostnameLinux
/etc/resolv.confDNS resolver configuration (see section 5)Both
/etc/passwdUser account records (no passwords stored here despite the name)Both
/etc/groupGroup definitions and membershipBoth
/etc/shadowHashed user passwords, root-readable onlyLinux
/etc/ssh/sshd_configSSH server (sshd) configurationLinux (server); macOS has its own path under /etc/ssh
/etc/nginxNginx configuration if installedBoth, if Nginx is installed
/etc/apache2Apache configuration (Debian/Ubuntu naming)Linux (RHEL family uses /etc/httpd)
/etc/phpPHP configuration (version-specific subfolders)Linux, distro-dependent layout
/etc/systemd/systemCustom and overridden systemd service unit filesLinux only
/etc/cron.d, /etc/crontabSystem-wide scheduled task definitionsLinux; macOS deprecated cron in favor of launchd
/etc/environmentSystem-wide environment variablesLinux

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.test

That 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.

LinuxmacOSSafe, read-only
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 time

Add 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-only
ping 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 cache

pingonly 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-only
dig 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 configuration

On 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.

PathTypically holds
/var/logApplication and system logs (section 7)
/var/wwwA common convention for website files on Debian/Ubuntu — not mandatory
/var/libPersistent application state, e.g. database data directories
/var/cacheRegenerable cached data (package manager caches, etc.)
/var/spoolQueued work waiting to be processed, e.g. cron or mail queues
/var/tmpTemporary 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.

LogTypical path / commandPlatform
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.logLinux, if Nginx installed
Apache/var/log/apache2/ (Debian/Ubuntu) or /var/log/httpd/ (RHEL family)Linux, distro-dependent
PHP-FPMPool-specific, often /var/log/php*-fpm.logLinux, version-dependent
MySQL / MariaDBOften /var/log/mysql/ or set by the server's own configLinux, config-dependent
Laravel applicationstorage/logs/laravel.log inside the projectBoth (it's app-level, not OS-level)
Docker containerdocker logs <container>, not a plain file you browse directlyBoth, wherever Docker runs
Everything under systemdjournalctl, optionally -u <service>Linux only
macOS system-widelog show / log streammacOS only

Useful commands:

LinuxSafe, read-only
tail -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 lines
macOSSafe, read-only
log 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:

  1. Confirm the response with a direct request rather than trusting the browser cache.
  2. Check the web server's error log for the specific upstream failure reason.
  3. Check whether the application process (PHP-FPM, Node.js, a queue worker) is actually running.
  4. Check the upstream service's own logs — PHP-FPM, Node.js, or whatever sits behind the proxy.
  5. Check that the port or Unix socket the web server expects to reach is the one the app is actually listening on.
  6. Check file and socket permissions between the web server user and the application user.
  7. Check disk space and memory — an OOM-killed process or a full disk can cause exactly this.
  8. 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:

FilePurpose
authorized_keysPublic 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.pubThe matching public key — safe to share, this is what goes in authorized_keys
known_hostsFingerprints 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."

LinuxmacOSSafe, read-only
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 searched

macOS 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-specific

These 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.

PathWhat it is
/proc/cpuinfoDetails about the CPU(s) the kernel sees
/proc/meminfoLive memory usage statistics
/proc/<PID>A directory of live information about one specific running process
/sysKernel and device state, organized around the kernel's internal device model
/dev/nullDiscards anything written to it; reading it returns nothing (end of file immediately)
/dev/random, /dev/urandomSources of random bytes used for cryptographic and general randomness
/runRuntime state created since the last boot — PID files, sockets — cleared on reboot
Safe, read-only
cat /proc/cpuinfo
cat /proc/meminfo
ls /proc

Look, 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.

LinuxSafe, read-only
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-only
ls -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 scripts

Reading 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:

ModeMeaningTypical use
644Owner: read/write · Group & others: read onlyA normal file, e.g. an uploaded asset
755Owner: read/write/execute · Group & others: read/executeA script or a directory that needs to be entered/listed
600Owner: read/write · Group & others: nothingA private key or credentials file
Configuration-changing
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-specific

Most 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.

Requires sudo for state changes
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 entries
macOS-specific

macOS 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-only
ps 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 check

ss 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 distribution

The 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:

Safe, read-only
sudo nginx -t          # validates syntax without applying anything
sudo systemctl reload nginx   # state-changing — applies a valid config with no dropped connections

Apache

Path varies by distribution

On 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:

Safe, read-only
php --ini    # shows which php.ini (and any scanned additional .ini files) is loaded
php -i       # full, detailed PHP configuration dump

Laravel

Within a Laravel project:

PathPurpose
.envEnvironment-specific config: database credentials, app key, service secrets
storage/logs/laravel.logThe application's own log file
storage/frameworkCompiled views, sessions, and framework-generated cache
bootstrap/cacheCached configuration and route files for performance
publicThe 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-only
docker ps                   # running containers
docker logs <container>     # a container's stdout/stderr logs
docker volume ls            # named volumes Docker manages

Editing 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

AreaLinuxmacOS
User home directory/home/<user>/Users/<user>
System configuration/etc, distro-dependent contentsMostly 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)
Servicessystemd (systemctl)launchd (LaunchAgents/LaunchDaemons)
External drives/mnt, /media/Volumes
Package-manager locationsDistro-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 shellbash or zsh, distro-dependent defaultzsh (default since macOS Catalina)
DNS inspectionresolvectl status, dig, nslookupscutil --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.

Safe commands to inspect:
  1. cat /etc/hosts — confirm the line actually exists and points to the right IP.
  2. getent hosts 92nodes.test (Linux) or dscacheutil -q host -a name 92nodes.test (macOS) — confirm what the resolver returns right now.
  3. 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.

Safe commands to inspect:
  1. tail -n 50 /var/log/nginx/error.log — the exact upstream failure reason.
  2. systemctl status php8.3-fpm (or the relevant service) — is it actually running?
  3. ss -ltnp or lsof -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.

Safe commands to inspect:
  1. df -h — free space per mounted filesystem.
  2. df -i — free inodes per filesystem (a separate limit from raw space).
  3. du -sh on 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.

Safe commands to inspect:
  1. ls -la storage bootstrap/cache — current owner, group, and mode.
  2. Check which user your PHP-FPM pool is configured to run as (in its pool config file).
  3. 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.

Safe commands to inspect:
  1. systemctl status your-service — is it enabled to start on boot, and did it actually start?
  2. journalctl -u your-service --since today — what error, if any, appeared at startup.
  3. 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-only
pwd                              # 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 entries

ss -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.

macOSSafe, read-only
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.

22. Quick-Reference Cheat Sheet

GoalLinuxmacOSWatch out for
Edit local hostname mappingsudo nano /etc/hostssudo nano /etc/hostsAdd a line, don't overwrite the file
Find web-server logs/var/log/nginx/ or /var/log/apache2/Depends on how the server was installedPath varies by distro and install method
Check service statussystemctl status <service>launchctl list, or the app's own statusmacOS has no systemctl
Find a process using a portss -ltnplsof -i :<port>ss is Linux-only
Check disk usagedf -h and df -idf -hdf -i (inodes) is the one people forget
Find PHP configurationphp --iniphp --iniCLI and FPM can load different files
Inspect DNS configurationresolvectl statusscutil --dnsDon't hand-edit auto-generated resolv.conf
Find SSH configuration/etc/ssh/sshd_config/etc/ssh/sshd_configChanges need a service reload to take effect
Inspect application logsjournalctl -u <service> or the app's own log filelog 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.

Get Your Free Quote Today

Let us build something great together!

Contact 92nodes

Written by

Muhammad Raza Bangi

Founder · Engineering

View profile