skip to content

On a systemd host you edited /usr/lib/systemd/system/nginx.service directly, and a package upgrade silently reverted your change. Which directories does systemd load unit files from, in what order of precedence, and how should that change have been made so it survives upgrades?

level: middleimportance: must knowfreq 68%

answer

  1. more than one unit directory
  2. the package owns one of them
  3. admin configuration outranks the vendor
  4. a .d directory beside the unit name
  5. systemctl edit, then systemctl cat

basics

~20 s

systemd loads units from /etc/systemd/system (admin, wins), /run/systemd/system (runtime), and /usr/lib/systemd/system (package-owned, loses). Package upgrades rewrite the last one, so put changes in a drop-in created with systemctl edit instead of editing the vendor file.

solid answer

~40 s

The system manager searches several unit directories in a fixed precedence order: `/etc/systemd/system` (administrator), then `/run/systemd/system` (runtime, used by generators and transient units), then `/usr/lib/systemd/system` where packages install their units. For a given unit *name*, the first file found wins outright — the lower-priority copies are shadowed, not merged. Editing the package copy means the next `apt`/`dnf` upgrade replaces the file and your change disappears, which is exactly what happened. The right move is a drop-in: `systemctl edit nginx.service` creates `/etc/systemd/system/nginx.service.d/override.conf`, which is merged *on top of* the vendor unit, so you keep upstream fixes and carry only your delta. `systemctl edit` reloads the manager for you; a hand-written drop-in needs `systemctl daemon-reload`. Verify the result with `systemctl cat nginx.service`, which prints every file that contributed and its path.

code

bash · 4 lines
bash
sudo install -d /etc/systemd/system/nginx.service.d
printf '[Service]\nLimitNOFILE=65535\n' | sudo tee /etc/systemd/system/nginx.service.d/override.conf
sudo systemctl daemon-reload
systemctl cat nginx.service

go deeper

for a junior

Know that unit files live in more than one directory and that you never edit the one the package owns. Be able to say that systemctl edit is how you change a vendor unit.

for a middle

Explain the precedence order between /etc, /run and /usr/lib, that the main unit file is winner-takes-all while drop-ins merge on top, and that a hand edit needs daemon-reload before it counts.

for a senior

Show how you audit an unfamiliar host: systemctl cat for one unit, systemctl show for merged values, systemd-delta for everything that has been overridden — and explain why an undocumented full copy in /etc is a maintenance liability.

for a principal

Own the fleet policy: where configuration management is allowed to write, a numbering convention for drop-in file names so two tools do not fight, and the rule that forks of vendor units are reviewed at upgrade time rather than discovered at incident time.

