The terminal is your gateway to cloud infrastructure. Whether you're SSH'd into an EC2 instance, troubleshooting Lambda container environments, or configuring ECS tasks, you need to navigate Unix-like systems confidently. This guide covers the essential Linux command-line skills you'll use daily as a cloud engineer.
At the end of this article, you will have a working understanding of terminals, shells, file systems, permissions, and package management. You'll know how to navigate remote servers, troubleshoot issues, and automate workflows using command-line tools.
Prerequisites
Before you start, you need:
- A Unix-like environment (Linux, macOS, or Windows with WSL)
- Basic familiarity with text editors
- An AWS account (optional, for cloud-specific examples)
Understanding Terminals and Shells
The words "terminal," "shell," and "command line" get thrown around interchangeably. Let me clarify what each term means and why the distinction matters.
What is a Terminal?
A terminal is a program that accepts text input and renders text output. Historically, terminals were physical devices—keyboards and monitors connected to mainframe computers. Today, you use terminal emulators like Ghostty, iTerm2, or the built-in Terminal on macOS.
When you open your terminal application, it displays a prompt where you can type commands. The terminal itself doesn't interpret those commands—it just handles the input and output.
What is a Shell?
A shell is the program that actually processes your commands. When you type echo "Hello World" and press Enter, the shell reads that text, evaluates it, executes it, and returns output. This cycle is called a REPL: Read, Evaluate, Print, Loop.
The two most common shells are:
- bash (Bourne Again Shell) - Default on most Linux distributions
- zsh (Z Shell) - Default on modern macOS
Both are powerful scripting languages that can handle variables, conditionals, loops, and functions. For this guide, all examples work in both bash and zsh.
Why This Matters for AWS
When you SSH into an EC2 instance, you're connecting to a shell session on that remote machine. When you configure Lambda container images, you're often writing shell scripts. When you troubleshoot ECS tasks, you're examining shell output logs.
Understanding the shell means understanding how your cloud infrastructure executes commands.
Let me show you what I mean. Open your terminal and run:
$ echo "Hello World"
The shell reads this command, identifies echo as a program, passes "Hello World" as an argument, and prints the result. Now try:
$ expr 123456 + 7890
Your output shows:
131346
The shell can perform arithmetic, manipulate strings, and execute complex logic. This is why shell scripting is foundational for DevOps automation.
File System Navigation
Cloud servers are just Linux machines. You need to navigate their file systems to configure applications, examine logs, and troubleshoot issues.
Your Current Location
At any moment, your shell has a "working directory"—the folder you're currently in. Check it with:
$ pwd
This prints your working directory. On macOS, you might see:
/Users/cindy
On Linux:
/home/cindy
This is your home directory, where your personal files live.
File Paths
A file path is a text representation of a file's location in the directory tree. All absolute paths start from the root directory (/).
Your home directory on Linux might be /home/achar, which means:
- Start at the root
/ - Enter the
homedirectory - Enter the
achardirectory
Each directory is separated by a forward slash.
Listing Files
The ls command lists directory contents:
$ ls
You see files and directories in your current location. Add flags to customize the output:
$ ls -l
This shows a "long" format with permissions, owners, file sizes, and modification dates. Add the -a flag to show hidden files:
$ ls -la
Hidden files start with a dot (.). Configuration files like .bashrc or .zshrc are hidden by default.
Changing Directories
Navigate the file system with cd:
$ cd /var/log
This moves you to /var/log, where system logs are stored. Check your new location:
$ pwd
The output shows /var/log.
To go up one directory level, use the special alias ..:
$ cd ..
Now you're in /var. To return to your home directory from anywhere:
$ cd ~
The tilde (~) is an alias for your home directory.
Relative vs Absolute Paths
Absolute paths start with / and work from anywhere:
$ cd /var/log/nginx
Relative paths start from your current location:
$ cd nginx
This only works if a nginx directory exists in your current location.
AWS Context: EC2 Log Files
When you SSH into an EC2 instance running a web application, you often need to examine logs. Application logs typically live in:
-
/var/log- System and service logs -
/var/log/nginxor/var/log/apache2- Web server logs -
/var/log/mysql- Database logs
To check your web server's error log:
$ cd /var/log/nginx
$ ls -l
You see files like access.log and error.log. This workflow is identical whether you're troubleshooting locally or on a remote server.
Working with Files
You need to read, create, modify, and delete files constantly. Let me show you the essential commands.
Reading File Contents
The cat command prints an entire file:
$ cat /etc/hosts
This displays your system's hostname mappings. For large files, cat is overwhelming because it dumps everything to your screen.
Reading Partial Contents
For large files, use head to see the first lines:
$ head -n 10 /var/log/syslog
This shows the first 10 lines. Similarly, tail shows the last lines:
$ tail -n 20 /var/log/syslog
The -n flag specifies the number of lines.
Following Log Files
When troubleshooting active applications, you want to see new log entries as they're written. Use tail -f:
$ tail -f /var/log/nginx/access.log
This continuously displays new lines as they're appended. Press Ctrl+C to stop.
Searching Within Files
The grep command searches for text patterns:
$ grep "error" /var/log/syslog
This prints every line containing "error". The search is case-sensitive by default. For case-insensitive search:
$ grep -i "error" /var/log/syslog
To search recursively through directories:
$ grep -r "database connection" /var/log
This searches all files in /var/log and its subdirectories.
Finding Files
The find command locates files by name or pattern:
$ find /var/log -name "*.log"
This lists all files ending in .log within /var/log. The asterisk (*) is a wildcard matching any characters.
To find files modified in the last 24 hours:
$ find /var/log -mtime -1
Creating Files
The touch command creates empty files:
$ touch test.txt
If test.txt already exists, touch updates its modification timestamp without changing its contents.
Creating Directories
Make new directories with mkdir:
$ mkdir project
To create nested directories in one command:
$ mkdir -p project/src/components
The -p flag creates parent directories as needed.
Moving and Renaming
The mv command both moves and renames files:
$ mv old-name.txt new-name.txt
To move a file to another directory:
$ mv config.json /etc/myapp/
Copying Files
The cp command copies files:
$ cp source.txt destination.txt
To copy directories recursively:
$ cp -r source-dir destination-dir
Deleting Files
The rm command removes files:
$ rm unnecessary-file.txt
To remove directories and their contents:
$ rm -r old-directory
Warning: There's no recycle bin on the command line. Deleted files are gone permanently.
AWS Context: S3 and Local Files
When you work with S3, you frequently sync files between your EC2 instance and S3 buckets. The AWS CLI uses familiar commands:
$ aws s3 cp local-file.json s3://my-bucket/data/
This copies a local file to S3. To download:
$ aws s3 cp s3://my-bucket/data/config.json ./
The patterns mirror standard file operations. Understanding local file manipulation makes cloud storage operations intuitive.
Permissions and Users
Unix-like systems have robust permission systems. This is critical for HIPAA-compliant infrastructure where you need to restrict access to protected health information (PHI).
Understanding Permission Strings
When you run ls -l, you see permission strings like:
-rw-r--r-- 1 achar staff 1234 Nov 13 09:30 file.txt
drwxr-xr-x 5 achar staff 160 Nov 13 09:31 directory
Let me break down the first string: -rw-r--r--
The first character indicates the type:
-
-= regular file -
d= directory
The remaining nine characters form three groups of three:
- Owner permissions (rw-): The user who owns the file
- Group permissions (r--): Users in the file's group
- Other permissions (r--): Everyone else
Each group has three permissions:
-
r= read -
w= write -
x= execute
A dash (-) means the permission is denied.
So -rw-r--r-- means:
- Owner can read and write
- Group members can read
- Others can read
- Nobody can execute
Checking Your User
Your username:
$ whoami
To see which groups you belong to:
$ groups
Changing Permissions
The chmod command modifies permissions. The syntax uses:
-
u= user (owner) -
g= group -
o= others -
a= all
To grant execute permission to the owner:
$ chmod u+x script.sh
To remove write permission from others:
$ chmod o-w sensitive-data.txt
To set permissions for everyone:
$ chmod a+r public-file.txt
You can combine changes:
$ chmod u=rwx,g=rx,o=r program.sh
This sets owner to read/write/execute, group to read/execute, and others to read-only.
The Execute Permission
For files, execute permission allows running the file as a program. For directories, it allows entering the directory.
When you download a script, you often need to make it executable:
$ chmod +x deploy.sh
$ ./deploy.sh
The ./ prefix explicitly runs the script in your current directory.
Changing Ownership
The chown command changes file ownership. This requires elevated privileges:
$ sudo chown nginx:nginx /var/www/html/index.html
This changes the owner to nginx and the group to nginx. The syntax is user:group.
The Root User
The root user is the superuser with unrestricted access to everything. Running commands as root is powerful and dangerous.
The sudo command lets you execute single commands as root:
$ sudo systemctl restart nginx
You'll be prompted for your password. After entering it correctly, the command runs with root privileges.
Healthcare Context: HIPAA Compliance
When building HIPAA-compliant systems, file permissions protect PHI. Database credentials, encryption keys, and patient data files must have restricted permissions.
A secure configuration file might have:
$ chmod 600 /etc/myapp/database.conf
This ensures only the owner can read or write the file. No group members or others can access it.
Your EC2 instance's IAM role should follow the principle of least privilege—grant only the minimum permissions needed. Similarly, file permissions should be as restrictive as possible while maintaining functionality.
Programs and Executables
Understanding how programs execute helps you troubleshoot deployment issues and write better automation scripts.
Compiled vs Interpreted Programs
Programs come in two flavors:
Compiled programs are converted to machine code before execution. Languages like Go, Rust, and C produce binaries—executable files containing processor instructions. These run directly on your hardware without needing additional software.
Interpreted programs require an interpreter to execute them. Python scripts need the Python interpreter. Shell scripts need a shell (bash or zsh).
When you run a compiled program:
$ /usr/bin/nginx
The operating system loads the binary and executes it.
When you run an interpreted program:
$ python3 app.py
The Python interpreter reads app.py, parses it, and executes the instructions.
Shebangs
Shell scripts and Python scripts often start with a shebang—a special first line indicating which interpreter to use:
#!/bin/bash
echo "This is a bash script"
Or for Python:
#!/usr/bin/env python3
print("This is a Python script")
The shebang tells the operating system which interpreter to invoke. With a proper shebang and execute permissions, you can run scripts directly:
$ chmod +x script.py
$ ./script.py
Without the shebang, you'd need to explicitly specify the interpreter:
$ python3 script.py
Finding Programs
The which command shows a program's location:
$ which python3
Output might show:
/usr/bin/python3
This tells you where the Python interpreter is installed.
AWS Context: Lambda Execution Environments
AWS Lambda functions run in execution environments—essentially small Linux containers. When you deploy Python code to Lambda, AWS provides the Python interpreter. Your code runs as an interpreted program.
For performance-critical workloads, you can deploy compiled binaries as custom Lambda runtimes. A Go binary, for example, starts faster and uses less memory than interpreted Python.
Understanding this distinction helps you make architectural decisions. Need microsecond response times? Use compiled languages. Need rapid development? Interpreted languages work well.
Environment Variables and the PATH
Environment variables configure programs without hardcoding values. The PATH variable is the most important one you'll work with.
What is PATH?
PATH is an environment variable containing a colon-separated list of directories. When you run a command, your shell searches these directories for a matching executable.
Check your PATH:
$ echo $PATH
You see something like:
/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
This means the shell searches (in order):
/usr/local/bin/usr/bin/bin/usr/sbin/sbin
When you type python3, the shell finds /usr/bin/python3 and executes it. You don't need to type the full path.
Adding to PATH
Programs you install often need to be added to PATH. To add a directory for the current session:
$ export PATH="$PATH:/home/achar/bin"
This appends /home/achar/bin to your existing PATH. Programs in that directory are now accessible by name.
To make this permanent, add the export command to your shell configuration file:
- bash:
~/.bashrc - zsh:
~/.zshrc
Edit the file:
$ nano ~/.zshrc
Add at the end:
export PATH="$PATH:/home/achar/bin"
Save and exit. New shell sessions will include this directory in PATH.
To apply changes to your current session:
$ source ~/.zshrc
Creating Environment Variables
Set environment variables with export:
$ export DATABASE_URL="postgresql://localhost:5432/mydb"
Programs can read this variable. In Python:
import os
db_url = os.getenv("DATABASE_URL")
print(f"Connecting to {db_url}")
AWS Context: Lambda Environment Variables
Lambda functions use environment variables extensively. You set them in the AWS Console or via Infrastructure as Code:
# Lambda function code
import os
import boto3
bucket_name = os.getenv("S3_BUCKET_NAME")
s3 = boto3.client("s3")
In your CloudFormation or Terraform configuration:
resource "aws_lambda_function" "processor" {
function_name = "data-processor"
environment {
variables = {
S3_BUCKET_NAME = "patient-records-bucket"
LOG_LEVEL = "INFO"
}
}
}
This pattern separates configuration from code—crucial for multi-environment deployments (dev, staging, production).
Healthcare Context: Secure Credentials
Never hardcode database passwords or API keys. Use environment variables:
$ export DB_PASSWORD="$(aws secretsmanager get-secret-value --secret-id prod-db --query SecretString --output text)"
This retrieves a secret from AWS Secrets Manager and stores it in an environment variable. Your application reads it without ever exposing the password in source code.
For HIPAA compliance, audit all environment variable usage. Ensure sensitive values come from secure sources like Secrets Manager, not plaintext files.
Input, Output, and Streams
Programs communicate through three standard streams: standard input (stdin), standard output (stdout), and standard error (stderr).
Standard Output
When a program prints results, it writes to stdout. The echo command demonstrates this:
$ echo "Hello"
The text appears in your terminal because stdout is directed there by default.
Redirecting Output
You can redirect stdout to a file with >:
$ echo "Log entry" > application.log
This creates application.log with the content "Log entry". If the file exists, > overwrites it.
To append instead of overwriting, use >>:
$ echo "Another entry" >> application.log
Standard Error
Programs write error messages to stderr, a separate stream from stdout. This separation lets you handle errors differently from regular output.
Most commands write errors to stderr:
$ ls nonexistent-directory
You see an error message. To redirect stderr to a file:
$ ls nonexistent-directory 2> errors.log
The 2> syntax redirects stderr (file descriptor 2). To redirect both stdout and stderr:
$ command > output.log 2> errors.log
Or combine them into one file:
$ command > combined.log 2>&1
Standard Input
Programs can read from stdin. The read command in bash demonstrates this:
#!/bin/bash
echo "What is your name?"
read name
echo "Hello, $name"
When you run this script, it waits for your input.
Piping
The pipe operator (|) connects stdout of one program to stdin of another:
$ cat application.log | grep "ERROR"
This reads application.log, then filters lines containing "ERROR". Piping creates powerful command chains.
Find the 10 most common error messages:
$ grep "ERROR" application.log | sort | uniq -c | sort -rn | head -10
Breaking this down:
-
grepextracts error lines -
sortarranges them alphabetically -
uniq -ccounts duplicates -
sort -rnsorts numerically in reverse -
head -10shows the top 10
AWS Context: CloudWatch Logs
When your Lambda function writes to stdout, AWS captures it in CloudWatch Logs. Your Python code:
def lambda_handler(event, context):
print(f"Processing event: {event}")
return {"statusCode": 200}
The print statement writes to stdout, which appears in CloudWatch:
$ aws logs tail /aws/lambda/my-function --follow
Understanding stdout/stderr helps you design effective logging strategies. Errors should go to stderr, normal operation to stdout.
Package Managers
Package managers automate software installation, dependency resolution, and updates. They're essential for maintaining cloud infrastructure.
Linux: apt
On Ubuntu and Debian-based systems, use apt:
$ sudo apt update
This refreshes the package index—the database of available software.
To install a package:
$ sudo apt install nginx
This installs the Nginx web server, including all dependencies. The package manager:
- Downloads Nginx and its dependencies
- Installs everything in the correct locations
- Configures the system PATH
- Sets up systemd services (if applicable)
To remove a package:
$ sudo apt remove nginx
macOS: Homebrew
On macOS, Homebrew is the de facto package manager:
$ brew install python3
This installs Python 3 and adds it to your PATH automatically.
Update all installed packages:
$ brew upgrade
Python: pip
Python has its own package manager:
$ pip install boto3
This installs the AWS SDK for Python. Use requirements.txt to manage project dependencies:
boto3==1.26.137
requests==2.28.2
psycopg2-binary==2.9.5
Install everything in the file:
$ pip install -r requirements.txt
AWS Context: EC2 User Data
When launching EC2 instances, you can run initialization scripts via User Data. This often involves package managers:
#!/bin/bash
apt-get update
apt-get install -y nginx python3-pip
pip3 install boto3
# Configure application
systemctl enable nginx
systemctl start nginx
This script runs when the instance first boots, installing dependencies and starting services.
Healthcare Context: Auditable Deployments
For HIPAA compliance, you need auditable deployment processes. Package managers help by:
- Versioning dependencies explicitly
- Creating reproducible environments
- Tracking what's installed on each server
Your requirements.txt becomes part of your compliance documentation, proving exactly which software versions handle PHI.
Troubleshooting Framework
When something breaks—and it will—follow this systematic approach.
Step 1: Identify the Symptom
What's actually failing? Be specific:
- "The application returns 502 errors"
- "The database connection times out"
- "The Lambda function exceeds memory limits"
Vague problems like "it doesn't work" waste time.
Step 2: Check the Logs
Logs contain most answers. For web servers:
$ tail -100 /var/log/nginx/error.log
For application logs:
$ journalctl -u myapp.service -n 100
For AWS services, check CloudWatch Logs:
$ aws logs tail /aws/lambda/my-function --since 10m
Step 3: Verify Permissions
Permission issues cause many failures. Check file permissions:
$ ls -l /var/www/html/config.php
Is the owner correct? Are permissions too restrictive?
Check your user's permissions:
$ groups
Are you in the right groups?
Step 4: Test Connectivity
Network issues are common. Test connections:
$ curl https://api.example.com/health
For databases:
$ telnet database.example.com 5432
If the connection fails, check security groups, network ACLs, and firewall rules.
Step 5: Verify Environment Variables
Many issues stem from missing or incorrect environment variables:
$ echo $DATABASE_URL
Is it set? Is it correct?
Step 6: Reproduce Locally
Try to replicate the issue on your development machine. If you can't reproduce it locally, the problem is environmental—likely configuration or permissions on the server.
Real-World Example: Deploying a Python Web Application
Let me walk through a complete deployment to an EC2 instance, demonstrating these concepts together.
Provision the Instance
Launch an Ubuntu 22.04 EC2 instance. Connect via SSH:
$ ssh -i keypair.pem ubuntu@ec2-xx-xx-xx-xx.compute-1.amazonaws.com
Install Dependencies
Update package index:
$ sudo apt update
Install Python and pip:
$ sudo apt install -y python3-pip python3-venv nginx
Create Application Structure
Create a directory:
$ mkdir -p /home/ubuntu/myapp
$ cd /home/ubuntu/myapp
Create a virtual environment:
$ python3 -m venv venv
$ source venv/bin/activate
Install Application Dependencies
Create requirements.txt:
flask==2.3.0
gunicorn==20.1.0
boto3==1.26.137
psycopg2-binary==2.9.5
Install:
$ pip install -r requirements.txt
Configure Environment Variables
Create a .env file:
export DATABASE_URL="postgresql://user:password@db.example.com:5432/mydb"
export S3_BUCKET="patient-records-bucket"
export AWS_REGION="us-east-1"
Load it:
$ source .env
Create the Application
Write app.py:
from flask import Flask, jsonify
import boto3
import os
app = Flask(__name__)
s3 = boto3.client("s3", region_name=os.getenv("AWS_REGION"))
bucket = os.getenv("S3_BUCKET")
@app.route("/health")
def health():
return jsonify({"status": "healthy"})
@app.route("/files")
def list_files():
response = s3.list_objects_v2(Bucket=bucket, MaxKeys=10)
files = [obj["Key"] for obj in response.get("Contents", [])]
return jsonify({"files": files})
if __name__ == "__main__":
app.run(host="0.0.0.0", port=5000)
Test locally:
$ python app.py
Open another terminal and test:
$ curl http://localhost:5000/health
Configure Gunicorn
Create a systemd service file:
$ sudo nano /etc/systemd/system/myapp.service
Add:
[Unit]
Description=My Flask Application
After=network.target
[Service]
User=ubuntu
WorkingDirectory=/home/ubuntu/myapp
Environment="PATH=/home/ubuntu/myapp/venv/bin"
EnvironmentFile=/home/ubuntu/myapp/.env
ExecStart=/home/ubuntu/myapp/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:5000 app:app
[Install]
WantedBy=multi-user.target
Enable and start:
$ sudo systemctl enable myapp
$ sudo systemctl start myapp
$ sudo systemctl status myapp
Configure Nginx
Create Nginx configuration:
$ sudo nano /etc/nginx/sites-available/myapp
Add:
server {
listen 80;
server_name _;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Enable the site:
$ sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
$ sudo nginx -t
$ sudo systemctl reload nginx
Verify Deployment
Test from your local machine:
$ curl http://ec2-xx-xx-xx-xx.compute-1.amazonaws.com/health
You should see:
{"status": "healthy"}
Troubleshoot Issues
If it doesn't work, check logs:
$ sudo journalctl -u myapp -n 50
$ sudo tail -50 /var/log/nginx/error.log
Common issues:
-
Permission denied: Check file permissions with
ls -l -
Connection refused: Verify Gunicorn is running with
systemctl status myapp - 502 Bad Gateway: Nginx can't reach Gunicorn—check the proxy configuration
This workflow demonstrates terminals, shells, file navigation, permissions, environment variables, package management, and troubleshooting—all in a real deployment scenario.
Conclusion
The Linux command line is your primary interface to cloud infrastructure. You've learned:
- How terminals and shells work
- File system navigation and manipulation
- Permission systems and their security implications
- Program execution models
- Environment variables and PATH management
- Standard streams and piping
- Package managers for dependency management
- A systematic troubleshooting framework
- Real-world deployment workflows
These skills transfer directly to AWS and other cloud platforms. Whether you're managing EC2 instances, debugging Lambda functions, or configuring ECS tasks, you'll use these commands daily.
For your AWS certification preparation, practice these commands in real cloud environments. Spin up EC2 instances, SSH in, and deploy applications. The command line becomes intuitive through repetition.
As you build HIPAA-compliant healthcare systems, remember that proper file permissions, secure environment variable management, and auditable deployments start with solid Linux fundamentals.
The command line is the foundation of modern cloud engineering.
Top comments (0)