Right, so you’ve installed some package—let’s say nginx—and its maintainers have kindly provided a systemd service unit for you. It’s fine. It works. But it’s not yours. Maybe you want to add an environment variable, tweak a restart policy, or run it as a different user. Your first instinct might be to just copy /usr/lib/systemd/system/nginx.service to /etc/systemd/system/ and go to town.

Don’t. That’s how you create a maintenance nightmare. The next time the nginx package updates, your custom version is now a time bomb, completely oblivious to any security or functionality changes the vendor might have made. You’ll be left with a service file that’s both outdated and out of sync.

systemd, for all its… opinions, has an elegant solution for this: drop-in snippets. The idea is brilliant in its simplicity. Instead of replacing the entire unit file, you create a small fragment that overrides only the specific directives you want to change. The main unit file remains pristine and owned by the package manager, and your customizations live separately, applied on top at runtime. It’s the best of both worlds.

How systemctl edit Creates a Drop-In

The easiest way to do this without memorizing paths is to use systemctl edit. This command is your new best friend. Let’s say you want to modify the nginx service. You’d run:

sudo systemctl edit nginx.service

This command isn’t magic, though it feels like it. It does a few key things for you:

  1. It creates the directory /etc/systemd/system/nginx.service.d/ if it doesn’t exist.
  2. It creates a new file inside that directory, typically named override.conf.
  3. It drops you into your $EDITOR with that file open.

Now, here’s the critical part: when systemd loads the nginx.service unit, it will automatically read all .conf files in that .d directory, in alphabetical order, and apply the directives within them after the main unit file. Your override.conf is just one of those files.

Anatomy of an Override File

The file you edit isn’t a complete unit file. It’s a fragment. You only include the sections and directives you want to change. Let’s say the vendor’s nginx.service has:

[Service]
ExecStart=/usr/sbin/nginx -g 'daemon off;'

…and you want to add a custom configuration file via an environment variable. Your override.conf would look like this:

[Service]
Environment="NGINX_COOL_MODE=1"
# You can add new directives or override existing ones
Restart=always
# This completely replaces the original ExecStart!
ExecStart=
ExecStart=/usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/my-cool-config.conf

Notice the ExecStart= line? This is a systemd quirk you must remember. You can’t just append to an existing ExecStart; you have to clear it first by specifying it with no value, then redefine it. If you forget that empty assignment, systemd will helpfully assume you want to have two ExecStart commands and will fail spectacularly because, well, a service can only have one. It’s a common foot-gun, and I’ve shot myself in the foot with it more times than I’d like to admit.

The Raw, Manual Method

systemctl edit is fantastic, but it’s good to know what’s happening under the hood. The manual process is straightforward:

  1. Create the drop-in directory: sudo mkdir -p /etc/systemd/system/nginx.service.d
  2. Create your override file in that directory, e.g., sudo vim /etc/systemd/system/nginx.service.d/override.conf
  3. Tell systemd to reload its configuration to pick up the new drop-in: sudo systemctl daemon-reload

The daemon-reload is crucial. systemd reads unit files once at startup; this command forces it to reread them without a full reboot. Never, ever skip this step after creating or modifying a drop-in manually.

Viewing Your Masterpiece and Debugging

After you’ve made your changes, you naturally want to see the final, assembled product. systemctl cat nginx.service is your go-to command. It will concatenate and print the main unit file and all active drop-ins, in the order they are applied. It’s the absolute best way to debug why your override isn’t behaving as expected.

To see specifically what you’ve overridden, use systemctl show nginx.service. This dumps the entire, parsed, effective configuration as a list of key-value pairs. It’s verbose, but it leaves no room for doubt.

Best Practices and Pitfalls

  • Naming Your Drop-Ins: While override.conf is the default, you can use any name ending in .conf. I sometimes use descriptive names like 90-custom-port.conf to make their purpose obvious, especially if I have multiple drop-ins for a single service. The alphabetical order matters for application!
  • Don’t Fight the Vendor: Use drop-ins to augment, not gut, the vendor’s unit. If you find yourself needing to override every single line, maybe you should just write your own unit file from scratch and disable the vendor one.
  • The daemon-reload Ritual: I’ll say it again. sudo systemctl daemon-reload after any manual change. It’s the equivalent of turning it off and on again, but for systemd’s brain.
  • Test Before You Restart: Before you systemctl restart nginx.service, do a systemctl try-reload nginx.service if it supports it, or a systemctl restart --dry-run (if available), or at least a systemctl status nginx.service to ensure systemd is happy with your new configuration syntax. A failed restart might take your service offline.