Why Linux for AWS?

Over 90% of cloud workloads run on Linux. EC2 instances, ECS/EKS containers, Lambda execution environments, and most managed services run on a Linux kernel under the hood. Solid Linux skills let you:

  • Debug EC2 instances over SSH and Session Manager
  • Write UserData / cloud-init bootstrap scripts
  • Build and run Docker containers for ECS, EKS, and Fargate
  • Read logs, trace processes, and diagnose performance issues
  • Answer scenario questions on SOA-C02, DVA-C02, and SAP-C02 exams
💡 Tip

On EC2, /var/log/cloud-init-output.log captures your UserData script output - the first place to look when a bootstrap fails.

☁️ Amazon Linux 2023

AL2023 is the current default Amazon Linux. It uses dnf (not yum), systemd, and ships with SELinux in permissive mode. The default login user is ec2-user.

Distributions & Shells

DistroPackage MgrDefault EC2 UserNotes
Amazon Linux 2023dnfec2-userAWS-tuned, current default
Amazon Linux 2yumec2-userLegacy, still widely used
UbuntuaptubuntuPopular, large ecosystem
RHEL / Rocky / Almadnf/yumec2-user / rockyEnterprise, RPM-based
DebianaptadminStable, minimal
SUSE (SLES)zypperec2-userSAP workloads

The shell is the program that interprets your commands. bash is the most common; sh, zsh, and dash also appear. Check yours with echo $SHELL.

cat /etc/os-release       # Identify the distribution and version
echo $SHELL               # Your login shell
uname -r                  # Kernel version
hostnamectl               # Host, OS, kernel summary (systemd)

Linux File System (FHS)

Linux follows the Filesystem Hierarchy Standard. Everything is a file, and everything hangs off a single root /.

DirectoryPurpose
/Root of the entire filesystem
/bin, /usr/binEssential and user binaries (ls, cp, mv)
/sbin, /usr/sbinSystem admin binaries (mount, iptables)
/etcSystem-wide configuration files
/homeUser home directories
/rootHome directory of the root user
/varVariable data (logs, spools, caches, mail)
/var/logLog files - your debugging home base
/tmpTemporary files (often cleared on reboot)
/procVirtual FS exposing kernel/process info
/sysVirtual FS for devices and kernel objects
/devDevice files (disks: /dev/xvda, /dev/nvme0n1)
/optOptional / third-party software packages
/mnt, /mediaMount points for extra filesystems
💡 Everything is a file

Disks, processes, and even network sockets are represented as files. That is why tools like cat /proc/cpuinfo and cat /proc/meminfo work.

File Permissions & Ownership

-rwxr-xr-- 1 ubuntu aws-team 1234 Jul 10 12:00 deploy.sh
│└──┬──┘└──┬──┘└──┬──┘
│   │      │      └── Other: r-- (4)
│   │      └───────── Group: r-x (5)
│   └──────────────── Owner: rwx (7)
└── File type: - file, d directory, l symlink
PermissionSymbolOctal
Readr4
Writew2
Executex1
None-0
chmod 755 script.sh          # rwxr-xr-x
chmod 640 secret.conf        # rw-r-----
chmod +x script.sh           # Add execute for all
chmod -R g+w /shared         # Recursive group write
chown ubuntu:team file.txt   # Change owner:group
chgrp team file.txt          # Change group only
umask 022                    # Default permission mask

Special permission bits

BitOctalEffect
setuid4000Run as file owner (e.g. passwd)
setgid2000Run as group / inherit group on dirs
sticky1000Only owner can delete (e.g. /tmp)
⚠ AWS Exam Tip

SSH private keys must be chmod 400 (or 600). SSH refuses to use a .pem that is readable by group/other with an "UNPROTECTED PRIVATE KEY FILE" error.

Users, Groups & sudo

# Identity
whoami; id                    # Current user, UID/GID/groups
who; w                        # Who is logged in

# Manage users (need root/sudo)
sudo useradd -m -s /bin/bash alice   # Create user with home + shell
sudo passwd alice                    # Set password
sudo usermod -aG docker alice        # Add to supplementary group
sudo userdel -r alice                # Delete user and home

# Groups
sudo groupadd deploy
groups alice                         # Show a user's groups

