Skip to main content

Documentation Index

Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt

Use this file to discover all available pages before exploring further.

Linux Process Management

Learn to monitor, control, and manage processes in Linux. A process is simply a running program — every command you type, every service running on your server, every background task is a process. Understanding processes is like understanding traffic flow in a city: you need to know what is running, what is stuck, and how to clear a jam.

Viewing Processes

# List all processes on the system
# a = show processes from all users
# u = display user/owner column
# x = include processes not attached to a terminal (daemons, services)
ps aux

# The columns that matter most:
# USER  PID  %CPU  %MEM  VSZ  RSS  STAT  START  TIME  COMMAND
# - PID: unique process ID (you need this to kill or inspect a process)
# - %CPU / %MEM: resource consumption at this instant
# - STAT: process state (S=sleeping, R=running, Z=zombie, D=uninterruptible sleep)
# - TIME: cumulative CPU time used (not wall clock time)

# Real-time process monitor (refreshes every few seconds)
top
# htop is the version you actually want -- color-coded, scrollable, mouse-friendly
htop  # Install with: sudo apt install htop

# Show the process tree -- see parent-child relationships
# This is invaluable for understanding which process spawned which
pstree
pstree -p  # Include PIDs in the tree

# Find a process by name (returns just the PID, great for scripting)
pgrep nginx
pgrep -a nginx  # Also show the full command line

# The classic approach -- but watch out for the grep process appearing in results
ps aux | grep nginx
ps aux | grep [n]ginx  # Bracket trick to exclude grep itself from results
When to use what: Use htop for interactive exploration (what is eating my CPU right now?). Use ps aux for scripting and one-off checks. Use pstree when you need to understand process relationships, like which worker was spawned by which master process.

Signals and Process Control

Killing a process is not always about force — Linux uses signals to communicate with processes. Think of signals as tapping someone on the shoulder (polite) versus pulling the fire alarm (forceful).
# Send the default SIGTERM (signal 15) -- asks the process to shut down gracefully
# The process gets a chance to close files, flush buffers, and clean up
kill PID

# SIGKILL (signal 9) -- forces immediate termination, no cleanup
# Use this only as a last resort when SIGTERM is ignored
kill -9 PID

# SIGHUP (signal 1) -- traditionally means "reload configuration"
# Many daemons (nginx, Apache) re-read their config files on SIGHUP
kill -HUP PID

# Kill by name instead of PID (convenient but be careful with naming collisions)
pkill nginx        # Sends SIGTERM to all processes named "nginx"
killall nginx      # Same idea, slightly different matching behavior

# Kill all processes owned by a user (nuclear option for runaway sessions)
pkill -u username

Common Signals Reference

SignalNumberDefault ActionCommon Use
SIGHUP1TerminateReload config files
SIGINT2TerminateCtrl+C in terminal
SIGQUIT3Core dumpCtrl+\ for debugging
SIGKILL9Terminate (cannot be caught)Force kill unresponsive process
SIGTERM15TerminateGraceful shutdown (default)
SIGSTOP19Stop (cannot be caught)Freeze a process
SIGCONT18ContinueResume a stopped process
Always try SIGTERM before SIGKILL. SIGKILL (kill -9) does not let the process clean up — it can leave behind lock files, corrupt data being written to disk, or leave child processes orphaned. Give SIGTERM a few seconds to work first.

Background and Foreground Jobs

When you are working in a terminal, you often need to run something in the background while continuing other work. This is job control.
# Run a command in the background (the & at the end)
./long-running-task.sh &

# List background jobs in the current shell session
jobs
# Output: [1]+  Running  ./long-running-task.sh &

# Bring background job #1 to the foreground (interactive again)
fg %1

# Suspend the current foreground process (does not kill it, just pauses)
# Press Ctrl+Z

# Resume a suspended process in the background
bg %1

# Detach a process from the terminal entirely so it survives logout
# nohup redirects output to nohup.out and ignores SIGHUP
nohup ./long-task.sh &

# Or use disown to detach an already-running background job
./long-task.sh &
disown %1
Production tip: For long-running tasks on remote servers, use tmux or screen instead of nohup. They give you a persistent terminal session you can detach from and reattach to later, even after disconnecting SSH.

Systemd Services

