- Posted on
- • Artificial Intelligence
Artificial Intelligence Cloud Cost Optimisation
- Author
-
-
- User
- linuxbash
- Posts by this author
- Posts by this author
-
Artificial Intelligence Cloud Cost Optimisation: A Bash-First Playbook
If your AI cloud bill can double overnight, you’re not alone. GPU instances are expensive, storage grows without bound, and “just one more experiment” can burn thousands. The good news: you can take back control with simple, automation-friendly Bash tactics that deliver fast, measurable savings—without slowing your model velocity.
This article explains why AI cloud cost optimisation matters, then gives you 3–5 actionable, real-world steps (with Bash you can copy/paste) to cut waste, right-size compute, and make preemptible capacity practical. Installation instructions are provided for apt, dnf, and zypper wherever tools are cited.
Why this is worth your time
AI workloads are bursty and hardware-sensitive. Choosing the wrong GPU size or not switching instance types at the right time can double costs for the same result.
Idle time is the silent budget killer. Even a few hours/day of idle GPUs can add up to thousands per month.
Preemptible/spot instances are cheap but intimidating. With checkpointing and simple signal handling, they become reliable workhorses.
Storage and egress can quietly exceed compute costs. Object storage, lifecycle policies, and smart syncing tame this.
Bash is everywhere, auditable, and easy to automate via cron or CI. Let’s put it to work.
Prerequisites: install the core tools
We’ll use standard Linux tools and optional cloud CLIs. Install what you need for your distro.
- Debian/Ubuntu (apt):
sudo apt update
sudo apt install -y jq bc sysstat rclone awscli
sudo systemctl enable --now sysstat
- Fedora/RHEL/CentOS Stream (dnf):
sudo dnf install -y jq bc sysstat rclone awscli
sudo systemctl enable --now sysstat
- openSUSE (zypper):
sudo zypper refresh
sudo zypper install -y jq bc sysstat rclone aws-cli
sudo systemctl enable --now sysstat
Notes:
nvidia-smi comes with the NVIDIA driver; install your vendor’s driver if you need GPU metrics.
rclone requires a one-time
rclone configto connect to S3/Google Cloud Storage/Azure Blob.awscli requires credentials and IAM permissions; for cost APIs, Cost Explorer must be enabled in the console.
1) Profile first, then right-size your AI compute
Before switching instance types or GPUs, measure real usage. If your GPU sits <40% utilisation during training or inference, you’re overpaying.
- Watch GPU usage live:
watch -n 5 'nvidia-smi --query-gpu=name,utilization.gpu,memory.used --format=csv'
- Quick CPU utilisation check:
sar -u 1 5
- Script: average GPU utilisation over a window (across all GPUs)
sudo tee /usr/local/bin/gpu-avg.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
dur="${1:-60}"
if ! command -v nvidia-smi >/dev/null 2>&1; then
echo "nvidia-smi not found; GPU metrics unavailable" >&2
exit 1
fi
end=$((SECONDS+dur))
sum=0; n=0
while [ $SECONDS -lt $end ]; do
inst_avg=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null \
| awk '{s+=$1; c+=1} END{if(c>0) print int(s/c); else print 0}')
sum=$((sum+inst_avg)); n=$((n+1))
sleep 1
done
echo $((sum/n))
EOF
sudo chmod +x /usr/local/bin/gpu-avg.sh
- Sanity-check effective cost at your current utilisation:
COST_PER_HOUR="2.50" # edit: instance $/hour
UTIL=$(gpu-avg.sh 60) # average over 60s
if [ "$UTIL" -eq 0 ]; then
echo "GPU util 0%; effective $/GPU-hr: infinite (all waste)"
else
echo "Avg util: ${UTIL}%"
echo -n "Effective $/100%-util GPU-hr: "
echo "scale=2; $COST_PER_HOUR*100/$UTIL" | bc
fi
If that “effective cost” is eye-watering, try:
Smaller/cheaper GPUs (e.g., L4/L40s/T4 for lighter inference).
Mixed precision, flash attention, or kernel-optimised inference servers to raise utilisation.
CPU offload and dataloader tuning to relieve GPU stalls.
2) Make spot/preemptible compute safe with checkpointing
Preemptible capacity can be 50–90% cheaper, but you must handle termination. Two simple practices make it robust:
Save checkpoints frequently (and atomically) to object storage.
Trap SIGTERM and respond to preemption notices.
Example wrapper that traps termination and syncs checkpoints with rclone:
cat >/tmp/train_with_checkpoint.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
CHECKPOINT_DIR="${CHECKPOINT_DIR:-/workspace/checkpoints}"
REMOTE="${REMOTE:-s3:ai-checkpoints/my-run}" # requires 'rclone config'
checkpoint() {
echo "[checkpoint] $(date -Is) syncing $CHECKPOINT_DIR -> $REMOTE"
rclone sync --fast-list --checksum "$CHECKPOINT_DIR" "$REMOTE"
}
trap 'echo "[signal] TERM received"; checkpoint; exit 143' TERM INT
# Optional: AWS Spot two-minute interruption notice
( while sleep 5; do
if curl -fsS --max-time 1 http://169.254.169.254/latest/meta-data/spot/instance-action >/dev/null; then
echo "[preemption] notice received"
checkpoint
sleep 60
exit 0
fi
done ) &
# Your actual training command
python train.py --save-dir "$CHECKPOINT_DIR"
EOF
bash /tmp/train_with_checkpoint.sh
Tips:
Use smaller, frequent checkpoints (e.g., every N steps/minutes).
Store immutable artifacts (datasets, pretrained weights) in shared object storage to make rescheduling trivial.
3) Auto-stop idle GPUs with a lightweight cron job
Kill waste automatically. This script stops an EC2 instance when both GPU and CPU are idle for a sustained period. If no cloud CLI is configured, it falls back to powering off the machine.
sudo tee /usr/local/bin/ai-idle-stop.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
GPU_THRESH="${GPU_THRESH:-10}" # % GPU util below which we consider idle
CPU_IDLE_THRESH="${CPU_IDLE_THRESH:-80}" # % CPU idle above which we consider idle
IDLE_WINDOW="${IDLE_WINDOW:-900}" # seconds required idle (default 15m)
STATE_FILE="/var/run/ai-idle.state"
gpu_idle=true
if command -v nvidia-smi >/dev/null 2>&1; then
util=$(nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits 2>/dev/null \
| awk '{s+=$1; c+=1} END{if(c>0) print int(s/c); else print 0}')
[ "${util:-0}" -lt "$GPU_THRESH" ] || gpu_idle=false
fi
# CPU idle using sysstat (mpstat)
cpu_idle=100
if command -v mpstat >/dev/null 2>&1; then
cpu_idle=$(LC_ALL=C mpstat 1 3 | awk '/Average.*all/ {print $NF+0}')
fi
cpu_ok=false
awk -v idle="$cpu_idle" -v thr="$CPU_IDLE_THRESH" 'BEGIN{exit !(idle>=thr)}' && cpu_ok=true
now=$(date +%s)
if $gpu_idle && $cpu_ok; then
if [ -e "$STATE_FILE" ]; then
first=$(cat "$STATE_FILE")
else
first=$now
fi
echo "$first" > "$STATE_FILE"
if [ $((now-first)) -ge "$IDLE_WINDOW" ]; then
echo "$(date -Is) idle window met; attempting to stop instance"
if command -v aws >/dev/null 2>&1 && curl -fsS --max-time 1 http://169.254.169.254/latest/meta-data/instance-id >/dev/null; then
IID=$(curl -fsS http://169.254.169.254/latest/meta-data/instance-id)
REGION=$(curl -fsS http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)
aws ec2 stop-instances --instance-ids "$IID" --region "$REGION" >/dev/null 2>&1 || true
echo "$(date -Is) sent stop to EC2 instance $IID in $REGION"
else
echo "$(date -Is) powering off locally"
systemctl poweroff || shutdown -h now
fi
else
echo "$(date -Is) idle but window not met yet: $((now-first))/$IDLE_WINDOW s"
fi
else
echo "$now" > "$STATE_FILE" # reset window
echo "$(date -Is) busy: gpu_idle=$gpu_idle cpu_idle=${cpu_idle}%"
fi
EOF
sudo chmod +x /usr/local/bin/ai-idle-stop.sh
Schedule it every 5 minutes as root:
sudo crontab -e
# add:
*/5 * * * * /usr/local/bin/ai-idle-stop.sh >> /var/log/ai-idle-stop.log 2>&1
Environment knobs (set in crontab if desired):
GPU_THRESH=15 CPU_IDLE_THRESH=85 IDLE_WINDOW=1200 /usr/local/bin/ai-idle-stop.sh
4) Optimise storage and data movement
Keep large, cold data in object storage (S3/GCS/Azure Blob); stage hot shards locally.
Sync checkpoints/logs out quickly to avoid losing work; prune aggressively.
Examples with rclone:
# One-time setup
rclone config
# Push checkpoints and logs (fast, checksum-aware)
rclone sync --fast-list --checksum /workspace/checkpoints s3:ai-checkpoints/run-123
rclone copy --fast-list /workspace/logs s3:ai-logs/run-123
# Periodic prune of old local artifacts (keep last 5)
ls -1dt /workspace/checkpoints/* | tail -n +6 | xargs -r rm -rf
Consider object storage lifecycle policies (transition to infrequent access or cold tiers after N days, expire after M days). This alone can cut storage bills dramatically for long-running experiments.
5) Tag and measure spend from the shell
Tag everything (instances, buckets, jobs) so you can slice spend by project or team. Then query costs programmatically.
- Tag current EC2 instance with your project:
IID=$(curl -fsS http://169.254.169.254/latest/meta-data/instance-id)
REGION=$(curl -fsS http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region)
aws ec2 create-tags --resources "$IID" --region "$REGION" \
--tags Key=Project,Value=nlp-finetune Key=Owner,Value=$USER
- Quick Cost Explorer query by tag (last 7 days):
START=$(date -u -d '7 days ago' +%Y-%m-%d)
END=$(date -u +%Y-%m-%d)
aws ce get-cost-and-usage --region us-east-1 \
--time-period Start=$START,End=$END \
--granularity DAILY \
--metrics UnblendedCost \
--group-by Type=TAG,Key=Project \
| jq '.ResultsByTime[] | {date: .TimePeriod.Start, groups: .Groups}'
Use these reports to spot runaway projects, underutilised fleets, and to validate that your automation is paying off.
A quick, real-world-style workflow
Profiling shows your fine-tune job averages 45% GPU util on a large GPU.
Switch to a cheaper GPU type that still fits your model, enable mixed precision, and increase dataloader workers; utilisation climbs to ~75%.
Wrap training with the checkpoint script and move to spot/preemptible pools; interruptions no longer hurt.
Enable the idle-stop cron: after jobs finish, instances self-stop within 15 minutes.
Push logs/checkpoints to object storage with rclone; set a 30-day lifecycle to cold storage and 90-day expiration.
Tag everything; validate spend trends via the CLI each week.
Result: lower effective $/GPU-hr, fewer idle hours, and less storage waste—without sacrificing iteration speed.
Conclusion and next steps (CTA)
You don’t need a massive platform overhaul to slash AI cloud costs. Start with the 80/20:
Measure utilisation, then right-size.
Make spot/preemptible safe with checkpoints.
Auto-stop idle compute.
Sync and prune storage.
Tag and measure spend continuously.
Copy the scripts above, set up the cron job, and run a one-week experiment. Track your savings with the cost query. When you’re ready, expand to job schedulers, heterogeneous fleets, and deeper model-level optimisations.
If you want a follow-up post with provider-specific recipes (AWS/GCP/Azure) or container-native versions of these scripts, say the word.