Linux CLI Best Practices
A condensed summary of the 25 most important best practices drawn from every page in this section.
- Prefer SIGTERM Over SIGKILL: Always try
kill <PID>(SIGTERM) first so the process can close files, release locks, and flush buffers; reach forkill -9only after a graceful stop fails. - Run Long-Lived Apps Under systemd: Wrap Node services in a unit file with
Restart=on-failure,RestartSec,WorkingDirectory, andEnvironment=NODE_ENV=productionso the app restarts cleanly on crash and survives reboots. - Persist Sessions With tmux or screen: Processes started in a plain SSH shell die when the connection drops, so run interactive work inside
tmux(or register it as a service) instead of relying onnohup … &. - Allow SSH Before ufw enable: Run
sudo ufw allow 22/tcpbeforesudo ufw enableor the next reconnect will be locked out and you will need console access to recover. - Proxy, Don't Bind, Privileged Ports: Non-root processes cannot bind below 1024, so run the Node app on a high port and put nginx or caddy in front on 80/443 rather than running Node as root.
- Diagnose With journalctl and dmesg: When a service is down, walk
systemctl status→journalctl -u <svc> --since …→dmesg | tailso you catch both application errors and kernel-level OOM kills. - Lock Down Secret Files: Use
chmod 600for.envand credential files (owner read/write only),755for executables and scripts, and644for regular content. - Prefer ripgrep Over grep:
rgis substantially faster on large codebases and auto-respects.gitignore, so it skipsnode_modules/and.next/without manual excludes. - Always Scope find and grep: Never search from
/or the project root unfiltered — pass a directory and-not -path "*/node_modules/*"(or--exclude-dir=node_modules) so searches finish in seconds instead of minutes. - Use -F for Literal Strings: When the pattern contains regex metacharacters like
.,(,[, or|, passgrep -F/rg -Fso the string is matched literally instead of interpreted as regex. - Know the macOS sed Quirk: Basic
sed -iin-place edits require an empty backup argument on macOS (sed -i '' 's/…/…/g' file), which is different from Linux; installgnu-sedif you want portable scripts. - Escalate to -E or -P for \d and \w: Basic
grepregex does not support\d,\w,+, or lookaheads, so switch togrep -E/grep -P(or just userg) when the pattern needs them. - Use -print0 | xargs -0 for Filenames:
xargssplits on whitespace and breaks on spaces in paths, so pairfind … -print0withxargs -0(or always quote"$file") when filenames are not guaranteed clean. - Show Context With -A, -B, -C: When investigating matches, add
-C 2(or-A/-B) so you see the surrounding lines instead of re-opening each hit in an editor. - Start Scripts With set -euo pipefail: Put
set -euo pipefailat the top of every shell script so the script exits on the first error, treats undefined variables as errors, and does not silently swallow failures in the middle of a pipe. - Put Aliases in ~/.zshrc on macOS: macOS defaults to zsh, which does not read
~/.bashrc; keep aliases and functions in~/.zshrc(orsourcebashrc from it) or they will appear to vanish in new terminals. - Prefer $() Over Backticks: Use
$(…)for command substitution because it nests cleanly ($(echo $(date))) and reads better; backticks require awkward escaping and mix poorly with quoting. - Redirect With 2>&1 After the Target: To capture both streams, write
cmd > out.log 2>&1(stderr follows stdout to the file) — reversing the order sends stderr to the terminal instead. - Parallelize With xargs -P: When processing many files with the same command, add
xargs -P 4 -I {}to run up to four in parallel and use-I {}as an explicit placeholder for clarity. - Use npm ci in CI:
npm ciinstalls the exact versions inpackage-lock.json, deletesnode_modulesfirst, and is faster thannpm install, which can quietly update the lockfile and produce non-reproducible builds. - Pin Node Everywhere: Commit a
.nvmrc(e.g.,22) and set"engines": { "node": ">=22" }inpackage.jsonso teammates and CI run the same Node version andnvm useauto-switches in the project. - Prefix Client Env Vars With NEXT_PUBLIC_: Env vars referenced in browser code must start with
NEXT_PUBLIC_in Next.js; without the prefix they resolve toundefinedat runtime, while unprefixed vars stay safely server-only. - Prefer npx Over Global Installs: Run one-off tools like
create-next-app,tsc, orprettiervianpxso you always get the project-local version and avoid global-vs-local conflicts that silently change behavior. - Free Stuck Ports Decisively: When
EADDRINUSE :3000hits, either runlsof -ti :3000 | xargs kill -9to reclaim the port or start the app on a different one withPORT=3001 npm run dev. - Lift the V8 Heap for Big Builds: Fix
FATAL ERROR: Allocation failedduring builds withNODE_OPTIONS="--max-old-space-size=8192" npm run build, but treat repeated OOMs as a signal to audit dependencies or memory leaks rather than raising the limit forever.