Linux Command Line: The Stuff You'll Actually Use on Servers
A practical guide to the Linux command line for developers. Covers file navigation, file manipulation, permissions, process management, networking, and the commands you'll reach for on every server you touch.
If you deploy code to any server, you'll eventually SSH into a Linux box and need to figure out what's happening. Not "learn Linux system administration" -- just the practical stuff: find files, read logs, check what's running, restart a service, figure out why the disk is full.
This isn't a comprehensive Linux reference. This is the 20% of commands that handle 80% of what developers actually do on servers. The stuff you'll google less once you know it.
Navigating the Filesystem
You'll spend most of your time moving between directories and looking at files.
pwd # Where am I right now?
ls # What's in this directory?
ls -la # Everything, including hidden files, with details
ls -lh # Human-readable file sizes (KB, MB, GB)
cd /var/log # Go to an absolute path
cd .. # Go up one directory
cd - # Go back to the previous directory
cd ~ # Go to your home directory
The -la flags on ls show everything. -l gives the long format (permissions, owner, size, date). -a includes hidden files (those starting with .). You'll type ls -la a thousand times.
find / -name "nginx.conf" # Find by exact name
find /var/log -name "*.log" # Find by pattern
find . -name "*.py" -mtime -7 # Python files modified in last 7 days
find . -type f -size +100M # Files larger than 100MB
find . -name "node_modules" -type d # Find all node_modules directories
# Faster alternative (if installed)
locate nginx.conf # Uses a pre-built index, much faster
find searches in real-time and works everywhere. locate uses a cached database and is much faster for large filesystems, but the database needs to be updated periodically (sudo updatedb).
Reading and Searching Files
cat file.txt # Print entire file
less file.txt # Scrollable viewer (q to quit)
head -n 20 file.txt # First 20 lines
tail -n 20 file.txt # Last 20 lines
tail -f /var/log/syslog # Follow a log file in real-time (Ctrl+C to stop)
tail -f is one of the most useful commands for debugging. It continuously outputs new lines as they're written to a file. Open one terminal with tail -f on a log file, reproduce the bug in another terminal, and watch the errors appear.
Searching inside files:
grep "error" /var/log/syslog # Lines containing "error"
grep -i "error" /var/log/syslog # Case-insensitive
grep -r "TODO" ./src # Search recursively in a directory
grep -n "function" app.js # Show line numbers
grep -c "404" access.log # Count matching lines
grep -v "DEBUG" app.log # Lines NOT containing "DEBUG"
grep -A 3 "error" app.log # Show 3 lines after each match
grep -B 2 "error" app.log # Show 2 lines before each match
# Combine with pipes
grep "ERROR" app.log | grep -v "timeout" # Errors that aren't timeouts
grep -r (recursive) and grep -i (case-insensitive) are the ones you'll use most. For faster searching in large codebases, install ripgrep (rg) -- it's significantly faster than grep.
File Manipulation
cp file.txt backup.txt # Copy a file
cp -r src/ src-backup/ # Copy a directory recursively
mv old.txt new.txt # Rename/move a file
mv file.txt /tmp/ # Move to another directory
rm file.txt # Delete a file (no confirmation!)
rm -r directory/ # Delete a directory and its contents
rm -rf directory/ # Force delete (no prompts, no errors)
mkdir -p app/src/components # Create nested directories
touch newfile.txt # Create an empty file (or update timestamp)
Be extremely careful with rm -rf. There's no recycle bin. It's gone immediately. Double-check your path before hitting enter, especially when using variables or wildcards.
Working with text:
wc -l file.txt # Count lines
sort file.txt # Sort lines alphabetically
sort -n file.txt # Sort numerically
sort file.txt | uniq # Remove duplicate lines
sort file.txt | uniq -c # Count occurrences of each line
cut -d',' -f1,3 data.csv # Extract columns 1 and 3 from CSV
Pipes and Redirection
Pipes (|) are the superpower of the command line. They connect the output of one command to the input of another.
# Find the 10 largest files in a directory
du -ah /var/log | sort -rh | head -10
# Count unique IP addresses in an access log
awk '{print $1}' access.log | sort | uniq -c | sort -rn | head -20
# Find all Python files containing "import requests"
grep -rl "import requests" --include="*.py" .
# Monitor a log for errors and send to a file
tail -f app.log | grep --line-buffered "ERROR" >> errors.txt
Redirection:
command > file.txt # Redirect output to file (overwrite)
command >> file.txt # Append output to file
command 2> errors.txt # Redirect errors to file
command 2>&1 # Redirect errors to same place as output
command > /dev/null 2>&1 # Discard all output (silence a noisy command)
Permissions
Linux permissions confuse everyone at first, but the model is straightforward.
Every file has three permission groups: owner, group, others. Each group gets three permissions: read (r), write (w), execute (x).
ls -la
# -rw-r--r-- 1 deploy www-data 4096 Mar 26 10:00 app.py
# ^^^ owner
# ^^^ group
# ^^^ others
In this example: the owner (deploy) can read and write. The group (www-data) can read. Everyone else can read.
chmod 755 script.sh # rwxr-xr-x (owner: all, group+others: read+execute)
chmod 644 config.txt # rw-r--r-- (owner: read+write, everyone else: read)
chmod +x script.sh # Add execute permission for everyone
chmod u+w file.txt # Add write permission for owner only
chown deploy:www-data app.py # Change owner and group
chown -R deploy:deploy /app # Change recursively for a directory
The numeric system: read=4, write=2, execute=1. Add them up for each group. 755 means owner gets 7 (4+2+1=rwx), group gets 5 (4+1=r-x), others get 5 (4+1=r-x).
chmod 600 ~/.ssh/id_rsa # SSH keys: owner-only read/write
chmod 755 /var/www # Web root: owner writes, everyone reads
chmod 644 /var/www/*.html # Web files: owner writes, everyone reads
chmod +x deploy.sh # Make a script executable
Process Management
ps aux # List all running processes
ps aux | grep node # Find Node.js processes
top # Live process monitor (q to quit)
htop # Better live process monitor (install it)
kill 12345 # Send SIGTERM to process (graceful shutdown)
kill -9 12345 # Send SIGKILL (force kill, last resort)
killall node # Kill all processes named "node"
# Find what's using a port
lsof -i :3000 # What's listening on port 3000?
ss -tlnp # All listening TCP ports
netstat -tlnp # Same (older systems)
# Run something in the background
nohup node server.js & # Runs even after you disconnect
kill -9 should be a last resort. It terminates the process immediately without letting it clean up (close files, release locks, save state). Try kill (SIGTERM) first and give the process a few seconds to shut down gracefully.
Disk Space
When the disk fills up, things break in confusing ways. Databases crash, logs stop writing, deployments fail.
df -h # Disk usage by filesystem (human-readable)
du -sh /var/log # Total size of a directory
du -sh /var/log/* # Size of each item in a directory
du -ah /var | sort -rh | head -20 # 20 largest files/dirs under /var
# Common culprits for full disks:
du -sh /var/log # Log files
du -sh /tmp # Temp files
du -sh /var/lib/docker # Docker images and containers
Quick cleanup:
# Clear old log files (check what you're deleting first!)
sudo journalctl --vacuum-size=500M # Limit systemd journal to 500MB
sudo apt autoremove # Remove unused packages (Debian/Ubuntu)
docker system prune -a # Remove unused Docker data
Networking
curl https://api.example.com/health # Make HTTP request
curl -X POST -H "Content-Type: application/json" \
-d '{"key":"value"}' https://api.example.com/data
wget https://example.com/file.zip # Download a file
ping google.com # Test connectivity
dig example.com # DNS lookup
nslookup example.com # Another DNS lookup tool
ssh user@server.com # Connect to a remote server
scp file.txt user@server:/tmp/ # Copy file to remote server
scp user@server:/var/log/app.log ./ # Copy file from remote server
curl is your best friend for debugging APIs. Add -v for verbose output (shows headers), -s for silent mode (no progress bar), -o to save output to a file.
# Debug an API endpoint
curl -v https://api.example.com/users 2>&1 | head -30
# Check response headers only
curl -I https://example.com
# Follow redirects
curl -L https://example.com/old-page
# Send with authentication
curl -H "Authorization: Bearer token123" https://api.example.com/me
systemd: Managing Services
Most modern Linux servers use systemd to manage services (web servers, databases, your app).
sudo systemctl status nginx # Is nginx running?
sudo systemctl start nginx # Start the service
sudo systemctl stop nginx # Stop the service
sudo systemctl restart nginx # Restart the service
sudo systemctl reload nginx # Reload config without downtime
sudo systemctl enable nginx # Start automatically on boot
sudo systemctl disable nginx # Don't start on boot
# View service logs
sudo journalctl -u nginx # All logs for nginx
sudo journalctl -u nginx -f # Follow logs in real-time
sudo journalctl -u nginx --since "1 hour ago"
Environment Variables
echo $PATH # Print a variable
env # List all environment variables
export MY_VAR="hello" # Set a variable for this session
unset MY_VAR # Remove a variable
# Set for a single command
DATABASE_URL="postgres://..." node server.js
# Permanent variables (add to ~/.bashrc or ~/.profile)
echo 'export API_KEY="abc123"' >> ~/.bashrc
source ~/.bashrc # Reload without logging out
Practical Scenarios
"The app is down. What happened?"sudo systemctl status myapp # Is the service running?
sudo journalctl -u myapp --since "30 min ago" # Recent logs
tail -100 /var/log/myapp/error.log # Application error log
df -h # Is the disk full?
free -h # Is memory exhausted?
"The server is slow."
top # What's using CPU and memory?
htop # Better view (if installed)
iostat # Disk I/O statistics
vmstat 1 # System performance snapshot every second
"I need to deploy a fix quickly."
cd /app
git pull origin main
npm install --production
sudo systemctl restart myapp
sudo journalctl -u myapp -f # Watch logs to confirm it started
"Something is using port 3000."
sudo lsof -i :3000 # Find the process
sudo kill $(sudo lsof -t -i :3000) # Kill it
Tips That Save Time
Use tab completion. Press Tab to auto-complete file names and commands. Press Tab twice to see all options. This alone saves hours over a career. Use Ctrl+R for history search. Press Ctrl+R and start typing to search your command history. Way faster than hitting the up arrow 50 times. Use aliases for common commands:# Add to ~/.bashrc
alias ll='ls -la'
alias gs='git status'
alias dc='docker-compose'
alias ..='cd ..'
alias ...='cd ../..'
Use screen or tmux for long-running tasks. If your SSH connection drops, a screen or tmux session keeps running. Reconnect and reattach:
tmux new -s deploy # Start a named session
# ... do your work ...
# Ctrl+B, then D to detach
tmux attach -t deploy # Reattach later
You don't need to memorize all of this. Bookmark this page and come back when you need something specific. Over time, the commands you use most will become muscle memory.
If you're building your development skills and want to get comfortable with the tools and environments that professional developers use daily, CodeUp helps you build that practical knowledge from the ground up.