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
On EC2, /var/log/cloud-init-output.log captures your UserData script output - the first place to look when a bootstrap fails.
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
| Distro | Package Mgr | Default EC2 User | Notes |
|---|---|---|---|
| Amazon Linux 2023 | dnf | ec2-user | AWS-tuned, current default |
| Amazon Linux 2 | yum | ec2-user | Legacy, still widely used |
| Ubuntu | apt | ubuntu | Popular, large ecosystem |
| RHEL / Rocky / Alma | dnf/yum | ec2-user / rocky | Enterprise, RPM-based |
| Debian | apt | admin | Stable, minimal |
| SUSE (SLES) | zypper | ec2-user | SAP 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 /.
| Directory | Purpose |
|---|---|
/ | Root of the entire filesystem |
/bin, /usr/bin | Essential and user binaries (ls, cp, mv) |
/sbin, /usr/sbin | System admin binaries (mount, iptables) |
/etc | System-wide configuration files |
/home | User home directories |
/root | Home directory of the root user |
/var | Variable data (logs, spools, caches, mail) |
/var/log | Log files - your debugging home base |
/tmp | Temporary files (often cleared on reboot) |
/proc | Virtual FS exposing kernel/process info |
/sys | Virtual FS for devices and kernel objects |
/dev | Device files (disks: /dev/xvda, /dev/nvme0n1) |
/opt | Optional / third-party software packages |
/mnt, /media | Mount points for extra filesystems |
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
| Permission | Symbol | Octal |
|---|---|---|
| Read | r | 4 |
| Write | w | 2 |
| Execute | x | 1 |
| 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
| Bit | Octal | Effect |
|---|---|---|
| setuid | 4000 | Run as file owner (e.g. passwd) |
| setgid | 2000 | Run as group / inherit group on dirs |
| sticky | 1000 | Only owner can delete (e.g. /tmp) |
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
| File | Contents |
|---|---|
/etc/passwd | User accounts (name, UID, GID, home, shell) |
/etc/shadow | Hashed passwords (root-only) |
/etc/group | Group definitions and members |
/etc/sudoers | Who can run sudo and how |
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
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 # DownloadProcesses & 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
| Signal | Number | Meaning |
|---|---|---|
| SIGTERM | 15 | Polite request to terminate (default) |
| SIGKILL | 9 | Force kill, cannot be caught |
| SIGHUP | 1 | Hang up / reload configuration |
| SIGINT | 2 | Interrupt (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
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
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.
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 nginxapt (Ubuntu, Debian)
sudo apt update
sudo apt upgrade -y
sudo apt install -y nginx
sudo apt remove nginx
apt search keyword
dpkg -l | grep nginxAmazon 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
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.
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-idShell 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
-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
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
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"
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
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
sudoper command. - Key-only SSH: disable password auth (
PasswordAuthentication noin/etc/ssh/sshd_config). - Patch regularly:
dnf update -y/apt upgrade -y, or use SSM Patch Manager. - Host firewall:
firewalld(RHEL family) orufw(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
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
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
| Symptom | First commands to run |
|---|---|
| Instance out of disk | df -h, then du -sh /* | sort -rh |
| High CPU / slow | top, uptime, ps aux --sort=-%cpu | head |
| Out of memory / OOM kills | free -h, dmesg | grep -i oom |
| Service will not start | systemctl status svc, journalctl -u svc -xe |
| Cannot SSH in | Check SG, NACL, subnet route, key perms, /var/log/secure |
| App cannot reach internet | ip route, curl -v, check NAT/IGW route |
| UserData did not run | cat /var/log/cloud-init-output.log |
| Port not listening | ss -tulnp, lsof -i :PORT |
When something on Linux breaks, read the logs: journalctl -xe and tail -f /var/log/.... Ninety percent of issues announce themselves there.
📋 Study Checklist
- 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