{"id":15716506,"url":"https://github.com/cloudymax/spot-prices","last_synced_at":"2026-02-28T22:31:41.794Z","repository":{"id":65293906,"uuid":"582980803","full_name":"cloudymax/spot-prices","owner":"cloudymax","description":"notes about querying spot pricing","archived":false,"fork":false,"pushed_at":"2024-03-22T06:52:17.000Z","size":137,"stargazers_count":3,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-01-14T14:42:38.602Z","etag":null,"topics":["aws","azure","bash","equinix","gcp","spot-instances","spot-pricing"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cloudymax.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-12-28T12:12:27.000Z","updated_at":"2025-04-15T16:56:39.000Z","dependencies_parsed_at":"2024-10-24T13:12:25.302Z","dependency_job_id":"83857b34-0cb2-4711-94ca-a396fb899f15","html_url":"https://github.com/cloudymax/spot-prices","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cloudymax/spot-prices","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudymax%2Fspot-prices","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudymax%2Fspot-prices/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudymax%2Fspot-prices/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudymax%2Fspot-prices/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cloudymax","download_url":"https://codeload.github.com/cloudymax/spot-prices/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cloudymax%2Fspot-prices/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29953282,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T18:42:55.706Z","status":"ssl_error","status_checked_at":"2026-02-28T18:42:48.811Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","azure","bash","equinix","gcp","spot-instances","spot-pricing"],"created_at":"2024-10-03T21:45:52.535Z","updated_at":"2026-02-28T22:31:41.696Z","avatar_url":"https://github.com/cloudymax.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Spot Pricing Notes\n\nThese are just my notes, check out https://cloudoptimizer.io/ for a real implimentation \n\n## Usage\n\nFind CPU by trait:\n```bash\n# Get the CPU with the fastest single-threaded performance\nyq '.processors.intel |sort_by(.cpumarkSingleThread)' instances.yaml |yq '.[-1]'\n```\nOutput:\n```yaml\ncpu_name: Xeon E-2378G\nslug: 2378G\nrelease_date: 2021\ncpu_cores: 8\ncpu_threads: 16\nbaseClock: 2800\nturboClock: 5100\ntdp: 80w\nmemory: DDR4\ncpumarkSingleThread: 3477\ncpumarkMultiThread: 22755\n```\nJust get the name:\n```\nyq '.processors.intel |sort_by(.cpumarkSingleThread)' instances.yaml |yq '.[-1].cpu_name'\n\u003e Xeon E-2378G\n```\n\nFind instance by CPU:\n```bash\n# get all instances using that CPU modle\n\u003e yq '.vendors.*.*.[] | select(.cpu == \"Xeon E-2378G\")' instances.yaml\n```\n\nOutput:\n```yaml\ninstance_name: m3.small.x86\ncpu: Xeon E-2378G\nnumCpus: 1\ninstance_vCores: 16\nram: 64GB\ndiskSize:\n  - 480GB\nnumDisks: 2\nprice: 0.11\n```\n\n## AWS\n\nGetting AWS prices is actuall pretty straight-forward, each metadata filed is queryable in an intuitive way.\n\nResources:\n\n- [Spot Prices](https://aws.amazon.com/ec2/spot/pricing/)\n- [Spot Advisor](https://aws.amazon.com/ec2/spot/instance-advisor/)\n- [Instance Types](https://aws.amazon.com/ec2/instance-types/)\n\nPreferred Instances:\n\n```bash\necho \"Name,vCores,Memory,Drives,Price\" \u003e table.csv\nINSTANCE_TYPE_LIST=(\"m6i.large\" \"m6i.2xlarge\" \"m6i.4xlarge\")\n\nfor i in \"${INSTANCE_TYPE_LIST[@]}\"\ndo\n  INSTANCE_TYPE=\"m6i.large\"\n  ZONE=\"eu-central-1a\"\n  INSTANCES=\"m6i.large\"\n  START=$(date +%Y-%m-%dT00:00:00)\n  END=$(date +%Y-%m-%dT%H:%M:%S)\n\n  VCORES=$(aws ec2 describe-instance-types \\\n  --instance-types $INSTANCE_TYPE |grep \"VCPUINFO\" |awk '{print $4}')\n\n  MEMORY=$(aws ec2 describe-instance-types \\\n  --instance-types $INSTANCE_TYPE |grep \"MEMORYINFO\" |awk '{print $2}')\n\n  DISK=$(aws ec2 describe-instance-types \\\n  --instance-types $INSTANCE_TYPE |grep \"SUPPORTEDROOTDEVICETYPES\" |awk '{print $2}')\n\n  PRICE=$(aws ec2 describe-spot-price-history \\\n  --availability-zone $ZONE \\\n  --instance-types $INSTANCES \\\n  --product-description \"Linux/UNIX\" \\\n  --start-time $START \\\n  --end-time $END |awk '{print $5}')\n\n echo \"$INSTANCE_TYPE,$VCORES,$MEMORY,$DISK,$PRICE\" \u003e\u003e table.csv\ndone\n\nrich table.csv\n```\n\n\n### How to Get prices:\n```bash\nZONE=\"eu-central-1a\"\nINSTANCES=\"m6i.large\"\nSTART=$(date +%Y-%m-%dT00:00:00)\nEND=$(date +%Y-%m-%dT%H:%M:%S)\n\naws ec2 describe-spot-price-history \\\n--availability-zone $ZONE \\\n--instance-types $INSTANCES \\\n--product-description \"Linux/UNIX\" \\\n--start-time $START \\\n--end-time $END \\\n--output table\n```\n\n## Azure\n\nAzure is also pretty straight-forward but you will need to do some filtering on the query results to get the data you need. There are however some large potential issues you need to plan around.\n\n- westeurope = Netherlands\n- northeurope = Ireland\n\n1. Gen1 vs Gen2 VMs.\n\n  - Azure has 2 hypervsirs they use. Gen1 which is based on legacy BIOS, and Gen2 which is based on UEFI. Many VM families only support one or the other, though some support both. You will need to check which is required by the VM family you want to use. See [HERE](https://learn.microsoft.com/en-us/azure/virtual-machines/generation-2)\n  \n2. Availability\n\n  - Not every Azure datacenter has every type of machine. You will need to check if the machine you want is availbe in the datacenter you will be using.\n  \n    ```bash\n    az vm list-skus --location \"westeurope\" \\\n      --size Standard_N \\\n      --output table\n    ```\n\n3. Quotas\n\n  - Azure uses resource quotas just liek all the other major clouds. These may be too low for you to create certain types of virtual machines, GPUs, Spot instances, or Low-Priority VMs. You can request quota changes via the portal [HERE](https://portal.azure.com/#view/Microsoft_Azure_Capacity/QuotaMenuBlade/~/overview).\n\nMore Resources:\n\n- [Spot Prices](https://azure.microsoft.com/en-us/pricing/spot-advisor/)\n- [Instance Types](https://learn.microsoft.com/en-us/azure/virtual-machines/sizes-general)\n- Nested virtualization is NOT supported on ANY of Azure's GPU VMs.\n\n## GPU VM Types\n\n| VM Name | CPU Name | vCores | RAM | GPU Name | GPUs | vRAM | Monthly Spot |\n| ---  | --- | ---    | --- | --- | ---  | ---  | --- |\n|Standard_NC6 | Xeon E5-2690 v3 | 6 | 56 | Tesla K80 | 1 | 12 | 80.75 |\n|Standard_NC6s_v2 | Xeon E5-2690 v4 | 6 | 112 | Tesla P100 | 1 | 16 | 185.73 |\n|Standard_NC6s_v3 | Xeon E5-2690 v4 | 6 | 112 | Tesla V100 | 1 | 12 | 1,251.83 |\n|Standard_NC4as_T4_v3 | AMD EPYC 7V12(Rome) | 4 | 28 | Tesla T4 | 1 | 16 | 229.25 |\n|Standard_ND6s | Xeon E5-2690 v4  | 6 | 112 | Tesla P40 | 1 | 24 | 286.82 |\n|Standard_NV6 | Xeon E5-2690 v3 | 6 | 56 | Tesla M60 | 1/2 | 8 | 94.53 |\n|Standard_NV12 | Xeon E5-2690 v3 | 6 | 56 | Tesla M60 | 1 | 16 | 189.05 |\n|Standard_NV12s_v3 | Xeon E5-2690 v4 | 12| 112 | Tesla M60 | 0.5 | 8 | 98.75 |\n|Standard_NV24s_v3 | Xeon E5-2690 v4 | 12| 112 | Tesla M60 | 1 | 16 | 197.36 |\n|Standard_NV4as_v4 | AMD EPYC 7V12(Rome) | 4 | 14 | Radeon MI25 | 1/8 | 2 | 20.15 |\n|Standard_NV32as_v4 | AMD EPYC 7V12(Rome) | 32 | 112 | Radeon MI25 | 1 | 16 | 161.35 |\n|Standard_NV6ads_A10_v5 | AMD EPYC 74F3V(Milan) | 6 | 55 | Nvidia A10 | 1/6 | 4 | 163.43 |\n|Standard_NV36ads_A10_v5 | AMD EPYC 74F3V(Milan) | 36 | 440 | Nvidia A10 | 1 | 24 | 1152.32 |\n\n## Get current prices witha gross one-liner:\n\n```bash\nexport ARM_SKU=\"Standard_NV36ads_A10_v5\"\nexport LOCATION=\"'EU West'\"\nexport METER_NAME=$(echo \"'$ARM_SKU Spot'\"| sed 's/Standard_//g' |sed 's/_/ /g')\nexport ARM_SKU_NAME=$(echo \"'$ARM_SKU'\")\n\ndocker run --platform linux/amd64 \\\n-e API_URL=\"https://prices.azure.com/api/retail/prices\" \\\n-e LOCATION=\"$LOCATION\"  \\\n-e SERVICE_NAME=\"'Virtual Machines'\" \\\n-e SERVICE_FAMILY=\"'Compute'\" \\\n-e METER_NAME=\"$METER_NAME\" \\\n-e ARM_SKU_NAME=\"$ARM_SKU_NAME\" \\\n-e ARM_SKU=\"$ARM_SKU\" \\\n-e ARM_CLIENT_ID=$(bw get item admin-robot |jq -r '.fields[] |select(.name==\"clientId\") |.value') \\\n-e ARM_CLIENT_SECRET=$(bw get item admin-robot |jq -r '.fields[] |select(.name==\"clientSecret\") |.value') \\\n-e ARM_TENANT_ID=$(bw get item admin-robot |jq -r '.fields[] |select(.name==\"tenantId\") |.value') \\\nmcr.microsoft.com/azure-cli:2.9.1 sh -c 'az login --service-principal \\\n--username \"$ARM_CLIENT_ID\" \\\n--password \"$ARM_CLIENT_SECRET\" \\\n--tenant \"$ARM_TENANT_ID\" 2\u003e\u00261 \u003e /dev/null \u0026\u0026 \\\necho \"az rest --method get --uri \\\"${API_URL}?\\\\\\$filter=\\\nlocation eq $LOCATION and \\\nserviceName eq $SERVICE_NAME and \\\nserviceFamily eq $SERVICE_FAMILY and \\\narmSkuName eq $ARM_SKU_NAME and \\\nmeterName eq $METER_NAME\"\\\" \\\n--query \\\"[Items][0][*].{name:productName, sku:armSkuName, location:location, hourly_price:retailPrice, hourly_price:retailPrice, currency:currencyCode, type:type}\\\" \\\n-o json |sh' |jq\n```\n\n## Equinix Spot Metal\n\nEquinix isnt a cloud-provider so much as a colocation service with a good set of APIs. They dont have a fancy cli so you just query an API endpoint and get back a json file that basically already in the format we want anyway.\n\n- locations: https://www.equinix.se/data-centers\n\nResources\n- [Metros](https://metal.equinix.com/developers/docs/locations/metros/)\n- [Instance types](https://metal.equinix.com/product/servers/)\n\n### How to Get Prices with a gross one-liner\n\n```bash\nTOKEN=$(bw get notes equinix-api-token) \u0026\u0026 \\\nURL=\"https://api.equinix.com/metal/v1/market/spot/prices/metros\" \u0026\u0026 \\\nMETRO=\"am\" \u0026\u0026 \\\nPRICES=$(curl -X GET -H \"X-Auth-Token: $TOKEN\" $URL -d \"metro=$METRO\" | \\\npython3 -c \"import sys, json; print(json.load(sys.stdin)['spot_market_prices']['am'])\"| sed \"s/'/\\\"/g\") \u0026\u0026 \\\necho $PRICES |jq\n```\n\n## Google Cloud Platform (out of date, api now on v2beta)\n\n- europe-west1 = Belgium\n- europe-west4 = Netherlands\n\nGoogle is the worst when it comes to transparancy around what exact CPU you will get when you request a VM from them.\nUNless you want the NEWEST (A2 - Cascade lake), you could get ANY cpu from a mix of old to really old CPUs.\nThe fact that the only GPU capable SKU's are the N1 (random cpu) or A2 (cascade lake) means I can't really give an accurate CPU model or date.\n\n- https://www.densify.com/articles/google-compute-engine-machine-types\n\nGCP is also frustrating because we have to put in a LOT of boilerplate to get the data we want. Since GCP doesnt use machine families the same way others do, we have to get the individual prices of the CPU type, RAM amount, and GPU type that we will use in the VM. Once we have all these data-points we can create a final price.\n\n### How to get Prices\n\n1. have an existing project (an organzation alone isnt enough)\n2. Enable the Cloud Billing API: https://console.cloud.google.com/flows/enableapi?apiid=cloudbilling.googleapis.com\n3. Create an API key: https://cloud.google.com/docs/authentication/api-keys\n\nCreate an API key using the gcloud CLI\n\n```bash\ngcloud alpha services api-keys create --display-name=SOME_NAME\n```\n\nGet price per CPU core\n\n```bash\n\n# JQ explanation\n# 1. set the REGION arg to an array represented as a string - required to check multiple regions\n# 2. filter results for items in the desired regions. uses 'index' because we need to find an item in a nested array\n# 3. filter for items priced by the hour 'h'. This removes reserved instances from results\n# 4. filter based on the `resourceGroups` filed which is poorly named. It's closer to `family` or `machine type` from other cloud providers.\n# 5. filter usage types for Preemptable only, you could also filter for OnDemand type instances.\n# 6. Check if the description contains the GPU type we want. This data is oddly not in its own filed anywhere.\n\ncurl https://cloudbilling.googleapis.com/v1/services/6F81-5844-456A/skus?key=$(bw get notes \"GCP API key\") \u003e skus_compute_engine.json\n\nexport FAMILY=\"N1Standard\" # or `CPU`\nexport REGION='\"europe-west1\", \"europe-west4\", \"europe-central2\"'\nexport CORES=\"16\"\nexport CPU_TIER=\"N1\" # or A2 \n\nDATA=$(cat skus_compute_engine.json \\\n  | jq -r --arg REGION \"$REGION\" '.skus[] \n  | select((.serviceRegions | index( '\"$REGION\"' )) \n  and select(.pricingInfo[0].pricingExpression.usageUnit==\"h\") \n  and .category.resourceGroup==env.FAMILY \n  and .category.usageType==\"Preemptible\" \n  and select(.description | contains( env.CPU_TIER )))')\n\nexport NANOS=$(echo $DATA |jq '.pricingInfo[0].pricingExpression.tieredRates[0].unitPrice.nanos')\nCONVERTED_RATE=$(bc \u003c\u003c\u003c \"scale=5; $NANOS/1000000000\")\nCPU_PRICE=$( bc \u003c\u003c\u003c \"scale=5; $CONVERTED_RATE * $CORES\" )\necho \"CPU Price: $CPU_PRICE\"\n```\n\nGet RAM price:\n\n```bash\nexport FAMILY=\"N1Standard\" # or `RAM`\nexport REGION='\"europe-west1\", \"europe-west4\"'\nexport RAM_AMOUNT=\"64\"\nexport CPU_TIER=\"N1\"\n\nDATA=$(cat skus_compute_engine.json \\\n  | jq -r --arg REGION \"$REGION\" '.skus[] \n  | select((.serviceRegions | index( '\"$REGION\"' )) \n  and select(.pricingInfo[0].pricingExpression.usageUnit==\"GiBy.h\") \n  and .category.resourceGroup==env.FAMILY \n  and .category.usageType==\"Preemptible\" \n  and select(.description | contains( env.CPU_TIER )))')\n\nexport NANOS=$(echo $DATA |jq '.pricingInfo[0].pricingExpression.tieredRates[0].unitPrice.nanos')\nCONVERTED_RATE=$(bc \u003c\u003c\u003c \"scale=5; $NANOS/1000000000\")\nRAM_PRICE=$( bc \u003c\u003c\u003c \"scale=5; $CONVERTED_RATE * $RAM_AMOUNT\" )\necho \"RAM Price: $RAM_PRICE\"\n```\n\nGet price per GPU\n\n```bash\n# JQ explanation\n# 1. set the REGION arg to an array represented as a string - required to check multiple regions\n# 2. filter results for items in the desired regions. uses 'index' because we need to find an item in a nested array\n# 3. filter for items priced by the hour 'h'. This removes reserved instances from results\n# 4. filter based on the `resourceGroupz filed which is poorly named. It's closer to 'family' or 'machine type' from other cloud providers.\n# 5. filter usage types for Preemptable only, you could also filter for OnDemand type instances.\n# 6. Check if the description contains the GPU type we want. This data is oddly not in its own filed anywhere.\n\nGPU Types: T4, P4, A100, P100\n\nexport FAMILY=\"GPU\"\nexport GPU_TYPE=\"T4\"\nexport GPUS=1\nexport REGION='\"europe-west1\", \"europe-west4\"'\n\nDATA=$(cat skus_compute_engine.json \\\n  | jq -r --arg REGION \"$REGION\" '.skus[] \n  | select((.serviceRegions | index( '\"$REGION\"' )) \n  and select(.pricingInfo[0].pricingExpression.usageUnit==\"h\") \n  and .category.resourceGroup==env.FAMILY \n  and .category.usageType==\"Preemptible\" \n  and select(.description | contains( env.GPU_TYPE )))')\n\n\nexport NANOS=$(echo $DATA |jq '.pricingInfo[0].pricingExpression.tieredRates[0].unitPrice.nanos')\n\nCONVERTED_RATE=$(bc \u003c\u003c\u003c \"scale=5; $NANOS/1000000000\")\n\nGPU_PRICE=$( bc \u003c\u003c\u003c \"scale=5; $CONVERTED_RATE * $GPUS\" )\n\necho \"GPU Price: $GPU_PRICE\"\n```\n\nCombine prices\n\n```bash\nCOMBINED_PRICE=$(bc \u003c\u003c\u003c \"scale=5; $CPU_PRICE + $RAM_PRICE + $GPU_PRICE\")\necho \"Combined Price: $COMBINED_PRICE\"\n```\n\n\n## Chart Rough Draft\n\n|Vendor |Name           |CPU Name                 |CPU Date|vCPU|MEM|DISK |Price/hr|\n|-------|---------------|-------------------------|--------|----|---|-----|--------|\n|GCP    |c2d-highcpu-2  |Zen3 AMD EPYC Milan      |Q1-2021 |2   |4  |???  |$0.0198 |\n|GCP    |c2d-highcpu-4  |Zen3 AMD EPYC Milan      |Q1-2021 |4   |8  |???  |$0.0396 |\n|Azure  |Standard_D4d_v4|Xeon Platinum 8272CL     |Q2-2019 |4   |16 |150  |$0.04   |\n|GCP    |c2d-standard-4 |Zen3 AMD EPYC Milan      |Q1-2021 |4   |16 |???  |$0.04796|\n|GCP    |c2-standard-4  |3.9 GHz Cascade Lake Xeon|Q1-2020 |4   |16 |???  |$0.0557 |\n|GCP    |c2d-highcpu-8  |Zen3 AMD EPYC Milan      |Q1-2021 |8   |16 |???  |$0.0792 |\n|Azure  |Standard_D8d_v4|Xeon Platinum 8272CL     |Q2-2019 |8   |32 |300  |$0.08   |\n|Equinix|m3.small.x86   |Rocket Lake Xeon E-2378G |Q3-2021 |16  |64 |960  |$0.11   |\n|AWS    |M6i.large      |Xeon Ice Lake 8375C      |Q2-2020 |2   |8  |???  |$0.113  |\n|AWS    |M6a.large      |AMD EPYC 7R13 Zen3       |Q1-2021 |2   |8  |???  |$0.113  |\n|GCP    |c2-standard-8  |3.9 GHz Cascade Lake Xeon|Q1-2020 |8   |32 |???  |$0.1114 |\n|AWS    |M6i.xlarge     |Xeon Ice Lake 8375C      |Q2-2020 |4   |16 |???  |$0.2259 |\n|AWS    |M6a.xlarge     |AMD EPYC 7R13 Zen3       |Q1-2021 |4   |16 |???  |$0.2259 |\n|Equinix|n3.xlarge.x86  |Xeon Gold 6314U Ice Lake |Q2-2021 |64  |512|7.6TB|$0.45   |\n|AWS    |M6i.2xlarge    |Xeon Ice Lake 8375C      |Q2-2020 |8   |32 |???  |$0.4518 |\n|AWS    |M6a.2xlarge    |AMD EPYC 7R13 Zen3       |Q1-2021 |8   |32 |???  |$0.4518 |\n|Equinix|a3.xlarge.x86  |2 x Xeon Gold 6338       |Q2-2021 |128 |1TB|480GB|$0.75   |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudymax%2Fspot-prices","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcloudymax%2Fspot-prices","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcloudymax%2Fspot-prices/lists"}