# Elevated privileges
sudo command                         # Run one command as root
sudo -i                              # Interactive root shell
visudo                               # Safely edit /etc/sudoers
FileContents
/etc/passwdUser accounts (name, UID, GID, home, shell)
/etc/shadowHashed passwords (root-only)
/etc/groupGroup definitions and members
/etc/sudoersWho can run sudo and how
☁️ AWS Context

On EC2, the default user (ec2-user / ubuntu) is granted passwordless sudo via a file in /etc/sudoers.d/. SSM Session Manager connects as ssm-user.

Text Processing (grep, sed, awk)

# grep - search text
grep -i "error" app.log          # Case-insensitive
grep -r "TODO" src/              # Recursive
grep -c "404" access.log         # Count matches
grep -v "DEBUG" app.log          # Invert (exclude)
grep -E "warn|error" app.log     # Extended regex

# sed - stream editor (find/replace)
sed 's/foo/bar/g' file.txt       # Replace all foo with bar
sed -i 's/8080/80/g' app.conf    # In-place edit
sed -n '10,20p' file.txt         # Print lines 10-20

# awk - column/field processing
awk '{print $1}' access.log      # First field of each line
awk -F',' '{print $3}' data.csv  # CSV third column
awk '$9 == 500 {print}' access.log  # Rows where field 9 is 500
awk '{sum+=$1} END {print sum}' nums.txt  # Sum a column

# cut / sort / uniq / wc
cut -d':' -f1 /etc/passwd         # First colon-delimited field
sort file.txt | uniq -c | sort -rn  # Frequency count, descending
wc -l file.txt                    # Count lines
💡 Real-world combo

Find the top 10 IPs hitting your server: awk '{print $1}' access.log | sort | uniq -c | sort -rn | head

Command Reference

# Pipes and redirection
command | other           # Pipe stdout to next command
command > file            # Redirect stdout (overwrite)
command >> file           # Redirect stdout (append)
command 2> err.log        # Redirect stderr
command > out.log 2>&1    # Redirect both stdout and stderr
command < input.txt       # Feed file as stdin
command1 && command2      # Run command2 only if command1 succeeds
command1 || command2      # Run command2 only if command1 fails

# Search the filesystem
find / -name "*.log" -type f          # By name
find /var -size +100M                 # Files larger than 100MB
find . -mtime -1                      # Modified in last 24h
find /tmp -type f -delete             # Find and delete
which python3; whereis nginx; type ls # Locate binaries

# Archives & compression
tar -czvf archive.tar.gz /mydir       # Create gzip tarball
tar -xzvf archive.tar.gz              # Extract gzip tarball
tar -tzvf archive.tar.gz              # List contents
zip -r backup.zip /mydir; unzip backup.zip
gzip file; gunzip file.gz

# Transfer & sync
scp -i key.pem file ec2-user@IP:/tmp/ # Secure copy
rsync -avz src/ ec2-user@IP:/dest/    # Efficient sync
curl -O https://example.com/file.tgz  # Download
wget https://example.com/file.tgz     # Download

Processes & Signals

ps aux                    # All running processes
ps aux | grep nginx       # Filter for nginx
ps -ef --forest           # Process tree
pgrep -a nginx            # PIDs by name
top                       # Real-time process viewer
htop                      # Friendlier top (if installed)

# Sending signals
kill PID                  # SIGTERM (15) - graceful stop
kill -9 PID               # SIGKILL (9) - force kill
kill -HUP PID             # SIGHUP (1) - reload config
pkill nginx               # Kill by name
killall -9 python3        # Kill all matching

# Job control
command &                 # Run in background
jobs                      # List background jobs
fg %1; bg %1              # Foreground / background a job
nohup long_task &         # Survive logout
SignalNumberMeaning
SIGTERM15Polite request to terminate (default)
SIGKILL9Force kill, cannot be caught
SIGHUP1Hang up / reload configuration
SIGINT2Interrupt (Ctrl+C)

Memory, CPU & Load

free -h                   # Memory usage (human readable)
vmstat 1                  # Virtual memory stats every 1s
top                       # CPU + memory live view
uptime                    # Load averages (1, 5, 15 min)
nproc                     # Number of CPU cores
cat /proc/cpuinfo         # CPU details
cat /proc/meminfo         # Memory details
iostat -x 1               # Disk I/O stats (sysstat pkg)
sar -u 1 5                # Historical CPU sampling
💡 Reading load average

