Skip to content

Instantly share code, notes, and snippets.

@VincentBean
Created November 7, 2025 14:40
Show Gist options
  • Select an option

  • Save VincentBean/8ba565047cd21ebbeaa3303280f4fb72 to your computer and use it in GitHub Desktop.

Select an option

Save VincentBean/8ba565047cd21ebbeaa3303280f4fb72 to your computer and use it in GitHub Desktop.
A script to find cheaper Hetzner servers compared to what you have
#!/bin/bash
#
# Hetzner Cloud Server Info & Cost Optimizer
#
# Description:
# Fetches all servers from your Hetzner Cloud account and analyzes their pricing.
# For each server, it identifies cheaper server types with equal or better specs.
# Results are exported to a tab-separated file (servers.txt) with cost savings.
#
# Setup:
# 1. Create a .env file in the same directory with your Hetzner API token:
# HETZNER_API_TOKEN=your_token_here
# 2. Ensure jq and curl are installed
# 3. Make the script executable: chmod +x get_server_info.sh
#
# Usage:
# ./get_server_info.sh
#
# Output file (servers.txt) contains columns:
# - Server name, type, IP, cores, RAM, disk, current price
# - Cheaper alternative type, price, and potential savings (if available)
#
if [ -f ".env" ]; then
source .env
else
echo "Error: .env file not found"
exit 1
fi
if [ -z "$HETZNER_API_TOKEN" ]; then
echo "Error: HETZNER_API_TOKEN not set in .env file"
exit 1
fi
OUTPUT_FILE="servers.txt"
> "$OUTPUT_FILE"
echo "Fetching servers from Hetzner Cloud API..."
echo "Fetching available server types..."
server_types_response=$(curl -s -H "Authorization: Bearer $HETZNER_API_TOKEN" "https://api.hetzner.cloud/v1/server_types")
all_servers=""
for page in 1 2 3; do # very basic pagination, adjust as needed
echo "Fetching servers page $page..."
api_response=$(curl -s -H "Authorization: Bearer $HETZNER_API_TOKEN" "https://api.hetzner.cloud/v1/servers?page=$page&per_page=50")
if [ $? -ne 0 ]; then
echo "Error: Failed to fetch servers from Hetzner API (page $page)"
exit 1
fi
if [ -z "$all_servers" ]; then
all_servers="$api_response"
else
all_servers=$(echo "$all_servers" | jq --argjson new "$api_response" '.servers += $new.servers')
fi
done
echo "$all_servers" | jq -r '.servers[] |
[
.name,
.server_type.name,
.public_net.ipv4.ip,
(.server_type.cores | tostring),
(.server_type.memory | tostring),
(.server_type.disk | tostring),
(.server_type.prices[0].price_monthly.gross)
] | @tsv' | while IFS=$'\t' read -r name server_type ip cores ram_gb disk price; do
disk_gb="$disk"
price_formatted=$(printf "%.2f" "$price" | sed 's/\./,/')
cheaper_option=$(echo "$server_types_response" | jq -r --arg cores "$cores" --arg ram "$ram_gb" --arg disk "$disk" --arg current_price "$price" '
[.server_types[] |
select(
(.cores >= ($cores | tonumber)) and
(.memory >= ($ram | tonumber)) and
(.disk >= ($disk | tonumber)) and
(.prices[0].price_monthly.gross | tonumber) < ($current_price | tonumber)
) |
{name: .name, price: (.prices[0].price_monthly.gross | tonumber)}] |
sort_by(.price) |
.[0] // empty |
[.name, (.price | tostring)] |
@tsv
')
if [ -n "$cheaper_option" ]; then
cheaper_type=$(echo "$cheaper_option" | cut -f1)
cheaper_price=$(echo "$cheaper_option" | cut -f2)
cheaper_price_formatted=$(printf "%.2f" "$cheaper_price" | sed 's/\./,/')
savings=$(echo "$price - $cheaper_price" | bc)
savings_formatted=$(printf "%.2f" "$savings" | sed 's/\./,/')
echo "Processing: $name ($server_type) - Cheaper option available: $cheaper_type"
printf "%s\t%s\t%s\t%s\t%sGB\t%sGB\t%s\t%s\t%s\t%s\n" "$name" "$server_type" "$ip" "$cores" "$ram_gb" "$disk_gb" "$price_formatted" "$cheaper_type" "$cheaper_price_formatted" "$savings_formatted" >> "$OUTPUT_FILE"
else
echo "Processing: $name ($server_type) - Best price"
printf "%s\t%s\t%s\t%s\t%sGB\t%sGB\t%s\t-\t-\t-\n" "$name" "$server_type" "$ip" "$cores" "$ram_gb" "$disk_gb" "$price_formatted" >> "$OUTPUT_FILE"
fi
done
echo ""
echo "Done! Results written to $OUTPUT_FILE"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment