Skip to content

Linux & the Command Line for Developers

Whether you're running Linux natively, using macOS, or working in WSL on Windows — this is the foundation you need. Most servers run Linux. Most security tools are built for Linux. And once you understand it, everything else clicks.


TL;DR

  • macOS and Linux share the same Unix DNA — most commands are identical
  • The terminal is not scary. It's faster, more powerful, and more transparent than any GUI
  • File permissions aren't optional — they're your first line of defense
  • Never run as root. Never install packages globally. Never skip the firewall.
  • Virtual environments are not optional for Python development

The Terminal: Why You Hate It, Why You Need It, Why You'll Love It

Let's be honest. When most people see a terminal for the first time, their reaction is somewhere between confusion and mild disgust. It's a black box with a blinking cursor. No buttons. No icons. No visual feedback. You can't see your project taking shape. You can't watch a file render as you edit it. You can't drag and drop something into a satisfying folder. You type into the void and hope something happens.

And compared to a GUI? The contrast is brutal.

A GUI — graphical user interface, meaning any tool with a visual interface like VS Code's file explorer, GitHub Desktop, Finder, or even Excel — gives you something the terminal fundamentally doesn't: you can see what you're doing. Nobody wants an ugly spreadsheet of raw numbers when they could have a beautiful chart. Think about the difference between staring at rows of data versus seeing it transformed into a bar chart, a line graph, a pie chart, a scatter plot, a heatmap, a waterfall chart, a treemap — something that makes the data make sense at a glance. GUIs build trust. They give you guardrails. They show you your project as it's being built.

The terminal gives you none of that comfort.

The real complaints — and they're all valid:

  • You can't see your project while you're working in it
  • You have to memorize commands instead of clicking menus
  • One typo and everything breaks — sometimes silently
  • You have to know the exact path you're in at all times (pwd becomes your best friend out of necessity)
  • No undo button. No "are you sure?" Most of the time.
  • Error messages that read like they were written by someone who hates you
  • It looks ugly. A wall of white text on black feels like 1995.
  • You feel stupid — like everyone else knows something you don't

Every single one of these complaints is legitimate. Nobody is going to tell you the terminal is intuitive, because it isn't. It was designed by engineers for engineers, before visual interfaces existed, and it never had to compete for your affection.

But here's the truth: it's an evil necessity.

Whether you're a student, building apps as a side hustle, vibe coding your way through a project, or working toward a career in tech — the terminal is going to show up. Every deployment tool uses it. Every server runs on it. Every serious development workflow eventually touches it. The question isn't whether you'll use it. The question is whether you'll be comfortable when you do.


Why Developers Actually Use It

Here's what changes once you get past the learning curve.

The terminal is faster. Not marginally — dramatically. A task that takes four clicks through a GUI, waiting for a window to load, navigating a menu, clicking confirm — takes one command in a terminal. Developers who work in terminals aren't masochists. They're optimizing.

The terminal is precise. When you click a button in a GUI, you're trusting that button does exactly what the label says. In a terminal, you see the exact command being executed. There's no abstraction layer hiding what's actually happening to your files, your server, your database.

The terminal is scriptable. You can automate repetitive tasks, chain commands together, write scripts that run at 3am without you touching anything. GUIs can't do this. This is why every DevOps role, every cloud platform, every CI/CD pipeline, every security tool is built around the command line. Businesses aren't requiring terminal skills to be difficult — they're requiring them because automation at scale is impossible without them.

The terminal is universal. The same commands that work on your local Linux machine work on a server in a data center in another country. The same commands work in Docker containers, in cloud shells, in CI pipelines. GUIs are platform-specific. The terminal is everywhere.

Think about it like this: In gaming, there's a long-running debate — console controller vs keyboard and mouse. Console players have the comfort, the familiarity, the ease of picking up and playing. But in competitive gaming, keyboard and mouse players almost universally outperform console players. The precision of a mouse versus a thumbstick. The speed of keyboard shortcuts versus button combinations. The ability to rebind every key to exactly what makes sense for you. It's not that controllers are bad — it's that once you invest in the higher-precision tool and learn it, you don't go back. The terminal is keyboard and mouse. GUIs are the controller.

You will get used to it. Then one day you'll open a GUI tool to do something simple and think — why is this so slow? That's when you know it clicked.

And yes — you can make it look good too. We'll get to that. Jump to Ricing Your Terminal →


What Is Linux — and Why Do Developers Use It

Linux is an open-source operating system that powers the majority of the world's servers, cloud infrastructure, and security tools. When you deploy an app to AWS, DigitalOcean, or any VPS — you're almost certainly deploying to Linux.

macOS and Linux share the same DNA. macOS is built on Unix (specifically BSD Unix). Linux is Unix-like, built to follow the same POSIX standards. This means the two operating systems behave almost identically at the command line. If you learned something on a Mac, it almost certainly works on Linux. If it doesn't, there's usually a one-line workaround.

This is one of the main reasons developers prefer Mac over Windows — the terminal behavior translates directly to production servers.

What about Windows? Windows has WSL — Windows Subsystem for Linux. It lets you run a full Linux environment (Ubuntu, Debian, etc.) directly inside Windows without a virtual machine. If you're on Windows, WSL is how you access everything covered in this page.

What about running Kali or other Linux distros?

If you're learning security, pentesting, or just want to experiment with a different Linux distribution without touching your main machine — this is where virtual machines come in.

A virtual machine (VM) is exactly what it sounds like: a virtual computer running inside your real computer. Think of it like running a completely separate laptop inside a window on your screen. It has its own operating system, its own files, its own settings — completely isolated from your main machine. If you break something inside the VM, your real computer is fine. If you get a virus inside the VM, it stays there. It's a sandbox.

This is why security students and researchers run Kali Linux (a security-focused Linux distro loaded with tools) in a VM on their Mac or Windows machine. You get the full Linux environment without risking your main setup.

Popular VM software:

  • VirtualBox — free, open source, runs on Mac and Windows
  • VMware Fusion (Mac) / VMware Workstation (Windows) — more polished, free tier available
  • Parallels (Mac only) — fastest option for Mac, paid

Think of the filesystem like Windows Explorer or Mac Finder — the thing you use to click through folders and files. The terminal does the exact same thing, just with commands instead of clicks.

A directory is just a folder. Same concept as a folder on your desktop — you can put files in it, create more folders inside it, move things in and out. The only difference is you're doing it by typing instead of clicking.

A file path is the address of a file or folder on your system. Just like a street address tells you exactly where a house is, a file path tells your computer exactly where a file lives. For example:

/Users/youruser/projects/myapp/app.py

That reads left to right: start at the root of the system (/), go into Users, then youruser, then projects, then myapp, and the file is app.py.

Your home directory is your personal folder — the place the terminal drops you when you open it. On Linux it's /home/youruser/. On macOS it's /Users/youruser/. The shortcut for it is always ~ on both systems. So ~/projects means "the projects folder inside my home directory" — no matter whose machine you're on.

These commands are identical on both Linux and macOS:

ls              # list files and folders in current directory
ls -la          # list everything including hidden files, with permissions
cd ~/projects   # move into a directory (folder)
cd ..           # go up one level (back to the parent folder)
pwd             # print working directory — "where am I right now?"
mkdir myproject # create a new directory (folder)
touch app.py    # create an empty file
cat file.txt    # print the contents of a file to the screen
cp file.txt backup.txt      # copy a file
mv file.txt ../file.txt     # move or rename a file
rm file.txt                 # delete a file

rm -rf — Read This Before You Ever Run It

rm deletes files. -r means recursive — delete everything inside a folder too. -f means force — no confirmation, no warnings, no second chances.

Put them together: rm -rf deletes a folder and absolutely everything inside it, instantly, permanently, with no undo.

rm -rf /        # deletes your entire system. yes, everything.
rm -rf ~/       # deletes your entire home directory. all your files. gone.
rm -rf ./       # deletes everything in the current directory

There is no Trash. There is no Ctrl+Z. There is no recovery without a backup. This command has ended careers, wiped servers, and destroyed projects. Always double-check what directory you're in (pwd) before running any rm -rf command. Always.

Where things live — Linux vs macOS:

Location What it is Linux macOS
Home folder Your personal space /home/youruser/ /Users/youruser/
System config System-wide settings files /etc/ /etc/ (same)
Logs System and app log files /var/log/ /var/log/ (same)
Temp files Temporary files, cleared on reboot /tmp/ /tmp/ (same)
Installed programs Where software lives /usr/bin/ /usr/local/bin/
macOS only Apple system files /System/, /Library/

The ~ symbol always means your home directory on both systems. When you see a path starting with ~, it's personal to whoever is logged in.


Things Nobody Tells You (But Should)

A note before we go further.

This guide isn't for the computer science grads or the seasoned security engineers. They already know this. And honestly? Most of them aren't going to teach you it either — not because they're bad people, but because that's just not how tech works. You'll get a link to a Stack Overflow thread. You'll get told to "just Google it." You'll sit in a class where the professor assumes you already know what a shell is. You'll watch a YouTube tutorial where the guy opens a terminal and starts typing like you're supposed to know what any of it means.

It's not malicious. It's just that most people in tech learned by doing — by breaking things, Googling the error, fixing it, breaking it again. 90% of what experienced developers know came from research, not a classroom. That's still the gold standard. But nobody hands you the starting point.

That's why I'm here. This section is the stuff nobody shows you. The commands that make you feel stupid when you don't know them and obvious once you do. The things that would have saved you three hours of frustration if someone had just said it out loud.

Don't give up. You're not behind. You just haven't been shown yet.


man — the manual that's always there

Every command on Linux and macOS has a built-in manual. Most people don't know this exists. Type man followed by any command and it shows you exactly what it does and every option available:

man ls        # full manual for ls
man chmod     # full manual for chmod
man grep      # full manual for grep

Press q to exit the manual. You now never have to Google basic command syntax again.


clear — clean your screen

Your terminal is cluttered with output from the last ten commands. Type clear and it wipes the screen. That's it. Ctrl + L does the same thing faster.


whoami — who are you running as

whoami

Prints your current username. Sounds trivial. Becomes critical when you're using sudo and need to confirm whether you're running as yourself or as root. Good habit to check before running anything sensitive.


Tab completion — the thing that will save you

Press Tab while typing a command or file path and the terminal will autocomplete it for you. If there are multiple options, press Tab twice and it shows you all of them. This works for commands, file names, folder names, everything.

cd ~/pro[TAB]     # autocompletes to cd ~/projects if that's the only match

Once you start using Tab completion you will never stop. It also prevents typos — if Tab won't complete it, the path doesn't exist.


The up arrow — your command history

Press the up arrow key in your terminal and it cycles through your previous commands. Press it again for the one before that. You never have to retype a long command — just arrow up and hit Enter.

history           # shows your full command history with numbers
!42               # re-runs command number 42 from your history

echo — printing to the screen

echo "Hello world"             # prints Hello world
echo $HOME                     # prints your home directory path
echo "API_KEY=abc123" >> .env  # appends a line to a file

You'll see echo constantly in tutorials and scripts. It prints text to the terminal or writes it to a file. It's also how you check what a variable contains.


grep — finding things

grep searches for text inside files or filters the output of other commands. You will use this constantly:

grep "password" config.txt        # find lines containing "password" in a file
grep -r "API_KEY" ./              # search recursively through all files in a folder
ps aux | grep python              # show only running processes that mention python
cat .env | grep SECRET            # find lines in .env containing SECRET

That | symbol is called a pipe. It takes the output of one command and feeds it as input to the next. ps aux lists all processes. grep python filters that list to only lines containing "python." Chain as many commands together as you need.


nano vs vim — terminal text editors

Sometimes you need to edit a file directly in the terminal — a config file on a server, a cron job, a system setting. Two editors come up constantly:

  • nano — simple, beginner-friendly. Opens a file, you type, Ctrl + S to save, Ctrl + X to exit. Use this.
  • vim — powerful, extremely fast once you know it, deeply confusing until you do. If you accidentally open vim and can't exit: press Esc, then type :q! and hit Enter. That's it. You're welcome.
nano config.txt     # open a file in nano
vim config.txt      # open a file in vim (only if you know what you're doing)

