Drop-in files let you tweak systemd unit behavior without touching the original unit files, which is crucial because package updates will overwrite your changes.
Let’s see this in action. Imagine you have a service, my-app.service, and you want to change its working directory.
# /etc/systemd/system/my-app.service.d/override.conf
[Service]
WorkingDirectory=/opt/my-app/new_location
Now, if you run systemctl cat my-app.service, you’ll see both the original unit file content and your override:
# /etc/systemd/system/my-app.service
[Unit]
Description=My Application
[Service]
ExecStart=/usr/bin/my-app
User=appuser
[Install]
WantedBy=multi-user.target
# /etc/systemd/system/my-app.service.d/override.conf
[Service]
WorkingDirectory=/opt/my-app/new_location
The effective configuration for my-app.service now includes WorkingDirectory=/opt/my-app/new_location.
The Problem: Customizing Systemd Services
Systemd manages almost everything on modern Linux systems, from boot-up to daemons. It uses unit files (like .service, .socket, .timer) to define how these components should run. Often, you need to adjust these units. Maybe you want to change the ExecStart command, add environment variables, modify resource limits, or change the user a service runs as. The obvious place to do this is in the unit file itself, usually found in /usr/lib/systemd/system/ or /etc/systemd/system/.
However, editing files in /usr/lib/systemd/system/ is a bad idea. These are managed by packages. When the package updates, your changes vanish. Even editing files in /etc/systemd/system/ can be problematic if you’re not careful, as it can lead to complex, hard-to-track configurations.
The Solution: Drop-in Files
Systemd provides a clean, intended mechanism for customization: drop-in files. These are small configuration files placed in a specific subdirectory (.d/) next to the original unit file. Systemd reads the original unit file and all drop-in files within its corresponding .d/ directory, merging their configurations.
The hierarchy is important:
- Original Unit File: e.g.,
/usr/lib/systemd/system/nginx.service - Drop-in Directory:
/etc/systemd/system/nginx.service.d/ - Drop-in File: e.g.,
/etc/systemd/system/nginx.service.d/custom-settings.conf
When systemd loads nginx.service, it first reads /usr/lib/systemd/system/nginx.service, then it looks for a directory named nginx.service.d in the same location and any higher-priority locations (like /etc/systemd/system/ before /usr/lib/systemd/system/). It then reads all .conf files within that directory, merging the settings.
This allows you to override or add specific directives without touching the original file. For example, to add an environment variable to sshd.service:
-
Create the directory:
sudo mkdir /etc/systemd/system/sshd.service.d/ -
Create the override file:
sudo nano /etc/systemd/system/sshd.service.d/env-vars.conf -
Add your configuration:
[Service] Environment="MY_CUSTOM_VAR=some_value" Environment="ANOTHER_VAR=another_value" -
Reload systemd:
sudo systemctl daemon-reload -
Restart the service:
sudo systemctl restart sshd.service
Now, sshd will have MY_CUSTOM_VAR and ANOTHER_VAR set.
The Mental Model: Layered Configuration
Think of systemd unit loading as a stack of transparencies. The base layer is the original unit file. Each drop-in file is another transparency placed on top. When systemd renders the final configuration, it’s like looking through all the transparencies at once. If a directive appears on multiple transparencies, the one on top "wins."
The order in which drop-in files are read matters. Systemd sorts them alphabetically by filename. So, if you have a.conf and b.conf in the same .d/ directory, b.conf will be read after a.conf, and its settings will take precedence if there’s a conflict. This is rarely needed for simple overrides but can be useful for complex scenarios.
The most common use case is modifying the [Service] section, but you can override directives in [Unit] and [Install] sections too. For instance, to change the After= directive for a service:
# /etc/systemd/system/my-app.service.d/after.conf
[Unit]
After=network-online.target another-service.service
This will add network-online.target and another-service.service to the After= list for my-app.service, in addition to whatever was originally specified. Note that if After= was already present in the original unit, the drop-in adds to it rather than replacing it entirely. This additive behavior is key for many directives.
A critical detail is that systemctl daemon-reload is always required after creating or modifying drop-in files for systemd to recognize the changes.
The power of drop-ins lies in their separation of concerns: the original unit file defines the default behavior provided by the package, and drop-in files define your specific customizations. This makes upgrades painless and troubleshooting much simpler. You can always check the effective configuration by running systemctl cat <unit_name>.service and inspecting the output, which will include the merged content from the original unit and any drop-ins.
The next hurdle you’ll likely encounter is understanding how to manage dependencies and ordering between services using directives like Wants=, Requires=, Before=, and After= in conjunction with drop-ins.