Tools: Stop Editing `/etc/sudoers` Directly: Practical `sudoers.d` + `visudo` on Linux (2026)

Tools: Stop Editing `/etc/sudoers` Directly: Practical `sudoers.d` + `visudo` on Linux (2026)

Why sudoers.d is the better default

First, confirm your main file includes the directory

Rule 1: validate with visudo, not a text editor alone

Example 1: delegate one service restart and log access

Example 2: allow package metadata refresh, but not full package installs

File naming and permission gotchas that bite people

1) Do not put dots in drop-in filenames

2) Use root ownership and mode 0440

3) Validate after writing, not just before

A safer automation pattern

What not to do

A quick rollback path

Final thought When a team needs one extra admin permission on a Linux box, the fastest path is often the messiest one: open /etc/sudoers, add a line, hope nothing breaks. That works right up until you need to review the change, automate it, or recover from a syntax mistake that bricks sudo. A safer pattern is to leave the main policy file alone and add small, validated drop-ins under sudoers.d. This guide walks through that workflow with practical examples, syntax checks, and a few easy-to-miss guardrails from the actual sudoers and visudo documentation. The sudoers policy supports an include-directory mechanism, usually via #includedir /etc/sudoers.d. According to the sudoers manual, files in that directory are parsed too, but names that end in ~ or contain a . are skipped. That makes sudoers.d useful because you can: The last point matters a lot. A 3-line drop-in is much easier to audit than a hand-edited global policy file full of historical exceptions. On many Debian and Ubuntu systems, the main file already includes it. You are typically looking for something like: If you do not see an include directory, stop and review your distro defaults before inventing your own layout. The visudo manual is very clear about why the tool exists: it locks the file against simultaneous edits and checks syntax before saving. Even better, it supports check-only mode and an alternate file path, which is exactly what you want for a drop-in workflow. The two flags to remember are: A safe pattern looks like this: On a valid file, you should see output like: That is the moment to install it, not before. A common real-world need is letting a deployment group restart one service and inspect its recent logs without giving them unrestricted root. Create a drop-in like this: To verify the effective access from an allowed account: If you need to test as a specific user from an admin shell: Sometimes a user only needs to refresh package metadata or inspect upgrade candidates. A narrower drop-in might look like this: This is intentionally different from granting full package installation rights. If you are tempted to add apt install, apt remove, wildcard-heavy command patterns, or shell escapes to the same rule, pause and re-scope it. Small delegated actions are the whole point. A few details from the manual matter more than they look. Per the sudoers manual, files in an included directory are skipped if the name ends in ~ or contains a .. That means editor backup files and “nice-looking” .conf names can silently fail to load. The sudoers documentation states the default file mode is 0440, readable by owner and group and writable by none. The visudo manual also documents ownership and permission checks in validation mode. A reliable install pattern is: If your automation writes the file and then changes ownership or mode incorrectly, the syntax may still be fine while the policy remains unusable. So validate the installed path too: According to the visudo manual, check mode against the default sudoers path also checks included files plus ownership and permissions. If you manage hosts with Ansible, shell scripts, or CI-built images, use a staged file plus validation before the final move. That gives you three useful properties: I would avoid these patterns unless you have a very specific reason: If a rule looks “temporarily broad”, it usually becomes permanently broad. If a new drop-in causes confusion, rollback is simple because the change is isolated. That is much less stressful than untangling a large hand-edited main file. sudo policy is one of those things that feels trivial until the day it is not. Using sudoers.d plus visudo turns it into something modular, reviewable, and a lot less fragile. For Linux admin work, that is usually the difference between “quick fix” and “clean operational habit.” Templates let you quickly answer FAQs or store snippets for re-use. Hide child comments as well For further actions, you may consider blocking this person and/or reporting abuse

Command

Copy