"How do I exit vim" is genuinely one of the most Googled questions in programming history. Now you know.


File Permissions

Every file on Linux and macOS has permissions that control who can read it, change it, or run it. This is identical on both systems and it matters enormously for security.

r, w, x — what they actually mean:

  • r (read) — you can open and read the file. For a directory, you can list what's inside it.
  • w (write) — you can modify the file, save changes, or delete it. For a directory, you can create or delete files inside it.
  • x (execute) — you can run the file as a program or script. For a directory, you can enter it with cd. Without x on a directory, you can't go inside it even if you have r.

When you run ls -la you'll see something like:

-rwxr-xr-- 1 youruser staff 4096 May 1 12:00 script.sh

Breaking that down:

- rwx r-x r--
│ │   │   └── others (everyone else): read only
│ │   └────── group (your team): read and execute
│ └────────── owner (you): read, write, and execute
└──────────── file type: - means file, d means directory

In plain English for that example: you can read, edit, and run this file. People in your group can read and run it but not edit it. Everyone else can only read it.

A - in any position means that permission is missing. r-- means read only. --- means no access at all.


chmod — changing permissions

chmod stands for "change mode." It's the command you use to set who can do what with a file. You'll use it constantly — especially to lock down files that contain secrets.

The numbers work like this: each permission has a value. r = 4, w = 2, x = 1. Add them together for each group:

  • 7 = 4+2+1 = read, write, execute
  • 6 = 4+2 = read, write
  • 5 = 4+1 = read, execute
  • 4 = read only
  • 0 = no access

Three digits = owner, group, others. So chmod 600 means owner gets read+write (6), group gets nothing (0), others get nothing (0).

chmod 600 .env          # only you can read or write — use this for ALL secret files
chmod 700 script.sh     # only you can read, write, and run this script
chmod 755 script.sh     # you have full access, others can read and run but not edit
chmod -R 755 folder/    # apply permissions recursively to everything in a folder
chown youruser file.txt # change who owns a file

When to use what:

  • .env files, private keys, credentials → always chmod 600
  • Scripts you want to run → chmod 700 (private) or chmod 755 (shared)
  • Public web files → chmod 644 or chmod 755

The .env File — What It Is and How to Use It

Before we talk about securing your .env file, you need to know what it actually is — because most tutorials assume you already know and skip right over it.

A .env file is a plain text file that stores environment variables — key-value pairs that your app reads at runtime instead of having sensitive values hardcoded in your code.

Instead of writing this in your code:

api_key = "sk-abc123supersecretkey"

You write this in your .env file:

API_KEY=sk-abc123supersecretkey
DATABASE_URL=postgresql://localhost/mydb
SECRET_KEY=somethingveryrandom

And then your code reads it like this (Python example):

import os
from dotenv import load_dotenv

load_dotenv()
api_key = os.getenv("API_KEY")

Why this matters: If your API key is hardcoded in your code and you push to GitHub, it's exposed to the entire internet. If it's in a .env file that's listed in .gitignore, it never leaves your machine.

How to create one:

touch .env              # create an empty .env file
nano .env               # open it and add your variables
chmod 600 .env          # lock it down immediately

What goes in .gitignore:

.env
*.env
.env.local
.env.production

The dot at the start of .env means it's a hidden file. On Linux and macOS, any file starting with . is hidden by default — it won't show up in ls, only in ls -la. This is intentional. Your secrets should be quiet.

Never commit your .env file

GitHub has seen millions of accidentally committed .env files. API keys, database passwords, secret tokens — all public, all exploitable within minutes by automated scanners. Add it to .gitignore before your first commit. Not after.


File Permissions Quick Reference

Command What it does
chmod 600 .env Owner read/write only — use for secrets
chmod 700 script.sh Owner only, full access
chmod 755 script.sh Owner full, others read/execute
chmod -R 755 folder/ Apply recursively to folder
chown user file Change file owner
ls -la See permissions on all files

Users, sudo, and Root

Before we get into users and sudo, two terms show up constantly that nobody explains to beginners — shell and SSH.

