skip to content

You build a tool from source on a Linux server and run `make install`. Under the FHS, why does it belong in /usr/local rather than /usr, and when is /opt the right home instead?

level: middleimportance: must knowfreq 55%

answer

  1. who owns the path on upgrade
  2. the package manager stays out of one tree
  3. autotools already defaults to it
  4. vendor bundle wants its own subtree
  5. content you serve is a third place

basics

~20 s

/usr is owned by the distribution's package manager, so an upgrade can overwrite or conflict with anything you drop there. /usr/local is reserved for software the administrator installs and packages never touch. /opt holds self-contained third-party bundles in their own subtree.

solid answer

~50 s

The FHS reserves `/usr` for files that belong to the distribution: every path there is owned by a package, and an upgrade may replace it. `/usr/local` is the opposite guarantee — no distribution package installs into it, so software you build stays yours across upgrades. It mirrors `/usr`'s layout (`bin`, `sbin`, `lib`, `share`, `include`, plus its own `etc`), which is why the GNU autotools default prefix is `/usr/local` and `make install` lands there by default. `/opt` is for a different shape of software: a self-contained third-party bundle that keeps its own binaries, libraries and data together under `/opt/<vendor>` or `/opt/<package>` rather than scattering files across the standard directories — typically commercial or vendor-shipped products. Rule of thumb: built from source and it follows Unix layout, `/usr/local`; a vendor tarball that wants to own a directory, `/opt`.

code

bash · 5 lines
bash
./configure --prefix=/usr/local
make
sudo make install
# where did the binary land, and does it shadow the packaged one?
type -a curl

go deeper

for a junior

Know that software you compile yourself goes to /usr/local, not /usr, and that this is why make install puts things in /usr/local/bin by default. Do not hand-copy binaries into /usr/bin.

for a middle

Explain the ownership guarantee in both directions: packages own /usr and may overwrite it, and distribution policy keeps packages out of /usr/local. Describe the mirrored layout and the PATH-shadowing consequence.

for a senior

Show the operational follow-through: the linker cache for /usr/local/lib, diagnosing a shadowed binary, and knowing when a manual install should become a real package or an image so a fleet stays describable.

for a principal

Own the policy: what a team is allowed to install by hand versus what must be packaged, how /opt vendor trees are integrated and audited, and what unmanaged /usr/local content costs you when hosts become immutable images.

## Two guarantees, not two preferences The reason this question is asked is that both answers "work" — a binary in `/usr/bin` runs fine. What differs is the *ownership guarantee*. On a distribution-managed system, every file under `/usr` is claimed by a package. The package database knows which package owns `/usr/bin/curl`, and upgrading that package replaces the file. If you drop your own build of `curl` into `/usr/bin`, one of two things happens: the next upgrade silently overwrites your build, or the package tool refuses to unpack because the path already exists and is not owned by it. Either way you have created a class of failure that appears months later, during an unrelated upgrade, with no obvious connection to what you did. `/usr/local` exists to make that impossible. The FHS states it is for use by the system administrator when installing software locally, and that it must be safe from being overwritten when system software is updated. Distribution policies enforce the other side of the bargain — Debian Policy, for example, forbids packages from installing files into `/usr/local`. So the guarantee is mutual: packages stay out, and your files survive. ## /usr/local mirrors /usr `/usr/local` is not a single flat directory. It carries the same structure one level down: ``` /usr/local/bin /usr/local/sbin /usr/local/lib /usr/local/include /usr/local/share /usr/local/etc /usr/local/src ``` That is why the GNU build system's default `--prefix` is `/usr/local`: a plain `./configure && make && sudo make install` places the binary in `/usr/local/bin`, the man page in `/usr/local/share/man`, headers in `/usr/local/include`. Everything lands in the right shape without any per-project knowledge. Two practical consequences follow. First, `/usr/local/bin` normally precedes `/usr/bin` in the default `PATH`, so a locally installed binary **shadows** the distribution's binary of the same name. That is usually what you want, and occasionally the cause of a mystifying version mismatch — `type -a <name>` shows every match in `PATH` order and settles the argument. Second, shared libraries installed under `/usr/local/lib` are found only if the dynamic linker searches there; distributions handle this with a configuration fragment under `/etc/ld.so.conf.d/` and a run of `ldconfig`, and a missing library at runtime after a source install is very often this and nothing more. ## /opt: self-contained bundles `/opt` answers a different problem. Some software does not want to be spread across `bin`, `lib` and `share` at all; it ships as a tree with its own internal layout, its own bundled runtime, and an expectation that it lives at a fixed path. The FHS gives it `/opt/<package>` or `/opt/<provider>` — a namespace where a vendor may impose its own structure. Typical inhabitants are commercial products, browser and IDE builds, and language toolchains installed outside the distribution. The FHS also carves out `/etc/opt/<package>` for such a package's host-specific configuration and `/var/opt/<package>` for its variable data, keeping the static/local/variable rule intact even for self-contained software. In practice many `/opt` vendors ignore that and keep everything under their own directory; what matters for an interview is that you know the intent. Because nothing in `/opt` is in `PATH` by default, the usual integration is a symlink from `/usr/local/bin` into the bundle, or a small profile fragment that extends `PATH`. ## The neighbouring directory people forget: /srv `/srv` is for data *served by this system* — a web root, an FTP or rsync export, a git repository collection. It is neither the program nor its internal state; it is content this host publishes. Putting a web root in `/srv/www` rather than `/var/www` or `/opt/site` is a defensible choice precisely because `/srv` announces "this is the payload, not the software". ## How to decide - Built from source, follows Unix layout, one tool among many → `/usr/local`. - Vendor bundle with its own tree and fixed install path → `/opt/<vendor>`. - Content this host serves to others → `/srv`. - Anything the distribution's package manager installs → `/usr`, and you never write there by hand. And the mature answer to the whole question: on a fleet, prefer to *package* the software instead. Building a `.deb` or `.rpm` (or shipping it in an image) turns "a directory somebody made on one host" into something with a version, a dependency list and a removal path. `/usr/local` is the right place for a manual install; it is not a substitute for a supply chain.

  • You install a library under /usr/local/lib and the program fails at startup with a missing shared object. What is going on?
    The dynamic linker searches a cached list of directories, and `/usr/local/lib` is included only if a configuration fragment under `/etc/ld.so.conf.d/` names it and `ldconfig` has been run to rebuild the cache. Until then the library exists but is invisible at load time. Running `ldconfig` after the install, or setting the correct `RPATH` at build time, resolves it.
  • Why does /usr/local/bin usually come before /usr/bin in PATH, and when does that bite?
    So an administrator's deliberately installed version wins over the distribution's — that is the point of a local install. It bites when someone forgets the local copy exists: a script runs an old self-built binary while everyone assumes the packaged one, and version output disagrees with the package database. `type -a <name>` lists every match in PATH order and ends the confusion.
  • When would you package the software instead of installing it into /usr/local?
    As soon as more than one host needs it. A package gives the file set a version, a dependency list, an owner in the package database and a clean removal path, and it makes the state of a machine describable. Manual `/usr/local` installs are invisible to every audit and drift silently across a fleet.

saying these in an interview costs you the question

  • Installs self-built binaries directly into /usr/bin
  • Thinks /usr/local is just an alias for /usr
  • Uses /opt for every locally built tool
  • Assumes /opt/<vendor>/bin is automatically in PATH
  • Claims package managers routinely install into /usr/local

context