## Why this comes up Almost every real systemd change is a change to somebody else's unit file: raise a file-descriptor limit on the vendor's nginx unit, add an environment variable to a database unit, tighten a sandbox setting. Where you write that change decides whether it survives the next package upgrade and whether the next engineer can find it. Interviewers ask this because the wrong answer produces a change that quietly vanishes months later. ## The unit search path The system manager (PID 1) reads unit files from a list of directories with a defined priority. From highest to lowest, the ones that matter day to day are: - `/etc/systemd/system` — administrator-owned. Nothing but you writes here. - `/run/systemd/system` — runtime, on tmpfs. Generators and transient units land here; it disappears on reboot. - `/usr/lib/systemd/system` — package-owned. Your distribution's `.rpm`/`.deb` files install units here. On distributions that have merged `/usr`, `/lib/systemd/system` is a compatibility symlink to the same place. There is also `/usr/local/lib/systemd/system` for locally built software, and generator output directories under `/run/systemd/`. You can print the effective list on a host with `systemd-analyze unit-paths`. The rule for the *main* unit file is winner-takes-all: systemd looks for `nginx.service` in each directory in order and uses the first one it finds. A full copy in `/etc` does not merge with the vendor copy — it replaces it entirely. That is occasionally what you want, but it means you have permanently forked the unit and will not receive upstream changes to it. ## Drop-ins: the mechanism you should be using Beside a unit file, systemd looks for a directory named after the unit with a `.d` suffix, and reads every `*.conf` inside it: ``` /etc/systemd/system/nginx.service.d/override.conf /usr/lib/systemd/system/nginx.service.d/50-vendor.conf ``` Drop-ins are *additive*: the main unit file is parsed first, then each drop-in is applied on top, in lexicographic order of the file names across all directories, with `/etc` applied after `/usr/lib` so the administrator wins on any setting both touch. A drop-in that sets only `[Service] LimitNOFILE=65535` leaves everything else in the vendor unit exactly as it was. Because the file names are sorted, the convention of prefixing them (`10-`, `50-`, `99-`) lets configuration-management tools slot changes in predictably. Drop-ins also exist for whole unit types — a `.conf` under `/etc/systemd/system/service.d/` applies to every service unit on the host — which is handy for fleet-wide defaults and surprising when you have forgotten it exists. ## The commands - `systemctl edit nginx.service` opens an editor on `/etc/systemd/system/nginx.service.d/override.conf`, creating it if needed, and runs a daemon-reload when you save. - `systemctl edit --full nginx.service` instead copies the whole vendor unit into `/etc/systemd/system/` for editing — the deliberate fork. - `systemctl cat nginx.service` prints the main unit file and every drop-in, each preceded by a comment naming its absolute path. This is the fastest way to answer "what is this unit actually configured to do". - `systemctl show nginx.service` prints the fully merged properties as systemd resolved them, which is the ground truth when a drop-in and a main file disagree. - `systemctl revert nginx.service` removes administrator drop-ins and copies, restoring the vendor unit. - `systemd-delta` walks the whole host and reports which units are overridden, extended by drop-ins, or masked. ## daemon-reload systemd parses unit files once and keeps them in memory. Any hand edit — a new file, a changed drop-in, a deleted override — requires `systemctl daemon-reload` before it has any effect; until then `systemctl status` may warn that the unit file changed on disk. Reloading the manager does **not** restart anything: the new configuration applies to the running unit only where systemd can apply it live, and a process-level setting like `ExecStart=` or `User=` takes effect at the next restart of that unit. ## What actually went wrong in the scenario Unit files are not treated as configuration files by the packaging tools, so there is no conffile prompt and no `.rpmsave` backup: the upgrade simply wrote a new `/usr/lib/systemd/system/nginx.service` over the old one, and the change was gone with no warning. The drop-in in `/etc` would have been untouched, and `systemctl cat` would still show it applied on top of the new vendor unit.

  • Once the drop-in is in place, how do you prove which files contributed to the running configuration?
    `systemctl cat nginx.service` prints the main unit file and every drop-in with its absolute path as a comment header. `systemctl show nginx.service` prints the merged properties as systemd resolved them, which settles disagreements between a drop-in and the main file. Across a whole host, `systemd-delta` lists every unit that is overridden or extended.
  • When is copying the whole vendor unit into /etc/systemd/system the right call rather than a drop-in?
    When you need to remove or restructure most of the vendor file rather than add to it — for example dropping a hard dependency the vendor set. `systemctl edit --full` does it deliberately. The cost is that you no longer track upstream changes to that unit, so the fork must be reviewed on major upgrades. Prefer a drop-in whenever the delta is small.
  • What exactly does systemctl daemon-reload do, and what does it not do?
    It makes the manager re-read unit files from disk and rebuild its dependency graph. It does not restart running units and does not re-apply process-level settings to an already-running process — `ExecStart=` or `User=` changes take effect at the unit's next restart. `systemctl edit` and `systemctl revert` run the reload for you; hand edits do not.

A drop-in is a sticky note on someone else's document: the document keeps getting revised upstream, and your note stays on top of whatever the current revision says.

saying these in an interview costs you the question

  • Editing the vendor unit under /usr/lib is fine, nothing overwrites it
  • A drop-in replaces the whole unit file
  • daemon-reload restarts the affected services
  • There is only one directory where unit files live
  • Whichever unit file was modified most recently wins

context