Load average is the number of processes waiting to run. On a machine with 4 cores, a load of 4.0 means fully busy. A load consistently above your core count means CPU saturation - a common CloudWatch alarm scenario.

Disks, Partitions & Mounts

df -h                     # Disk usage per filesystem
du -sh /var/log           # Size of a directory
du -sh * | sort -rh | head  # Biggest items in current dir
lsblk                     # Block devices tree
blkid                     # Show UUIDs and filesystem types
mount | column -t         # Currently mounted filesystems

# Format and mount a new EBS volume (Nitro: /dev/nvme1n1)
sudo mkfs -t xfs /dev/nvme1n1
sudo mkdir /data
sudo mount /dev/nvme1n1 /data
# Persist across reboot via /etc/fstab (use UUID from blkid)
echo 'UUID=xxxx /data xfs defaults,nofail 0 2' | sudo tee -a /etc/fstab
⚠ AWS Exam Tip

On Nitro-based instances, EBS volumes appear as NVMe devices (/dev/nvme1n1), not /dev/xvdf. Use lsblk to map them. Always add nofail in /etc/fstab so a missing volume does not block boot.

☁️ Grow a filesystem after resizing EBS

After increasing an EBS volume: sudo growpart /dev/nvme0n1 1 then sudo xfs_growfs / (xfs) or sudo resize2fs /dev/nvme0n1p1 (ext4).

Package Management

dnf / yum (Amazon Linux, RHEL)

sudo dnf update -y
sudo dnf install -y nginx
sudo dnf remove nginx
dnf search keyword
dnf list installed
rpm -qa | grep nginx

apt (Ubuntu, Debian)

sudo apt update
sudo apt upgrade -y
sudo apt install -y nginx
sudo apt remove nginx
apt search keyword
dpkg -l | grep nginx
⚠ AWS Exam Tip

Amazon Linux 2 uses yum; Amazon Linux 2023 uses dnf; Ubuntu uses apt. UserData scripts must match the AMI's package manager or the bootstrap fails silently.

Linux Networking Commands

# Interfaces & addresses
ip addr show                  # Show IP addresses (modern)
ip route                      # Routing table
ip link                       # Network interfaces

# Connectivity
ping -c 4 8.8.8.8             # ICMP reachability
traceroute google.com         # Path to a host
mtr google.com                # Live traceroute + loss

# DNS
nslookup example.com          # Basic DNS lookup
dig example.com               # Detailed DNS query
dig +short example.com        # Just the answer
host example.com              # Simple lookup

# Ports & sockets
ss -tulnp                     # Listening TCP/UDP + PIDs (modern)
netstat -tulnp                # Legacy equivalent
lsof -i :80                   # What is using port 80

# HTTP debugging
curl -I https://example.com   # Response headers only
curl -v https://api.example.com   # Verbose handshake
curl -s -o /dev/null -w "%{http_code}\n" URL  # Just status code
⚠ AWS Exam Tip

If you can ping an EC2 instance but cannot reach port 22, check the Security Group inbound rules and Network ACLs first. AWS network controls are evaluated before traffic ever reaches the OS firewall.

☁️ Instance Metadata (IMDS)

Query instance metadata from inside EC2. Prefer IMDSv2 (token-based):

TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id

Shell Scripting

#!/bin/bash
set -euo pipefail          # Exit on error, unset var, pipe failure

# Variables
REGION="us-east-1"
NAME="${1:-default}"       # First arg or "default"
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id)

# Conditionals
if [ -f "/etc/nginx/nginx.conf" ]; then
  echo "Nginx config exists"
elif [ -d "/etc/nginx" ]; then
  echo "Directory exists but no config"
else
  echo "Nginx not installed"
fi