A shell is the program that runs inside your terminal and interprets the commands you type. Think of it like a window — you're on one side giving instructions, and the operating system is the contractor on the other side doing the actual work. The shell sits between you and passes your commands through. The most common shells are Bash (default on most Linux) and Zsh (default on macOS since 2019). When someone says "run this in your shell" they just mean: open your terminal and type it.

SSH (Secure Shell) is how you connect to another computer securely over a network — a remote server, a VPS, a cloud machine. It's encrypted so nobody can snoop on what you're sending. It uses key pairs to authenticate you — if you haven't set those up yet, the SSH Keys → section covers it and is worth reading before you manage any remote servers. sshd is the background service on a server that listens for incoming SSH connections, and its config file at /etc/ssh/sshd_config is where you control who can connect and how.


Root is the superuser account on Linux and macOS — it has unrestricted access to everything on the system. No file is off limits. No permission can stop it. You should never run your day-to-day work as root.

sudo lets you run a single command with root privileges without being root:

sudo apt update        # run this command as root, just this once
sudo nano /etc/hosts   # edit a system file that requires root access

Fun fact

The name SudoChef isn't an accident. sudo is the command that says "I'm running this as the boss." sudo literally means "superuser do." So SudoChef = superuser chef. Someone who can run root whenever they want. Let me cook — I run it all, in the kitchen and on the command line.

On macOS, sudo works identically. The difference is that macOS has System Integrity Protection (SIP) which blocks certain system directories even from root — a security layer Linux doesn't have by default.

The sudoers file controls who can use sudo and what they can do with it. It lives at /etc/sudoers and should only ever be edited with visudo — a special editor that validates the file before saving, so you can't accidentally lock yourself out:

sudo visudo   # the only safe way to edit sudoers — never edit it directly

A safe sudoers entry looks like:

youruser ALL=(ALL) ALL

A dangerous one looks like:

youruser ALL=(ALL) NOPASSWD: ALL   # never do this — no password required for anything

Disabling root login over SSH is a standard server hardening step. On a Linux server, open the SSH daemon config:

sudo nano /etc/ssh/sshd_config

Find this line and set it to no:

PermitRootLogin no

Then restart the SSH service:

sudo systemctl restart sshd

This means even if someone gets your root password, they can't log directly into your server as root over SSH. They'd need a regular user account first.

To lock the root account locally on Linux:

sudo passwd -l root


Basic Networking Commands

These help you understand what's happening on your network and are essential for security work:

ping google.com              # test if a host is reachable
curl https://example.com     # fetch a URL — see the raw response
wget https://example.com/file.zip  # download a file

Checking open ports and connections:

# Linux
ss -tulnp          # show all open ports and what's listening on them
netstat -tulnp     # older equivalent, still works on most systems

# macOS
netstat -an        # macOS still uses netstat — ss not available by default
lsof -i            # list all open network connections — works on Mac and Linux
lsof -i :8080      # what's using port 8080 specifically?

Mac vs Linux

ss is the modern Linux tool — it replaced netstat on most distros. macOS still ships netstat and doesn't include ss. Use lsof -i as your go-to cross-platform option.

The ufw firewall — Linux only:

A firewall controls what network traffic is allowed in and out of your machine. ufw (Uncomplicated Firewall) is the beginner-friendly Linux firewall tool:

sudo apt install ufw     # install if not present
sudo ufw enable          # turn it on
sudo ufw status          # check current rules
sudo ufw allow 22        # allow SSH (do this BEFORE enabling if on a remote server)
sudo ufw allow 80        # allow HTTP
sudo ufw allow 443       # allow HTTPS
sudo ufw deny 3306       # block MySQL from outside access
sudo ufw delete allow 80 # remove a rule

If you're on a remote server

Run sudo ufw allow 22 before you run sudo ufw enable. If you enable the firewall without allowing SSH first, you will lock yourself out of your own server.

macOS firewall

macOS uses pf under the hood but you don't need to touch it directly. Go to System Settings → Network → Firewall and enable it. For most developers on Mac, this is sufficient.


Package Management

Package managers install, update, and remove software. The concept is identical everywhere — only the tool changes:

