Bash Projects
Five practical, portfolio-ready Bash projects that combine everything from this series — a server health checker, log analyzer, backup tool, deploy script, and AWS resource auditor.
Project 1: Server Health Checker
Combines: functions, conditions, exit codes, file handling.
#!/usr/bin/env bash
set -euo pipefail
check_disk() {
local usage
usage=$(df / | awk 'NR==2 {print $5}' | tr -d '%')
[ "$usage" -lt 90 ] && echo "OK: Disk usage ${usage}%" || { echo "CRITICAL: Disk usage ${usage}%" >&2; return 1; }
}
check_memory() {
local available
available=$(free | awk '/Mem/ {printf "%.0f", $7/$2 * 100}')
[ "$available" -gt 10 ] && echo "OK: ${available}% memory available" || { echo "CRITICAL: only ${available}% memory available" >&2; return 1; }
}
check_load() {
local load cores
load=$(uptime | awk -F'load average:' '{print $2}' | awk -F, '{print $1}' | tr -d ' ')
cores=$(nproc)
echo "Load: $load (cores: $cores)"
}
main() {
local failed=0
check_disk || failed=1
check_memory || failed=1
check_load
exit "$failed"
}
main "$@"
Project 2: Log Analyzer With Alerting
Combines: text processing, log processing, networking automation.
#!/usr/bin/env bash
set -euo pipefail
LOGFILE="${1:?Usage: $0 <logfile> [threshold]}"
THRESHOLD="${2:-50}"
ERROR_COUNT=$(grep -c "ERROR" "$LOGFILE" || true)
echo "Errors found: $ERROR_COUNT (threshold: $THRESHOLD)"
if [ "$ERROR_COUNT" -gt "$THRESHOLD" ]; then
echo "=== Top error types ==="
grep "ERROR" "$LOGFILE" | awk -F'ERROR: ' '{print $2}' | sort | uniq -c | sort -rn | head -5
[ -n "${WEBHOOK_URL:-}" ] && curl -sf -X POST -H 'Content-Type: application/json' \
-d "{\"text\":\"Alert: $ERROR_COUNT errors in $LOGFILE\"}" "$WEBHOOK_URL"
exit 1
fi
Project 3: Automated Backup Tool
Combines: file handling, archiving, cron scheduling, error handling.
#!/usr/bin/env bash
set -euo pipefail
SOURCE_DIR="${1:?Usage: $0 <source_dir> <dest_dir>}"
DEST_DIR="${2:?Usage: $0 <source_dir> <dest_dir>}"
DATE=$(date +%Y%m%d-%H%M%S)
ARCHIVE="$DEST_DIR/backup-$DATE.tar.gz"
RETENTION_DAYS=30
mkdir -p "$DEST_DIR"
tar -czf "$ARCHIVE" -C "$(dirname "$SOURCE_DIR")" "$(basename "$SOURCE_DIR")"
echo "Backup created: $ARCHIVE ($(du -h "$ARCHIVE" | cut -f1))"
find "$DEST_DIR" -name "backup-*.tar.gz" -mtime "+$RETENTION_DAYS" -print -delete
echo "Cleaned up backups older than $RETENTION_DAYS days"
0 2 * * * /usr/bin/bash /opt/scripts/backup.sh /data /backups >> /var/log/backup.log 2>&1
Project 4: Zero-Downtime Deploy Script
Combines: SSH automation, signals & trap, functions, exit codes.
#!/usr/bin/env bash
set -euo pipefail
SERVERS=("web1.example.com" "web2.example.com" "web3.example.com")
APP_DIR="/opt/myapp"
FAILED_SERVERS=()
deploy_to_server() {
local server="$1"
ssh -o BatchMode=yes "deploy@$server" bash -s << EOF
set -e
cd "$APP_DIR"
git pull origin main
systemctl restart myapp
sleep 3
systemctl is-active --quiet myapp
EOF
}
for server in "${SERVERS[@]}"; do
echo "Deploying to $server..."
if deploy_to_server "$server"; then
echo "$server: OK"
else
echo "$server: FAILED" >&2
FAILED_SERVERS+=("$server")
fi
done
if [ "${#FAILED_SERVERS[@]}" -gt 0 ]; then
echo "Deployment failed on: ${FAILED_SERVERS[*]}" >&2
exit 1
fi
echo "Deployed successfully to all ${#SERVERS[@]} servers"
Project 5: AWS Resource Auditor
Combines: Bash + AWS CLI, arrays, text processing, production-grade structure.
#!/usr/bin/env bash
set -euo pipefail
REGION="${1:-us-east-1}"
echo "=== Unattached EBS Volumes (potential cost savings) ==="
aws ec2 describe-volumes --region "$REGION" --filters "Name=status,Values=available" \
--query 'Volumes[*].[VolumeId,Size,CreateTime]' --output table
echo "=== Untagged Running Instances ==="
aws ec2 describe-instances --region "$REGION" \
--filters "Name=instance-state-name,Values=running" \
--query "Reservations[].Instances[?!not_null(Tags[?Key=='Environment'])].[InstanceId,InstanceType]" \
--output table
echo "=== S3 Buckets Without Versioning Enabled ==="
for bucket in $(aws s3api list-buckets --query 'Buckets[*].Name' --output text); do
STATUS=$(aws s3api get-bucket-versioning --bucket "$bucket" --query 'Status' --output text 2>/dev/null || echo "None")
[ "$STATUS" != "Enabled" ] && echo "$bucket: $STATUS"
done
What Each Project Teaches
| Project | Core Skills Combined |
|---|---|
| Health Checker | Functions, conditions, exit codes |
| Log Analyzer | Text processing (awk/grep), alerting, thresholds |
| Backup Tool | File handling, tar, find, cron scheduling |
| Deploy Script | SSH automation, arrays, error tracking, heredocs |
| AWS Auditor | AWS CLI, JMESPath queries, loops over API results |
Production Considerations
- Each of these is a realistic starting point, not a finished tool — add
set -euo pipefail,trapcleanup, structured logging, and aDRY_RUNmode (per the Production-Grade Bash tutorial) before trusting any of them with real infrastructure. - Version-control these scripts alongside the infrastructure they manage, and run
shellcheckon them in CI just like application code. - Start simple, then layer in error handling and observability incrementally — a working, readable 30-line script beats an overengineered 300-line one that’s never actually tested.
Quick Interview Answer
“A strong portfolio project doesn’t need to be exotic — a server health checker, a log analyzer with alerting, an automated backup tool, a rolling multi-server deploy script, and an AWS resource auditor each combine 3-4 core Bash concepts (functions, error handling, text processing, SSH/API automation) into something genuinely useful. What makes them production-grade rather than a toy is
set -euo pipefail, proper cleanup viatrap, and structured logging — not more features.”
Common Mistakes
- Building an impressive-looking script that skips basic error handling (
set -euo pipefail) entirely. - Not testing failure paths (what happens when a server is unreachable, or
df/awsoutput is unexpected) — only the happy path. - Treating a “project” as a one-off script instead of something maintained, version-controlled, and reviewed like any other code.
Add More Questions to This Guide
Know a question that should be here? Share it and help the community!
Open Google Form