$ -weight: 600;">sudo grep -nE '^[#@]includedir' /etc/sudoers -weight: 600;">sudo grep -nE '^[#@]includedir' /etc/sudoers -weight: 600;">sudo grep -nE '^[#@]includedir' /etc/sudoers #includedir /etc/sudoers.d #includedir /etc/sudoers.d #includedir /etc/sudoers.d cat >/tmp/90-app-maint <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo /usr/sbin/visudo -cf /tmp/90-app-maint cat >/tmp/90-app-maint <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo /usr/sbin/visudo -cf /tmp/90-app-maint cat >/tmp/90-app-maint <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo /usr/sbin/visudo -cf /tmp/90-app-maint /tmp/90-app-maint: parsed OK /tmp/90-app-maint: parsed OK /tmp/90-app-maint: parsed OK -weight: 600;">sudo -weight: 500;">install -d -m 0755 /etc/sudoers.d -weight: 600;">sudo tee /etc/sudoers.d/90-app-maint >/dev/null <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo chown root:root /etc/sudoers.d/90-app-maint -weight: 600;">sudo chmod 0440 /etc/sudoers.d/90-app-maint -weight: 600;">sudo /usr/sbin/visudo -cf /etc/sudoers.d/90-app-maint -weight: 600;">sudo -weight: 500;">install -d -m 0755 /etc/sudoers.d -weight: 600;">sudo tee /etc/sudoers.d/90-app-maint >/dev/null <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo chown root:root /etc/sudoers.d/90-app-maint -weight: 600;">sudo chmod 0440 /etc/sudoers.d/90-app-maint -weight: 600;">sudo /usr/sbin/visudo -cf /etc/sudoers.d/90-app-maint -weight: 600;">sudo -weight: 500;">install -d -m 0755 /etc/sudoers.d -weight: 600;">sudo tee /etc/sudoers.d/90-app-maint >/dev/null <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo chown root:root /etc/sudoers.d/90-app-maint -weight: 600;">sudo chmod 0440 /etc/sudoers.d/90-app-maint -weight: 600;">sudo /usr/sbin/visudo -cf /etc/sudoers.d/90-app-maint -weight: 600;">sudo -l -U someuser -weight: 600;">sudo -l -U someuser -weight: 600;">sudo -l -U someuser -weight: 600;">sudo tee /etc/sudoers.d/91--weight: 500;">apt-audit >/dev/null <<'EOF' Cmnd_Alias APT_AUDIT = /usr/bin/-weight: 500;">apt -weight: 500;">update, /usr/bin/-weight: 500;">apt list --upgradable %ops ALL=(root) APT_AUDIT EOF -weight: 600;">sudo chown root:root /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo chmod 0440 /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo /usr/sbin/visudo -cf /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo tee /etc/sudoers.d/91--weight: 500;">apt-audit >/dev/null <<'EOF' Cmnd_Alias APT_AUDIT = /usr/bin/-weight: 500;">apt -weight: 500;">update, /usr/bin/-weight: 500;">apt list --upgradable %ops ALL=(root) APT_AUDIT EOF -weight: 600;">sudo chown root:root /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo chmod 0440 /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo /usr/sbin/visudo -cf /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo tee /etc/sudoers.d/91--weight: 500;">apt-audit >/dev/null <<'EOF' Cmnd_Alias APT_AUDIT = /usr/bin/-weight: 500;">apt -weight: 500;">update, /usr/bin/-weight: 500;">apt list --upgradable %ops ALL=(root) APT_AUDIT EOF -weight: 600;">sudo chown root:root /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo chmod 0440 /etc/sudoers.d/91--weight: 500;">apt-audit -weight: 600;">sudo /usr/sbin/visudo -cf /etc/sudoers.d/91--weight: 500;">apt-audit /etc/sudoers.d/90-app-maint /etc/sudoers.d/90-app-maint /etc/sudoers.d/90-app-maint /etc/sudoers.d/90-app-maint.conf /etc/sudoers.d/90-app-maint~ /etc/sudoers.d/90-app-maint.conf /etc/sudoers.d/90-app-maint~ /etc/sudoers.d/90-app-maint.conf /etc/sudoers.d/90-app-maint~ -weight: 600;">sudo chown root:root /etc/sudoers.d/90-app-maint -weight: 600;">sudo chmod 0440 /etc/sudoers.d/90-app-maint -weight: 600;">sudo chown root:root /etc/sudoers.d/90-app-maint -weight: 600;">sudo chmod 0440 /etc/sudoers.d/90-app-maint -weight: 600;">sudo chown root:root /etc/sudoers.d/90-app-maint -weight: 600;">sudo chmod 0440 /etc/sudoers.d/90-app-maint -weight: 600;">sudo /usr/sbin/visudo -c -weight: 600;">sudo /usr/sbin/visudo -c -weight: 600;">sudo /usr/sbin/visudo -c tmp=$(mktemp) cat >"$tmp" <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo /usr/sbin/visudo -cf "$tmp" -weight: 600;">sudo -weight: 500;">install -o root -g root -m 0440 "$tmp" /etc/sudoers.d/90-app-maint -weight: 600;">sudo /usr/sbin/visudo -c rm -f "$tmp" tmp=$(mktemp) cat >"$tmp" <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo /usr/sbin/visudo -cf "$tmp" -weight: 600;">sudo -weight: 500;">install -o root -g root -m 0440 "$tmp" /etc/sudoers.d/90-app-maint -weight: 600;">sudo /usr/sbin/visudo -c rm -f "$tmp" tmp=$(mktemp) cat >"$tmp" <<'EOF' Cmnd_Alias APP_MAINT = /usr/bin/-weight: 500;">systemctl -weight: 500;">restart myapp.-weight: 500;">service, /usr/bin/journalctl -u myapp.-weight: 500;">service -n 200 %deploy ALL=(root) APP_MAINT EOF -weight: 600;">sudo /usr/sbin/visudo -cf "$tmp" -weight: 600;">sudo -weight: 500;">install -o root -g root -m 0440 "$tmp" /etc/sudoers.d/90-app-maint -weight: 600;">sudo /usr/sbin/visudo -c rm -f "$tmp" -weight: 600;">sudo mv /etc/sudoers.d/90-app-maint /root/90-app-maint.disabled -weight: 600;">sudo /usr/sbin/visudo -c -weight: 600;">sudo mv /etc/sudoers.d/90-app-maint /root/90-app-maint.disabled -weight: 600;">sudo /usr/sbin/visudo -c -weight: 600;">sudo mv /etc/sudoers.d/90-app-maint /root/90-app-maint.disabled -weight: 600;">sudo /usr/sbin/visudo -c - keep the base /etc/sudoers file package-friendly - separate app- or team-specific privileges into small files - validate one candidate rule before installing it - manage delegated access with configuration management more cleanly - -c or --check for validation - -f or --file for an alternate file path - defines a command alias named APP_MAINT - allows members of the deploy group to run those commands as root - keeps the permission scope narrow and explicit - syntax is checked before -weight: 500;">install - final permissions are enforced during -weight: 500;">install - the complete active policy is checked afterward - editing /etc/sudoers directly for every small exception - granting ALL=(ALL:ALL) ALL to convenience groups - using wildcards loosely around commands with shell escapes or user-controlled arguments - storing drop-ins with .conf, .bak, or editor backup suffixes - skipping a full visudo -c after policy changes - sudoers manual: https://www.-weight: 600;">sudo.ws/docs/man/sudoers.man/ - visudo manual: https://www.-weight: 600;">sudo.ws/docs/man/visudo.man/ - Debian -weight: 600;">sudo package metadata: https://packages.debian.org/search?keywords=-weight: 600;">sudo