# Ubuntu / Debian Linux
sudo apt update           # refresh the list of available packages
sudo apt upgrade          # upgrade all installed packages
sudo apt install python3  # install a package
sudo apt remove python3   # remove a package

# macOS (Homebrew)
brew update               # refresh
brew upgrade              # upgrade all
brew install python3      # install
brew uninstall python3    # remove

# RHEL / Fedora / CentOS
sudo dnf install python3
sudo yum install python3  # older systems

Homebrew was directly inspired by Linux package managers — it's why Mac developers feel immediately at home on Linux servers.

Security habit: Keep packages updated. Unpatched packages are one of the most common attack vectors on real systems.

# Automatic security updates on Ubuntu
sudo apt install unattended-upgrades
sudo dpkg-reconfigure unattended-upgrades

Processes and Monitoring

A process is any program currently running on your system. Every terminal command, every app, every background service is a process.

ps aux              # list every running process
top                 # live process monitor — updates in real time (Linux and Mac)
htop                # better version of top, install separately: sudo apt install htop
kill 1234           # send a stop signal to process ID 1234
kill -9 1234        # force kill — no cleanup, just stop
pkill python        # kill all processes with "python" in the name

Finding what's using a port:

lsof -i :8080              # what process is using port 8080?
ss -tulnp | grep 8080      # Linux alternative

If you see a process you don't recognize listening on a port, that's worth investigating. Unknown listeners are a red flag.


Virtual Environments

This section is critical for Python developers and is identical on Linux and macOS.

First — understand the difference between two things people confuse constantly:

  • .env file — a plain text file containing environment variables like API keys. Loaded by your app at runtime. Covered in detail above.
  • venv — a Python virtual environment. A completely isolated folder containing its own Python interpreter and its own packages. Has nothing to do with .env files despite the similar name.

They are completely different things. Both matter. Neither replaces the other.

Why you never install Python packages globally:

pip install requests   # never do this outside a venv

Installing globally means every project on your system shares the same packages. Version conflicts break things. A compromised package affects your entire machine. It's a mess that's hard to undo.

The right way — always use a venv:

# Create a virtual environment inside your project folder
python3 -m venv venv

# Activate it
source venv/bin/activate        # Linux and macOS
venv\Scripts\activate           # Windows

# Your prompt will change to show (venv) — you're now isolated
(venv) $ pip install requests   # installs only inside this project

# When you're done
deactivate

Always add these to .gitignore:

venv/
.env
__pycache__/
*.pyc

Never commit your virtual environment folder or your .env file to GitHub. Ever.

Want to go deeper?

IDE security (VS Code and PyCharm hardening, extension vetting, settings sync risks), Windows-specific hardening, and enterprise managed device security are covered in full in the Secure Your Ship PDF guide.


Ricing Your Terminal — Making It Yours

The terminal doesn't have to look like 1995. The developer community has spent decades making it beautiful, fast, and deeply personal. "Ricing" is what people call customizing your terminal setup — themes, fonts, colors, prompts, shortcuts — until it looks and feels exactly like yours.

This isn't required. Learn the basics first. Come back here when you're ready to make it home.

The essentials to look into:

  • Oh-My-Zsh — a framework for Zsh that gives you themes, plugins, and a massive community of customizations. Start here: ohmyz.sh
  • Powerlevel10k — the most popular terminal theme. Highly configurable, genuinely fast, genuinely beautiful.
  • Nerd Fonts — fonts patched with developer icons and symbols that power-user themes require. JetBrains Mono Nerd Font is a great starting point: nerdfonts.com
  • iTerm2 (macOS) / Alacritty (Linux) / Windows Terminal (WSL) — better terminal emulators than the system defaults. Split panes, better colors, search.
  • Aliases — shortcuts you define yourself in ~/.zshrc:
alias gs="git status"
alias gp="git push origin main"
alias projects="cd ~/projects"
alias activate="source venv/bin/activate"
  • zsh-autosuggestions — suggests commands as you type based on your history
  • zsh-syntax-highlighting — colors your commands as you type so you catch typos before hitting Enter

Look up terminal setups on Reddit's r/unixporn when you want inspiration. Fair warning: you will lose an afternoon.