Why not edit the unit directly
Say nginx came from a package as /usr/lib/systemd/system/nginx.service,
and you need to raise LimitNOFILE. Historically there were three options:
- Edit the file straight in
/usr/lib/systemd/system/. Bad, a package update overwrites it. - Copy the whole thing into
/etc/systemd/system/nginx.serviceand edit it./etc/wins on priority, but: a) you carry the entire file, b) on a package update, new directives from upstream will not show up for you. - Drop-in, the one correct option: a small file holding the delta, while the base unit stays updatable.
How it works
For the unit nginx.service, systemd at load time merges every .conf
file from these directories:
/usr/lib/systemd/system/nginx.service.d/*.conf ← from packages (vendor)
/run/systemd/system/nginx.service.d/*.conf ← runtime
/etc/systemd/system/nginx.service.d/*.conf ← your local ones (highest)
Files inside one directory are merged in alphabetical order of the name.
Convention: 10-foo.conf, 20-bar.conf. The numeric prefix sets the order.
There are also "shared" drop-in directories keyed by unit type:
service.d/for all.serviceunitstimer.d/for all.timerunits
That is why systemctl status often shows:
Drop-In: /usr/lib/systemd/system/service.d
└─10-timeout-abort.conf, 50-keep-warm.conf
These are system-wide drop-ins that the distro applies to every service.
systemctl edit: the cleanest way to create one
Do not create a drop-in by hand. There is a command:
sudo systemctl edit nginx.service
What happens:
$EDITORopens with an empty buffer and an instruction comment.- After you save, systemd creates the file
/etc/systemd/system/nginx.service.d/override.conf. - It runs
daemon-reloadautomatically.
IMPORTANT: write only what you change there, with the required
[Section] header:
[Service]
LimitNOFILE=65536
Environment="DEBUG=1"
Do not restate Description=, ExecStart=, and the rest. They are pulled in
from the original.
Additive vs override-able directives
systemd directives fall into two classes:
List-style (additive). Wants=, Requires=, After=, ExecStartPre=,
Environment=, EnvironmentFile=. By default a drop-in adds to the
original list rather than replacing it.
Single-value (override). Type=, User=, Restart=, LimitNOFILE=.
The drop-in simply overwrites.
To clear and redefine a list, you need an empty reset line BEFORE the new value:
[Service]
ExecStart= # clear the entire original ExecStart
ExecStart=/usr/local/bin/nginx-wrapper # set the new one
Without the first empty line, nginx starts twice: the original ExecStart plus the new one. This is the most common mistake.
The same goes for Environment=, Wants=, and other list-style directives:
[Unit]
After= # clear
After=postgres.service redis.service
Check the final result
systemctl cat nginx.service
This shows the final unit with the drop-ins applied in the correct
order. Every line is tagged with its source through # /path/to/file above
each section:
# /usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network-online.target
[Service]
Type=forking
ExecStart=/usr/sbin/nginx
...
# /etc/systemd/system/nginx.service.d/override.conf
[Service]
LimitNOFILE=65536
Next, systemd-analyze verify to check the syntax:
systemd-analyze verify nginx.service
systemd-delta: what is overridden on my system
The command shows every unit on the system that has drop-ins or full overrides:
systemd-delta --type=extended # with drop-ins
systemd-delta --type=overridden # units with a full override
systemd-delta --type=masked # masked units (→ /dev/null)
Useful when you inherit a server from someone else and want to see what your predecessor admin set up.
systemctl revert: roll it back
Wipe all drop-ins and /etc/ overrides for a unit, leaving a clean vendor unit:
sudo systemctl revert nginx.service
It deletes /etc/systemd/system/nginx.service.d/ and /etc/systemd/system/nginx.service
itself if it was there as a full override.
Common cases
Raise limits:
[Service]
LimitNOFILE=1048576
TasksMax=infinity
Change the User:
[Service]
User=
User=newuser
(Single-value, so the empty line is not required, but writing it helps readability.)
Add an environment variable:
[Service]
Environment="GOMAXPROCS=4"
Environment="OTEL_ENDPOINT=http://collector:4318"
Send logs to a separate file instead of journald:
[Service]
StandardOutput=append:/var/log/myapp.log
StandardError=inherit
Do not forget daemon-reload if you edited without systemctl edit:
sudo systemctl daemon-reload
sudo systemctl restart nginx