Modern Linux distributions use systemd to manage services (daemons). Systemd is the first process that starts (PID 1) and it manages the lifecycle of everything else. Think of it as the conductor of an orchestra — it starts services in the right order, restarts them if they crash, and tracks their logs.
# Start a service right now
sudo systemctl start nginx

# Stop a running service
sudo systemctl stop nginx

# Restart (stop + start) -- use when config changes require a fresh process
sudo systemctl restart nginx

# Reload -- ask the service to re-read its config without stopping
# Not all services support this, but it avoids downtime when they do
sudo systemctl reload nginx

# Enable a service to start automatically on boot
sudo systemctl enable nginx

# Disable auto-start on boot (does not stop the currently running service)
sudo systemctl disable nginx

# Check if a service is running, enabled, and see recent log output
sudo systemctl status nginx

# List all running services
systemctl list-units --type=service --state=running

# List all failed services (first thing to check when something is broken)
systemctl list-units --type=service --state=failed

# View logs for a specific service (systemd captures stdout/stderr automatically)
sudo journalctl -u nginx

# Follow logs in real time (like tail -f)
sudo journalctl -u nginx -f

# Show logs from the last boot only (filter out noise from previous boots)
sudo journalctl -u nginx -b

# Show logs from the last hour
sudo journalctl -u nginx --since "1 hour ago"

Creating a Custom Service

When you deploy your own application, you write a unit file to let systemd manage it. This is one of the most practical skills in this chapter — every production application should be managed by systemd rather than run manually in a screen or tmux session.
# /etc/systemd/system/myapp.service
[Unit]
Description=My Application Server
# Start after networking is available (matters for web apps that bind to ports)
After=network.target
# Optional: also start after the database is ready
# Note: After= only controls ordering, not dependency. If postgresql fails to start,
# myapp still starts. Use Requires= for hard dependencies.
After=postgresql.service

[Service]
# Type=simple means systemd considers the service started as soon as the process launches
# Use Type=notify if your app signals readiness via sd_notify (more accurate health tracking)
# Use Type=forking if your app daemonizes itself (forks and parent exits)
Type=simple
User=deploy                    # Run as this user (never run app services as root)
Group=deploy
WorkingDirectory=/opt/myapp    # cd to this directory before starting
ExecStart=/opt/myapp/bin/server --port 8080
# Restart automatically if the process crashes (but not if it exits cleanly with code 0)
# Other options: always, on-abort, on-watchdog, no
Restart=on-failure
# Wait 5 seconds before restarting to avoid thrashing
# Without this, a crash-loop hammers your CPU and floods logs
RestartSec=5
# Cap restart attempts: if it crashes 5 times in 30 seconds, stop trying
StartLimitBurst=5
StartLimitIntervalSec=30
# Set environment variables for the service
Environment=NODE_ENV=production
# Or load environment from a file (better for secrets -- the file can be chmod 600)
# EnvironmentFile=/opt/myapp/.env

[Install]
# WantedBy=multi-user.target means "start this in normal multi-user mode"
# This is what makes 'systemctl enable' work (creates the symlink)
WantedBy=multi-user.target
# After creating or modifying a unit file, reload systemd's configuration
# Without this, systemd does not see your changes
sudo systemctl daemon-reload

# Start the service AND enable it for boot in one command
sudo systemctl enable --now myapp

# Verify it is running
sudo systemctl status myapp

# If something is wrong, check the logs (systemd captures stdout and stderr)
sudo journalctl -u myapp -f
Common systemd pitfalls: (1) Forgetting daemon-reload after editing a unit file — systemd uses a cached copy and your changes are ignored. (2) Using Type=simple when the app forks — systemd tracks the wrong PID and thinks the service died. (3) Not setting Restart=on-failure — a single crash at 3 AM takes your service offline until a human notices. (4) Running services as root when they do not need root privileges — use User= and Group= to run as a dedicated service account.

Key Takeaways

  • Every running program is a process with a PID, owner, and resource usage
  • Use htop for interactive monitoring, ps aux for scripting
  • Send SIGTERM first, SIGKILL only as a last resort
  • Job control (&, fg, bg, Ctrl+Z) manages processes within your terminal session
  • Systemd manages long-running services: start, stop, restart, and auto-start on boot
  • Always check systemctl status and journalctl -u when debugging service failures
  • Write custom unit files to let systemd manage your own applications

Next: Linux Networking →