# Loops
for FILE in /var/log/*.log; do
  echo "Processing: $FILE"
done

COUNT=0
while [ $COUNT -lt 3 ]; do
  echo "Attempt $COUNT"; COUNT=$((COUNT+1))
done

# Functions
log() { echo "[$(date +%FT%T)] $*"; }
log "Bootstrap starting"

# Exit codes
if command -v docker >/dev/null 2>&1; then
  log "docker present"
else
  log "docker missing"; exit 1
fi

Typical EC2 UserData bootstrap

#!/bin/bash
dnf update -y
dnf install -y nginx
systemctl enable --now nginx
echo "<h1>Hello from $(hostname)</h1>" > /usr/share/nginx/html/index.html
💡 Test conditions

-f file exists, -d directory exists, -z string empty, -n string not empty, -eq/-ne/-lt/-gt numeric comparisons.

Cron & Scheduling

crontab -e                # Edit current user's crontab
crontab -l                # List cron jobs

# Format:  minute hour day month weekday  command
# ┌── min (0-59)
# │ ┌── hour (0-23)
# │ │ ┌── day of month (1-31)
# │ │ │ ┌── month (1-12)
# │ │ │ │ ┌── day of week (0-6, Sun=0)
# * * * * * /path/script.sh

0 2 * * *   /opt/backup.sh          # Every day at 02:00
*/15 * * * * /opt/healthcheck.sh    # Every 15 minutes
0 0 * * 0   /opt/weekly.sh          # Weekly, Sunday midnight
@reboot     /opt/startup.sh         # Once at boot
☁️ systemd timers

Modern alternative to cron. List active timers with systemctl list-timers. Timers give logging via journald and dependency handling.

Logs & journald

# Traditional log files
tail -f /var/log/messages         # Amazon Linux / RHEL
tail -f /var/log/syslog           # Ubuntu / Debian
less /var/log/secure              # Auth events (RHEL family)
less /var/log/auth.log            # Auth events (Debian family)
cat /var/log/cloud-init-output.log  # UserData output on EC2

# systemd journal
journalctl -u nginx -f            # Follow one service
journalctl -u nginx --since "1 hour ago"
journalctl -xe                    # Recent entries + explanations
journalctl -p err -b              # Errors from current boot
journalctl --disk-usage           # How much space logs use
💡 Ship logs to CloudWatch

Install the CloudWatch agent to stream /var/log/* to CloudWatch Logs, then query with Logs Insights. Common on SOA-C02 monitoring scenarios.

SSH & EC2 Access

# Connect to EC2
chmod 400 my-key.pem
ssh -i my-key.pem ec2-user@54.123.45.67   # Amazon Linux
ssh -i my-key.pem ubuntu@54.123.45.67     # Ubuntu

# SSH config (~/.ssh/config)
Host my-ec2
    HostName 54.123.45.67
    User ec2-user
    IdentityFile ~/.ssh/my-key.pem
# Then just: ssh my-ec2

# Copy files
scp -i key.pem file.txt ec2-user@IP:/home/ec2-user/
scp -i key.pem -r localdir/ ec2-user@IP:/tmp/

# Port forwarding (tunnel to a private RDS through a bastion)
ssh -i key.pem -L 5432:rds-endpoint:5432 ec2-user@IP

# Jump host / bastion
ssh -i key.pem -J ec2-user@BASTION ec2-user@PRIVATE_IP

# Generate a new key pair
ssh-keygen -t ed25519 -C "you@example.com"
💡 Prefer SSM Session Manager

AWS Systems Manager Session Manager gives shell access with no open port 22, no bastion, and no key pairs - access is controlled by IAM and fully logged. Frequently the "most secure" exam answer: aws ssm start-session --target i-0abc123.

Systemd & Services

systemctl start nginx        # Start now
systemctl stop nginx         # Stop now
systemctl restart nginx      # Restart
systemctl reload nginx       # Reload config without downtime
systemctl enable nginx       # Auto-start on boot
systemctl enable --now nginx # Enable and start in one step
systemctl disable nginx      # Do not start on boot
systemctl status nginx       # Current status + recent logs
systemctl is-active nginx    # Quick active check
systemctl daemon-reload      # Reload unit files after edits

A minimal unit file

# /etc/systemd/system/myapp.service
[Unit]
Description=My App
After=network.target

[Service]
ExecStart=/usr/bin/python3 /opt/myapp/app.py
Restart=always
User=appuser

[Install]
WantedBy=multi-user.target
☁️ Why systemd matters on AWS

Restart=always keeps your app alive after crashes, and enable ensures it survives instance reboots and Auto Scaling replacements.

Security Hardening Basics

  • Least privilege: avoid logging in as root; use sudo per command.
  • Key-only SSH: disable password auth (PasswordAuthentication no in /etc/ssh/sshd_config).
  • Patch regularly: dnf update -y / apt upgrade -y, or use SSM Patch Manager.
  • Host firewall: firewalld (RHEL family) or ufw (Ubuntu) in addition to Security Groups.
  • SELinux / AppArmor: mandatory access control; check with getenforce.
  • Fail2ban: block repeated failed logins.
ufw allow 22/tcp; ufw enable        # Ubuntu firewall
firewall-cmd --add-service=https --permanent  # RHEL family
getenforce                          # SELinux mode
sudo lastb                          # Failed login attempts
⚠ Defense in depth

Security Groups protect the network edge, but OS-level controls (firewall, SELinux, patching) matter too. Exams reward layered security answers.

Containers on Linux

# Docker basics (foundation for ECS / EKS / Fargate)
docker ps                     # Running containers
docker ps -a                  # All containers
docker images                 # Local images
docker run -d -p 80:80 nginx  # Run detached, map port
docker exec -it CID /bin/bash # Shell into a container
docker logs -f CID            # Follow container logs
docker build -t myapp:1.0 .   # Build from Dockerfile
docker stop CID; docker rm CID

# Push to Amazon ECR
aws ecr get-login-password --region us-east-1 \
  | docker login --username AWS --password-stdin ACCOUNT.dkr.ecr.us-east-1.amazonaws.com
docker tag myapp:1.0 ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0
docker push ACCOUNT.dkr.ecr.us-east-1.amazonaws.com/myapp:1.0
☁️ Namespaces & cgroups

Containers are just Linux processes isolated with namespaces (what they can see) and limited with cgroups (how much CPU/memory they can use). No VM involved.

Troubleshooting Playbook

SymptomFirst commands to run
Instance out of diskdf -h, then du -sh /* | sort -rh
High CPU / slowtop, uptime, ps aux --sort=-%cpu | head
Out of memory / OOM killsfree -h, dmesg | grep -i oom
Service will not startsystemctl status svc, journalctl -u svc -xe
Cannot SSH inCheck SG, NACL, subnet route, key perms, /var/log/secure
App cannot reach internetip route, curl -v, check NAT/IGW route
UserData did not runcat /var/log/cloud-init-output.log
Port not listeningss -tulnp, lsof -i :PORT
💡 The universal first step

When something on Linux breaks, read the logs: journalctl -xe and tail -f /var/log/.... Ninety percent of issues announce themselves there.

📋 Study Checklist

Progress0%
  • Identify the distribution with cat /etc/os-release
  • Understand the Linux directory structure (FHS)
  • Navigate with cd, ls, pwd, find, tree
  • Know how file permissions work (rwx / octal)
  • Use chmod, chown, chgrp and understand umask
  • Understand setuid, setgid, and the sticky bit
  • Manage users and groups (useradd, usermod, groups)
  • Use sudo and understand /etc/sudoers
  • Process text with grep, sed, awk, cut, sort, uniq
  • Use pipes and redirects (|, >, >>, 2>&1, &&, ||)
  • Manage processes with ps, top, kill and know signals (15, 9, 1)
  • Check memory, CPU, and load (free, top, uptime, vmstat)
  • Inspect disks and mounts (df, du, lsblk, mount, /etc/fstab)
  • Format and mount an EBS/NVMe volume
  • Install packages with dnf, yum, and apt
  • Use networking commands (ip, ping, dig, ss, curl, traceroute)
  • Query IMDSv2 for instance metadata
  • Write a bash script with variables, conditionals, loops, functions
  • Use set -euo pipefail for safe scripts
  • Schedule tasks with cron and systemd timers
  • Read logs with tail -f, less, and journalctl
  • Connect to EC2 via SSH, SCP, tunnels, and jump hosts
  • Prefer SSM Session Manager over open port 22
  • Manage services with systemctl and write a unit file
  • Apply basic hardening (key-only SSH, patching, firewall, SELinux)
  • Run containers with Docker and push images to ECR
  • Follow a troubleshooting playbook for common EC2 issues