<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Guo</title>
    <description>The latest articles on DEV Community by Guo (@devguoo).</description>
    <link>https://dev.to/devguoo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3793357%2Fa2da7de4-d0cb-445c-ae71-80e23716752d.png</url>
      <title>DEV Community: Guo</title>
      <link>https://dev.to/devguoo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devguoo"/>
    <language>en</language>
    <item>
      <title>I Built a One-Command Speed Test for All 13 BandwagonHost Data Centers</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Sat, 04 Apr 2026 01:59:11 +0000</pubDate>
      <link>https://dev.to/devguoo/i-built-a-one-command-speed-test-for-all-13-bandwagonhost-data-centers-1k58</link>
      <guid>https://dev.to/devguoo/i-built-a-one-command-speed-test-for-all-13-bandwagonhost-data-centers-1k58</guid>
      <description>&lt;p&gt;If you've ever used &lt;a href="https://bandwagonhost.com" rel="noopener noreferrer"&gt;BandwagonHost&lt;/a&gt; (搬瓦工), you know the paradox of choice: 13 data centers spread across Los Angeles, Hong Kong, Tokyo, Osaka, Dubai, and the US East Coast. Which one is actually fastest from &lt;em&gt;your&lt;/em&gt; network?&lt;/p&gt;

&lt;p&gt;I got tired of manually pinging test IPs one by one, so I wrote a bash script that tests all 13 in about 30 seconds. Here's the tool and what I learned building it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The One-Liner
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/devguoo/bwg-speed-test/main/test.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No package to install, no Python, no Node. It runs on any Linux server, macOS, or WSL — anywhere you have &lt;code&gt;bash&lt;/code&gt; and &lt;code&gt;ping&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Want more detail? Add &lt;code&gt;-v&lt;/code&gt; for verbose mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/devguoo/bwg-speed-test/main/test.sh | bash &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What the Output Looks Like
&lt;/h2&gt;

&lt;p&gt;The script pings each datacenter (4 ICMP packets each), then sorts and color-codes the results:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  📊 Results (sorted by latency, lowest first)
  ─────────────────────────────────────────────────────────

  Datacenter                    Location            Latency  Status
  ──────────────────────────    ────────────────    ────────  ──────────────
  Hong Kong CN2 GIA             Hong Kong           42.5 ms  🏆 Fastest!
  Dubai                         Dubai, UAE          65.3 ms  ● Great
  Japan Osaka Softbank          Osaka, Japan       102.8 ms  ● Good
  DC6 CN2 GIA-E                 Los Angeles        148.2 ms  ● Good
  DC3 CN2                       Los Angeles        152.3 ms  ● Good
  DC9 CN2 GIA                   Los Angeles        155.1 ms  ● Good
  DC2 QNET                      Los Angeles        158.7 ms  ● Good
  DC8 ZNET                      Los Angeles        162.4 ms  ● Good
  DC4 MCOM                      Los Angeles        165.9 ms  ● Good
  Fremont                       Fremont, CA        172.3 ms  ● Good
  New Jersey                    New Jersey         238.5 ms  ● Fair
  New York                      New York           245.1 ms  ● Fair
  Japan Tokyo CN2 GIA           Tokyo, Japan             —   ⊘ Skipped (IPv6)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Green for fast, yellow for okay, red for slow. The fastest datacenter gets a 🏆. If your machine doesn't have IPv6, those targets are auto-skipped instead of timing out.&lt;/p&gt;

&lt;h2&gt;
  
  
  How It Works Under the Hood
&lt;/h2&gt;

&lt;p&gt;The script is ~150 lines of pure bash. Here's the basic flow:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Define all test targets&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each datacenter has a known test IP published by BandwagonHost. The script stores them in arrays:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;names&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"DC2 QNET"&lt;/span&gt; &lt;span class="s2"&gt;"DC3 CN2"&lt;/span&gt; &lt;span class="s2"&gt;"DC6 CN2 GIA-E"&lt;/span&gt; &lt;span class="s2"&gt;"Hong Kong CN2 GIA"&lt;/span&gt; ...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;ips&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;&lt;span class="s2"&gt;"104.194.76.1"&lt;/span&gt; &lt;span class="s2"&gt;"23.252.96.1"&lt;/span&gt; &lt;span class="s2"&gt;"162.244.241.103"&lt;/span&gt; &lt;span class="s2"&gt;"93.179.124.161"&lt;/span&gt; ...&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Ping each one&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Four ICMP packets per datacenter, with a 2-second timeout. The script parses the average RTT from &lt;code&gt;ping&lt;/code&gt; output — and yes, it handles both the Linux and macOS &lt;code&gt;ping&lt;/code&gt; output formats:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ping &lt;span class="nt"&gt;-c&lt;/span&gt; 4 &lt;span class="nt"&gt;-W&lt;/span&gt; 2 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ip&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-1&lt;/span&gt; | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="s1"&gt;'{print $5}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Sort and colorize&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Results get sorted numerically by latency, then each line is color-coded based on thresholds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Green&lt;/strong&gt; (&amp;lt; 100ms): Great&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yellow&lt;/strong&gt; (100–200ms): Good&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Red&lt;/strong&gt; (&amp;gt; 200ms): Fair/Slow&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;4. IPv6 detection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before pinging an IPv6 address, the script checks if the system actually has IPv6 connectivity. No connectivity? It skips gracefully instead of hanging for 8 seconds.&lt;/p&gt;

&lt;h2&gt;
  
  
  Some Patterns I've Noticed
&lt;/h2&gt;

&lt;p&gt;After running this from various locations, a few things stand out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From mainland China&lt;/strong&gt;, Hong Kong CN2 GIA consistently wins — usually 30–50ms. The CN2 GIA-E (DC6) in LA is the next best at ~150ms, significantly faster than regular LA datacenters.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;From US-based servers&lt;/strong&gt;, the LA datacenters cluster around 1–10ms (obviously), while Hong Kong and Dubai jump to 150–200ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CN2 vs non-CN2 matters&lt;/strong&gt; — even within Los Angeles, DC6 (CN2 GIA-E) and DC9 (CN2 GIA) route differently than DC2 (QNET) or DC4 (MCOM). The difference can be 20–30ms from Asia.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokyo CN2 GIA is IPv6-only&lt;/strong&gt;, so it's invisible from many machines. If you need Japan, Osaka Softbank is the IPv4 alternative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dubai is surprisingly solid&lt;/strong&gt; from both Asia and Europe — worth considering if you serve a global audience.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When This Is Actually Useful
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Choosing a datacenter for a new VPS&lt;/strong&gt; — run the script from a machine on the same network as your users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verifying migration options&lt;/strong&gt; — before migrating between BandwagonHost DCs, check if the new one is actually faster&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Periodic checks&lt;/strong&gt; — network routing changes. What was fastest 6 months ago might not be fastest today. Throw it in a cron job:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Weekly speed check, log results&lt;/span&gt;
0 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1 curl &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://raw.githubusercontent.com/devguoo/bwg-speed-test/main/test.sh | bash &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/bwg-results-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%F&lt;span class="si"&gt;)&lt;/span&gt;.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  "Is &lt;code&gt;curl | bash&lt;/code&gt; Safe?"
&lt;/h2&gt;

&lt;p&gt;Fair question. The script is &lt;a href="https://github.com/devguoo/bwg-speed-test/blob/main/test.sh" rel="noopener noreferrer"&gt;fully open source&lt;/a&gt; — it only uses &lt;code&gt;ping&lt;/code&gt; and standard shell utilities. No network calls except ICMP. No data collection. No telemetry.&lt;/p&gt;

&lt;p&gt;If you prefer to inspect first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/devguoo/bwg-speed-test.git
&lt;span class="nb"&gt;cd &lt;/span&gt;bwg-speed-test
&lt;span class="nb"&gt;cat &lt;/span&gt;test.sh   &lt;span class="c"&gt;# read it&lt;/span&gt;
./test.sh     &lt;span class="c"&gt;# then run it&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Try It Out
&lt;/h2&gt;

&lt;p&gt;The repo is at &lt;a href="https://github.com/devguoo/bwg-speed-test" rel="noopener noreferrer"&gt;github.com/devguoo/bwg-speed-test&lt;/a&gt;. It's MIT licensed — use it, fork it, improve it.&lt;/p&gt;

&lt;p&gt;If you find it useful, a ⭐ on the repo helps others discover it. And if you have ideas — more providers, download speed tests, a web UI — issues and PRs are welcome.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What datacenter came out fastest for you? Drop your results in the comments — I'm curious how routing differs across regions.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>vps</category>
      <category>devops</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How I Monitor Small Websites with Lightweight Uptime + TTFB Checks</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Sat, 14 Mar 2026 14:01:52 +0000</pubDate>
      <link>https://dev.to/devguoo/how-i-monitor-small-websites-with-lightweight-uptime-ttfb-checks-20jo</link>
      <guid>https://dev.to/devguoo/how-i-monitor-small-websites-with-lightweight-uptime-ttfb-checks-20jo</guid>
      <description>&lt;h2&gt;
  
  
  Why I Stopped Using UptimeRobot
&lt;/h2&gt;

&lt;p&gt;Let me be upfront: UptimeRobot is fine. Pingdom is fine. Better Stack is fine. For most people, a free-tier SaaS monitor is the right call.&lt;/p&gt;

&lt;p&gt;But I manage about a dozen small static and semi-static sites — mostly content sites behind Cloudflare. Here's what bugged me about the hosted monitoring route:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Free tiers cap at 5–10 monitors.&lt;/strong&gt; I have 12+ sites and want to check multiple endpoints per site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check intervals are 5 minutes at best.&lt;/strong&gt; That's an eternity if your origin server goes down and CF cache expires.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No TTFB tracking over time.&lt;/strong&gt; Most free monitors tell you "up" or "down" — they don't track whether your Time to First Byte is slowly creeping from 200ms to 1.8s.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Alert fatigue from false positives.&lt;/strong&gt; Hosted monitors ping from external IPs that occasionally get rate-limited or geo-blocked. I'd get 3am alerts for sites that were perfectly fine.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One more dashboard I never check.&lt;/strong&gt; I already live in the terminal. Adding another browser tab felt wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built something dumb and simple. It runs on the same box that serves the sites, costs nothing, and does exactly what I need.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Actually Monitor
&lt;/h2&gt;

&lt;p&gt;For each site, I care about three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Is it responding with HTTP 200?&lt;/strong&gt; (uptime)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;What's the TTFB from the origin?&lt;/strong&gt; (performance baseline)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Is the TLS cert expiring soon?&lt;/strong&gt; (because Let's Encrypt renewals fail silently more often than you'd think)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's it. No waterfall charts, no RUM, no synthetic transactions. Just the basics.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core: &lt;code&gt;curl&lt;/code&gt; Does Everything
&lt;/h2&gt;

&lt;p&gt;Here's the thing most people don't realize: &lt;code&gt;curl&lt;/code&gt; has a built-in timing breakdown that gives you more detail than most monitoring dashboards.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code} %{time_namelookup} %{time_connect} %{time_appconnect} %{time_starttransfer} %{time_total}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; https://example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This outputs something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;200 0.012 0.045 0.132 0.247 0.253
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Those numbers, in order:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variable&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time_namelookup&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;DNS resolution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time_connect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TCP handshake complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time_appconnect&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;TLS handshake complete&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time_starttransfer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;TTFB&lt;/strong&gt; — first byte received&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;time_total&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Full response downloaded&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;code&gt;time_starttransfer&lt;/code&gt; is your TTFB. That single number tells you more about your server's health than a green/red dot on a dashboard.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Script
&lt;/h2&gt;

&lt;p&gt;Here's the actual script I run. It's about 60 lines of bash. No dependencies beyond &lt;code&gt;curl&lt;/code&gt; and &lt;code&gt;date&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# site-check.sh — lightweight uptime + TTFB monitor&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="c"&gt;# === Config ===&lt;/span&gt;
&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/var/log/site-monitor"&lt;/span&gt;
&lt;span class="nv"&gt;ALERT_TTFB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2.0        &lt;span class="c"&gt;# seconds — alert if TTFB exceeds this&lt;/span&gt;
&lt;span class="nv"&gt;ALERT_TOTAL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5.0        &lt;span class="c"&gt;# seconds — alert if total time exceeds this&lt;/span&gt;
&lt;span class="nv"&gt;NOTIFY_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;          &lt;span class="c"&gt;# set to your notification command (see below)&lt;/span&gt;

&lt;span class="c"&gt;# Sites to check: URL and a friendly name&lt;/span&gt;
&lt;span class="nv"&gt;SITES&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"https://www.example.com|example"&lt;/span&gt;
  &lt;span class="s2"&gt;"https://www.another-site.com|another"&lt;/span&gt;
  &lt;span class="s2"&gt;"https://blog.example.com|blog"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nv"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +&lt;span class="s2"&gt;"%Y-%m-%dT%H:%M:%SZ"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;DATE_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +&lt;span class="s2"&gt;"%Y-%m-%d"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;entry &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;SITES&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'|'&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; url name &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$entry&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Perform the check&lt;/span&gt;
  &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{http_code}|%{time_namelookup}|%{time_connect}|%{time_appconnect}|%{time_starttransfer}|%{time_total}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--max-time&lt;/span&gt; 10 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--connect-timeout&lt;/span&gt; 5 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Cache-Control: no-cache"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"User-Agent: SiteMonitor/1.0"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;result&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"000|0|0|0|0|0"&lt;/span&gt;

  &lt;span class="nv"&gt;IFS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'|'&lt;/span&gt; &lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; code dns tcp tls ttfb total &lt;span class="o"&gt;&amp;lt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$result&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Log every check as CSV&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TIMESTAMP&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$dns&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$tcp&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$tls&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$ttfb&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;name&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DATE_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.csv"&lt;/span&gt;

  &lt;span class="c"&gt;# Determine status&lt;/span&gt;
  &lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"ok"&lt;/span&gt;
  &lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"200"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"301"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"302"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"down"&lt;/span&gt;
    &lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"HTTP &lt;/span&gt;&lt;span class="nv"&gt;$code&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ttfb&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$ALERT_TTFB&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"slow"&lt;/span&gt;
    &lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"TTFB &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ttfb&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s"&lt;/span&gt;
  &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$total&lt;/span&gt;&lt;span class="s2"&gt; &amp;gt; &lt;/span&gt;&lt;span class="nv"&gt;$ALERT_TOTAL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | bc &lt;span class="nt"&gt;-l&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;status&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"slow"&lt;/span&gt;
    &lt;span class="nv"&gt;reason&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Total &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;total&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s"&lt;/span&gt;
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Alert if not ok&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"ok"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="nv"&gt;$status&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="nv"&gt;$name&lt;/span&gt;&lt;span class="s2"&gt; (&lt;/span&gt;&lt;span class="nv"&gt;$url&lt;/span&gt;&lt;span class="s2"&gt;) — &lt;/span&gt;&lt;span class="nv"&gt;$reason&lt;/span&gt;&lt;span class="s2"&gt; at &lt;/span&gt;&lt;span class="nv"&gt;$TIMESTAMP&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;/alerts.log"&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NOTIFY_CMD&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
      &lt;/span&gt;&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$NOTIFY_CMD&lt;/span&gt;&lt;span class="s2"&gt; '&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="s2"&gt;'"&lt;/span&gt;
    &lt;span class="k"&gt;fi
  fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What's happening:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Each site gets checked with &lt;code&gt;curl&lt;/code&gt;, bypassing cache with the &lt;code&gt;Cache-Control: no-cache&lt;/code&gt; header.&lt;/li&gt;
&lt;li&gt;Results are logged as CSV — one file per site per day. Easy to grep, easy to chart later.&lt;/li&gt;
&lt;li&gt;If HTTP status isn't 2xx/3xx, or if TTFB/total time exceeds thresholds, it logs an alert.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;NOTIFY_CMD&lt;/code&gt; hook is where you plug in whatever alerting you prefer.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TLS Certificate Expiry Check
&lt;/h2&gt;

&lt;p&gt;This is the one that's bitten me the most. Certbot silently fails, the cert expires, and suddenly your site is showing browser warnings to every visitor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# cert-check.sh — TLS certificate expiry monitor&lt;/span&gt;

&lt;span class="nv"&gt;WARN_DAYS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;14

&lt;span class="nv"&gt;DOMAINS&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;
  &lt;span class="s2"&gt;"www.example.com"&lt;/span&gt;
  &lt;span class="s2"&gt;"www.another-site.com"&lt;/span&gt;
  &lt;span class="s2"&gt;"blog.example.com"&lt;/span&gt;
&lt;span class="o"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;domain &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOMAINS&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;expiry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; | openssl s_client &lt;span class="nt"&gt;-servername&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-connect&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt;:443"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    | openssl x509 &lt;span class="nt"&gt;-noout&lt;/span&gt; &lt;span class="nt"&gt;-enddate&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-z&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$expiry&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[error] &lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt; — couldn't fetch cert"&lt;/span&gt;
    &lt;span class="k"&gt;continue
  fi

  &lt;/span&gt;&lt;span class="nv"&gt;expiry_epoch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$expiry&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; +%s 2&amp;gt;/dev/null &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-jf&lt;/span&gt; &lt;span class="s2"&gt;"%b %d %H:%M:%S %Y %Z"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$expiry&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; +%s 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;now_epoch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;days_left&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;expiry_epoch &lt;span class="o"&gt;-&lt;/span&gt; now_epoch&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;86400&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;

  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt; days_left &amp;lt; WARN_DAYS &lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[warn] &lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt; — cert expires in &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;days_left&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; days (&lt;/span&gt;&lt;span class="nv"&gt;$expiry&lt;/span&gt;&lt;span class="s2"&gt;)"&lt;/span&gt;
  &lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[ok] &lt;/span&gt;&lt;span class="nv"&gt;$domain&lt;/span&gt;&lt;span class="s2"&gt; — cert valid for &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;days_left&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; days"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note on portability:&lt;/strong&gt; The &lt;code&gt;date&lt;/code&gt; parsing differs between Linux (&lt;code&gt;date -d&lt;/code&gt;) and macOS (&lt;code&gt;date -jf&lt;/code&gt;). The script tries both. If you're only on Linux, simplify to just &lt;code&gt;date -d&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Scheduling with Cron
&lt;/h2&gt;

&lt;p&gt;I run the uptime check every 2 minutes and the cert check once daily:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;*/2 * * * * /opt/scripts/site-check.sh &amp;gt;&amp;gt; /var/log/site-monitor/cron.log 2&amp;gt;&amp;amp;1
0 6 * * * /opt/scripts/cert-check.sh &amp;gt;&amp;gt; /var/log/site-monitor/cert-cron.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every 2 minutes might sound aggressive, but &lt;code&gt;curl&lt;/code&gt; with a 10-second timeout across a dozen sites finishes in under 5 seconds total. The server doesn't even notice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Notifications: Keep It Stupid Simple
&lt;/h2&gt;

&lt;p&gt;I've tried Slack webhooks, PagerDuty, custom Discord bots. For a solo operation, I came back to the simplest option: &lt;strong&gt;Telegram Bot API&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;NOTIFY_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'send_telegram'&lt;/span&gt;

send_telegram&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;bot_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_BOT_TOKEN"&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;chat_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"YOUR_CHAT_ID"&lt;/span&gt;

  curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.telegram.org/bot&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;bot_token&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/sendMessage"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;chat_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$chat_id&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$msg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;parse_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Markdown"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why Telegram?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Free, no rate limits for low volume.&lt;/li&gt;
&lt;li&gt;Push notifications on my phone.&lt;/li&gt;
&lt;li&gt;No app to maintain, no webhook server to keep running.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer Discord, Slack, or even plain email via &lt;code&gt;sendmail&lt;/code&gt;, swap in your 5-line function. The interface is just a string going in.&lt;/p&gt;




&lt;h2&gt;
  
  
  Analyzing the Data
&lt;/h2&gt;

&lt;p&gt;The CSV logs pile up. Here's how I actually use them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick TTFB average for a site today
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'{sum+=$8; n++} END {printf "Avg TTFB: %.3fs (%d checks)\n", sum/n, n}'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  /var/log/site-monitor/example_2025-03-12.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Find all checks where TTFB exceeded 1 second
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'$8 &amp;gt; 1.0 {print $1, $2, $8"s"}'&lt;/span&gt; /var/log/site-monitor/example_&lt;span class="k"&gt;*&lt;/span&gt;.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Weekly TTFB trend (average per day)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;f &lt;span class="k"&gt;in&lt;/span&gt; /var/log/site-monitor/example_2025-03-&lt;span class="k"&gt;*&lt;/span&gt;.csv&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;day&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-oE&lt;/span&gt; &lt;span class="s1"&gt;'[0-9]{4}-[0-9]{2}-[0-9]{2}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;avg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="s1"&gt;'{sum+=$8; n++} END {printf "%.3f", sum/n}'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$day&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$avg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csvs"&gt;&lt;code&gt;&lt;span class="ld"&gt;2025-03-06&lt;/span&gt; &lt;span class="mf"&gt;0.234&lt;/span&gt;
&lt;span class="ld"&gt;2025-03-07&lt;/span&gt; &lt;span class="mf"&gt;0.241&lt;/span&gt;
&lt;span class="ld"&gt;2025-03-08&lt;/span&gt; &lt;span class="mf"&gt;0.228&lt;/span&gt;
&lt;span class="ld"&gt;2025-03-09&lt;/span&gt; &lt;span class="mf"&gt;0.512&lt;/span&gt;
&lt;span class="ld"&gt;2025-03-10&lt;/span&gt; &lt;span class="mf"&gt;0.519&lt;/span&gt;
&lt;span class="ld"&gt;2025-03-11&lt;/span&gt; &lt;span class="mf"&gt;0.245&lt;/span&gt;
&lt;span class="ld"&gt;2025-03-12&lt;/span&gt; &lt;span class="mf"&gt;0.238&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See that spike on March 9–10? That was a runaway log rotation filling the disk. TTFB doubled. The site never went "down" — a binary up/down monitor would've missed it entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's the whole point of tracking TTFB over time.&lt;/strong&gt; Degradation is gradual. By the time a site is "down," you've already been serving slow pages for days.&lt;/p&gt;




&lt;h2&gt;
  
  
  Log Rotation
&lt;/h2&gt;

&lt;p&gt;Don't let your monitoring logs become the disk problem. Simple &lt;code&gt;logrotate&lt;/code&gt; config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;/&lt;span class="n"&gt;var&lt;/span&gt;/&lt;span class="n"&gt;log&lt;/span&gt;/&lt;span class="n"&gt;site&lt;/span&gt;-&lt;span class="n"&gt;monitor&lt;/span&gt;/*.&lt;span class="n"&gt;csv&lt;/span&gt; {
    &lt;span class="n"&gt;daily&lt;/span&gt;
    &lt;span class="n"&gt;rotate&lt;/span&gt; &lt;span class="m"&gt;90&lt;/span&gt;
    &lt;span class="n"&gt;compress&lt;/span&gt;
    &lt;span class="n"&gt;delaycompress&lt;/span&gt;
    &lt;span class="n"&gt;missingok&lt;/span&gt;
    &lt;span class="n"&gt;notifempty&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;90 days of compressed CSVs for a dozen sites is a few megabytes. Not worth optimizing further.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Intentionally Left Out
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A web dashboard.&lt;/strong&gt; If I need to visualize trends, I pipe the CSV into &lt;code&gt;gnuplot&lt;/code&gt; or import into a spreadsheet. Building a dashboard is a project I don't need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Multi-region checks.&lt;/strong&gt; I'm monitoring from the origin server. This tells me if &lt;em&gt;my server&lt;/em&gt; is healthy. For CDN/edge monitoring, you'd need external vantage points — and at that point, a SaaS tool makes more sense.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Incident management.&lt;/strong&gt; It's just me. An alert hits my phone, I SSH in, I fix it. No escalation policies needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database storage.&lt;/strong&gt; CSV files with &lt;code&gt;awk&lt;/code&gt; and &lt;code&gt;grep&lt;/code&gt; handle everything at this scale. If I ever hit 100+ sites, I'd pipe into SQLite. But flat files are debug-friendly and zero-maintenance.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The Full Setup at a Glance
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;/opt/scripts/
├── site-check.sh       # uptime + TTFB (runs every 2 min)
├── cert-check.sh       # TLS expiry (runs daily)
└── notify.sh           # shared notification function

/var/log/site-monitor/
├── example_2025-03-12.csv
├── another_2025-03-12.csv
├── blog_2025-03-12.csv
├── alerts.log
└── cert-cron.log
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Total disk footprint of the scripts: &lt;strong&gt;under 4KB.&lt;/strong&gt;&lt;br&gt;
Total runtime per check cycle: &lt;strong&gt;under 5 seconds.&lt;/strong&gt;&lt;br&gt;
Total dependencies: &lt;strong&gt;curl, openssl, bash, cron.&lt;/strong&gt; That's it.&lt;/p&gt;




&lt;h2&gt;
  
  
  When This Isn't Enough
&lt;/h2&gt;

&lt;p&gt;Be honest about the limits. This setup stops making sense when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need &lt;strong&gt;multi-region monitoring&lt;/strong&gt; (latency from Tokyo vs. Frankfurt matters)&lt;/li&gt;
&lt;li&gt;You need &lt;strong&gt;status pages&lt;/strong&gt; for customers&lt;/li&gt;
&lt;li&gt;You have &lt;strong&gt;SLA obligations&lt;/strong&gt; that require third-party verification&lt;/li&gt;
&lt;li&gt;Your team is &lt;strong&gt;more than 2–3 people&lt;/strong&gt; and needs shared dashboards&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that point, look at &lt;a href="https://github.com/louislam/uptime-kuma" rel="noopener noreferrer"&gt;Uptime Kuma&lt;/a&gt; (self-hosted, has a UI) or bite the bullet on a paid SaaS.&lt;/p&gt;

&lt;p&gt;But for a solo developer running a handful of sites? 60 lines of bash and a cron job is all you need.&lt;/p&gt;




&lt;h2&gt;
  
  
  Key Takeaways
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;curl -w&lt;/code&gt; is criminally underused.&lt;/strong&gt; It gives you DNS, TCP, TLS, and TTFB timing in one call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TTFB trending matters more than uptime percentage.&lt;/strong&gt; A site can be "up" and still painfully slow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CSV + cron + awk is a real monitoring stack&lt;/strong&gt; at small scale. Don't over-engineer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Check your TLS certs.&lt;/strong&gt; Certbot failures are silent. Don't learn this the hard way.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keep alerting simple.&lt;/strong&gt; A Telegram/Discord message beats a dashboard you never open.&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;&lt;em&gt;The scripts in this post are simplified versions of what I actually run. Feel free to adapt them. If you improve on them, I'd love to hear about it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>monitoring</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>How to Automate VPS Benchmarking with Bash, Cron, and Simple Logging</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Fri, 13 Mar 2026 14:01:55 +0000</pubDate>
      <link>https://dev.to/devguoo/how-to-automate-vps-benchmarking-with-bash-cron-and-simple-logging-3p5</link>
      <guid>https://dev.to/devguoo/how-to-automate-vps-benchmarking-with-bash-cron-and-simple-logging-3p5</guid>
      <description>&lt;p&gt;Managing a handful of VPS instances is fine. Managing a dozen gets messy fast — especially when you want to know whether that provider's "1 Gbps" claim holds up after the honeymoon period ends, or whether CPU performance silently degrades on a shared hypervisor over time.&lt;/p&gt;

&lt;p&gt;I got tired of SSHing into each box, running the same manual benchmarks, and then forgetting to compare the results to last month's numbers. So I built a small automation: each server runs a Bash script every six hours, logs results to a CSV, and pings me when something looks off. No Prometheus, no dashboards required — just Bash, Cron, and a text file.&lt;/p&gt;

&lt;p&gt;Here's the full setup.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Core Benchmark Script
&lt;/h2&gt;

&lt;p&gt;Save this as &lt;code&gt;/opt/bench/run_bench.sh&lt;/code&gt;. It covers the four things I track: network latency, download speed, CPU, and disk I/O.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# /opt/bench/run_bench.sh&lt;/span&gt;
&lt;span class="c"&gt;# Runs benchmarks and appends results to a monthly CSV log.&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/bench/logs"&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bench_&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.csv"&lt;/span&gt;
&lt;span class="nv"&gt;HOSTNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +&lt;span class="s2"&gt;"%Y-%m-%dT%H:%M:%SZ"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# Write CSV header if file is new&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"timestamp,host,ping_ms,dl_mbps,cpu_score,disk_write_mbps,disk_read_mbps"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# ---- 1. Ping latency (average across 3 public resolvers) ----&lt;/span&gt;
ping_avg&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  ping &lt;span class="nt"&gt;-c&lt;/span&gt; 10 &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$host&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;'/'&lt;/span&gt; &lt;span class="s1"&gt;'/^rtt/{print $5}'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;PING_GOOGLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ping_avg &lt;span class="s2"&gt;"8.8.8.8"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;PING_CF&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ping_avg &lt;span class="s2"&gt;"1.1.1.1"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;PING_QUAD9&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ping_avg &lt;span class="s2"&gt;"9.9.9.9"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;PING_AVG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s2"&gt;"BEGIN {printf &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;%.2f&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;, (&lt;/span&gt;&lt;span class="nv"&gt;$PING_GOOGLE&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$PING_CF&lt;/span&gt;&lt;span class="s2"&gt; + &lt;/span&gt;&lt;span class="nv"&gt;$PING_QUAD9&lt;/span&gt;&lt;span class="s2"&gt;) / 3}"&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ---- 2. Download speed via curl ----&lt;/span&gt;
&lt;span class="c"&gt;# 100 MB test download, 15s timeout to avoid hanging on slow connections&lt;/span&gt;
&lt;span class="nv"&gt;DL_MBPS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-o&lt;/span&gt; /dev/null &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"%{speed_download}"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--max-time&lt;/span&gt; 15 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"https://speed.cloudflare.com/__down?bytes=104857600"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{printf "%.2f", $1 / 1048576}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# ---- 3. CPU benchmark (pure Bash — no external dependencies) ----&lt;/span&gt;
cpu_bench&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;start end &lt;span class="nv"&gt;iterations&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;500000
  &lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%N&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;&lt;span class="nv"&gt;i&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; i&amp;lt;iterations&lt;span class="p"&gt;;&lt;/span&gt; i++&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    : &lt;span class="k"&gt;$((&lt;/span&gt;i &lt;span class="o"&gt;*&lt;/span&gt; i&lt;span class="k"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;done
  &lt;/span&gt;&lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%s%N&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="c"&gt;# iterations per millisecond&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;iterations &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="m"&gt;1000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="o"&gt;((&lt;/span&gt;end &lt;span class="o"&gt;-&lt;/span&gt; start&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="m"&gt;1000000&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;CPU_SCORE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;cpu_bench&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# If sysbench is available, uncomment for more standard numbers:&lt;/span&gt;
&lt;span class="c"&gt;# CPU_SCORE=$(sysbench cpu --cpu-max-prime=20000 run 2&amp;gt;/dev/null \&lt;/span&gt;
&lt;span class="c"&gt;#   | awk '/events per second/{printf "%.0f", $NF}')&lt;/span&gt;

&lt;span class="c"&gt;# ---- 4. Disk I/O (dd write + read, 256 MB each) ----&lt;/span&gt;
&lt;span class="nv"&gt;TESTFILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/tmp/bench_io_&lt;/span&gt;&lt;span class="nv"&gt;$$&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

parse_dd_speed&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'/copied/{
    for(i=1;i&amp;lt;=NF;i++) {
      if($i=="MB/s" || $i=="GB/s") { val=$(i-1); unit=$i }
    }
    if(unit=="GB/s") val=val*1024
    printf "%.2f", val
  }'&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;DISK_WRITE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/zero &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TESTFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;256 &lt;span class="nv"&gt;conv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fdatasync 2&amp;gt;&amp;amp;1 &lt;span class="se"&gt;\&lt;/span&gt;
  | parse_dd_speed&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;DISK_READ&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dd &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TESTFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;of&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/dev/null &lt;span class="nv"&gt;bs&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1M &lt;span class="nv"&gt;count&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;256 2&amp;gt;&amp;amp;1 &lt;span class="se"&gt;\&lt;/span&gt;
  | parse_dd_speed&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$TESTFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="c"&gt;# ---- Append result row to CSV ----&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TIMESTAMP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOSTNAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PING_AVG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DL_MBPS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CPU_SCORE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DISK_WRITE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;,&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DISK_READ&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"[bench] ping=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PING_AVG&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;ms dl=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DL_MBPS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;MB/s cpu=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CPU_SCORE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; disk_w=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DISK_WRITE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;MB/s disk_r=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DISK_READ&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;MB/s"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x /opt/bench/run_bench.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A note on the CPU score: it's "loop iterations per millisecond" — not a standard unit, but it's internally consistent on the same machine. For cross-server comparison, use sysbench. For trend tracking on one box, the Bash loop is perfectly fine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Log Rotation
&lt;/h2&gt;

&lt;p&gt;The script already separates logs by month (&lt;code&gt;bench_2025-03.csv&lt;/code&gt;, etc.), giving you automatic monthly archives. To compress old files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# /opt/bench/rotate_logs.sh&lt;/span&gt;

&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/bench/logs"&lt;/span&gt;
&lt;span class="nv"&gt;CURRENT_MONTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m&lt;span class="si"&gt;)&lt;/span&gt;

find &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_DIR&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"bench_*.csv"&lt;/span&gt; | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; f&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  &lt;/span&gt;&lt;span class="nv"&gt;month&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;basename&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; .csv | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="s1"&gt;'s/bench_//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$month&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$CURRENT_MONTH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;gzip&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Archived: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Setting Up Cron
&lt;/h2&gt;

&lt;p&gt;Here's where tutorials usually skip the annoying-but-critical details. Cron runs in a stripped environment: no &lt;code&gt;.bashrc&lt;/code&gt;, potentially no &lt;code&gt;/usr/local/bin&lt;/code&gt; in PATH, and output goes nowhere unless you redirect it.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;crontab -e&lt;/code&gt; and add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Explicit PATH — don't assume Cron inherits yours
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

# Benchmark every 6 hours
0 */6 * * * /opt/bench/run_bench.sh &amp;gt;&amp;gt; /opt/bench/logs/cron.log 2&amp;gt;&amp;amp;1

# Rotate logs on the 1st of each month at 3 AM
0 3 1 * * /opt/bench/rotate_logs.sh &amp;gt;&amp;gt; /opt/bench/logs/cron.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things worth highlighting:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Set &lt;code&gt;PATH&lt;/code&gt; explicitly at the top of your crontab.&lt;/strong&gt; On some minimal Debian/Ubuntu images, Cron's default PATH is just &lt;code&gt;/usr/bin:/bin&lt;/code&gt;. If &lt;code&gt;curl&lt;/code&gt; or &lt;code&gt;ping&lt;/code&gt; live in &lt;code&gt;/usr/local/bin&lt;/code&gt;, your job fails silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Always redirect &lt;code&gt;2&amp;gt;&amp;amp;1&lt;/code&gt;.&lt;/strong&gt; If the script errors out, you want the message captured in a log — not swallowed by the void. I learned this the hard way after a curl version mismatch went undetected for two weeks.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alerting When Things Go Wrong
&lt;/h2&gt;

&lt;p&gt;Passive logging is fine; active alerts make it actionable. The key design decision here: use dynamic thresholds based on each server's own history instead of hardcoded values. A 50ms ping is fine for a server in Europe but alarming for one sitting in the same datacenter as you.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# /opt/bench/check_alerts.sh&lt;/span&gt;
&lt;span class="c"&gt;# Reads the current month's CSV and alerts if the latest reading&lt;/span&gt;
&lt;span class="c"&gt;# is more than 3x (or less than 1/3) the historical average.&lt;/span&gt;

&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/bench/logs"&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/bench_&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;.csv"&lt;/span&gt;
&lt;span class="nv"&gt;WEBHOOK_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BENCH_WEBHOOK_URL&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="nv"&gt;ALERT_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;BENCH_ALERT_EMAIL&lt;/span&gt;&lt;span class="k"&gt;:-}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;

&lt;span class="c"&gt;# Args: column_number metric_label direction(high|low)&lt;/span&gt;
&lt;span class="c"&gt;# Returns alert message on stdout, exits 1 if alert triggered&lt;/span&gt;
check_metric&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;col&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nv"&gt;direction&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$3&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="nt"&gt;-F&lt;/span&gt;&lt;span class="s1"&gt;','&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;col&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$col&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nv"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$label&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nb"&gt;dir&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$direction&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s1"&gt;'
    NR == 1 { next }
    { rows[NR] = $col; total += $col; count++ }
    END {
      if (count &amp;lt; 3) exit 0
      last = rows[NR]
      hist_avg = (total - last) / (count - 1)
      if (hist_avg == 0) exit 0

      if (dir == "high" &amp;amp;&amp;amp; last &amp;gt; hist_avg * 3) {
        printf "ALERT %s: %.2f (3x avg %.2f)\n", label, last, hist_avg
        exit 1
      } else if (dir == "low" &amp;amp;&amp;amp; last &amp;lt; hist_avg / 3) {
        printf "ALERT %s: %.2f (&amp;lt; 1/3 avg %.2f)\n", label, last, hist_avg
        exit 1
      }
    }
  '&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$LOG_FILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;PING_ALERT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;check_metric 3 &lt;span class="s2"&gt;"ping_ms"&lt;/span&gt; &lt;span class="s2"&gt;"high"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;DL_ALERT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;check_metric 4 &lt;span class="s2"&gt;"dl_mbps"&lt;/span&gt; &lt;span class="s2"&gt;"low"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;CPU_ALERT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;check_metric 5 &lt;span class="s2"&gt;"cpu_score"&lt;/span&gt; &lt;span class="s2"&gt;"low"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;ALERTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PING_ALERT&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;DL_ALERT&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;CPU_ALERT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ALERTS&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nv"&gt;HOST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;hostname&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[bench] &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOST&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;ALERTS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$MESSAGE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

  &lt;span class="c"&gt;# Slack / Discord / any JSON webhook&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WEBHOOK_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$WEBHOOK_URL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s2"&gt;"{&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;}"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /dev/null
  &lt;span class="k"&gt;fi&lt;/span&gt;

  &lt;span class="c"&gt;# Email via sendmail or msmtp&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ALERT_EMAIL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"Subject: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MESSAGE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; | sendmail &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ALERT_EMAIL&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the variables in your crontab environment block and chain the scripts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BENCH_WEBHOOK_URL=https://hooks.slack.com/services/YOUR/WEBHOOK/HERE
BENCH_ALERT_EMAIL=you@example.com

0 */6 * * * /opt/bench/run_bench.sh &amp;gt;&amp;gt; /opt/bench/logs/cron.log 2&amp;gt;&amp;amp;1 &amp;amp;&amp;amp; \
            /opt/bench/check_alerts.sh &amp;gt;&amp;gt; /opt/bench/logs/cron.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The dynamic threshold approach means no per-server tuning. After a week of baseline data, anomalies start surfacing on their own.&lt;/p&gt;




&lt;h2&gt;
  
  
  Optional: Visualizing the Trends
&lt;/h2&gt;

&lt;p&gt;If you want a quick trend chart without standing up a full monitoring stack, gnuplot handles it in a few lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# /opt/bench/plot_trends.sh&lt;/span&gt;
&lt;span class="c"&gt;# Generates PNG charts from the current month's data.&lt;/span&gt;

&lt;span class="nv"&gt;MONTH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; +%Y-%m&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/bench/logs/bench_&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MONTH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.csv"&lt;/span&gt;
&lt;span class="nv"&gt;OUT_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"/opt/bench/logs"&lt;/span&gt;

gnuplot &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt;
set datafile separator ","
set xdata time
set timefmt "%Y-%m-%dT%H:%M:%SZ"
set format x "%m/%d %H:%M"
set xtics rotate by -45
set grid
set terminal png size 1200,400

set output "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/ping_trend.png"
set title "Ping Latency — &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MONTH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
set ylabel "ms"
plot "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;" using 1:3 skip 1 with linespoints lw 2 title "ping_ms"

set output "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;/dl_trend.png"
set title "Download Speed — &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;MONTH&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"
set ylabel "MB/s"
plot "&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;LOG_FILE&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;" using 1:4 skip 1 with linespoints lw 2 lc rgb "green" title "dl_mbps"
&lt;/span&gt;&lt;span class="no"&gt;EOF

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Charts written to &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;OUT_DIR&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you prefer the browser, this minimal HTML file loads and plots the CSV client-side with Chart.js (no server needed — just open it locally or serve it from nginx):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"UTF-8"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Bench Trends&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://cdn.jsdelivr.net/npm/chart.js@4/dist/chart.umd.min.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;style&amp;gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;max-width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1100px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2rem&lt;/span&gt; &lt;span class="nb"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/style&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Ping Latency&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"ping"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;h2&amp;gt;&lt;/span&gt;Download Speed&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;canvas&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dl"&lt;/span&gt; &lt;span class="na"&gt;height=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/canvas&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
    &lt;span class="c1"&gt;// Update filename to current month before deploying&lt;/span&gt;
    &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bench_YYYY-MM.csv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;csv&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;csv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ping&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dl&lt;/span&gt;     &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;parseFloat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mkChart&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Chart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
          &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;line&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;labels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;datasets&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;borderColor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;color&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;tension&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;pointRadius&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
            &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;legend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;scales&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ticks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;maxTicksLimit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nf"&gt;mkChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Ping (ms)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="nx"&gt;ping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#e74c3c&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;mkChart&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dl&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Speed (MB/s)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#2ecc71&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  After a Few Months of Running This
&lt;/h2&gt;

&lt;p&gt;The biggest win isn't catching catastrophic failures — those are usually obvious. It's catching the slow drift: a server that was reliably hitting 800 MB/s disk writes in January that's now averaging 420 MB/s in March. Without the log, I'd never have noticed.&lt;/p&gt;

&lt;p&gt;A few practical observations from running this on about eight servers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;False positives are rare&lt;/strong&gt; with the 3x threshold. In four months I've had three genuine alerts and zero false ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Disk I/O is the noisiest metric.&lt;/strong&gt; &lt;code&gt;dd&lt;/code&gt; results vary a lot run to run, especially on NVMe. If you want something more stable, consider &lt;code&gt;fio&lt;/code&gt; instead — but that's a separate rabbit hole.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The CSV format pays off.&lt;/strong&gt; I've imported the data into Excel, fed it to pandas, and piped it through &lt;code&gt;awk&lt;/code&gt; one-liners. Structured plaintext ages well.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The natural next step is shipping these CSVs into something like Prometheus (via a custom exporter or &lt;code&gt;node_exporter&lt;/code&gt; textfile collector) and viewing them in Grafana. But honestly, for most use cases, a few PNG charts and a Slack ping when things go sideways is enough. Sometimes the simple solution is the right one.&lt;/p&gt;

&lt;p&gt;The full script set lives at &lt;code&gt;/opt/bench/&lt;/code&gt; — about 150 lines of Bash total. No containers, no daemons, no dependencies beyond &lt;code&gt;curl&lt;/code&gt;, &lt;code&gt;ping&lt;/code&gt;, &lt;code&gt;dd&lt;/code&gt;, and &lt;code&gt;gzip&lt;/code&gt;. Runs on every Linux distro I've tried without modification.&lt;/p&gt;

&lt;p&gt;If you extend it or hit edge cases, I'd be curious what you run into.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>bash</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Budget VPS Showdown in 2026: I Tested 4 Providers So You Don't Have To</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Wed, 11 Mar 2026 14:01:51 +0000</pubDate>
      <link>https://dev.to/devguoo/budget-vps-showdown-in-2026-i-tested-4-providers-so-you-dont-have-to-4cll</link>
      <guid>https://dev.to/devguoo/budget-vps-showdown-in-2026-i-tested-4-providers-so-you-dont-have-to-4cll</guid>
      <description>&lt;h1&gt;
  
  
  Budget VPS Showdown in 2026: I Tested 4 Providers So You Don't Have To
&lt;/h1&gt;

&lt;p&gt;If you've spent any time looking for a cheap VPS lately, you've probably noticed the same pattern I did: every comparison post claims one provider is the obvious winner, and somehow that winner is usually the one with the referral link.&lt;/p&gt;

&lt;p&gt;That wasn't very helpful for me.&lt;/p&gt;

&lt;p&gt;I wanted something more practical: if you're a developer, indie hacker, self-hoster, or just someone who needs a small Linux box for side projects, which budget VPS actually makes sense &lt;strong&gt;once you factor in latency, billing model, migration flexibility, and the annoying extra costs nobody mentions up front&lt;/strong&gt;?&lt;/p&gt;

&lt;p&gt;So instead of framing this as &lt;strong&gt;"BandwagonHost vs everyone else"&lt;/strong&gt;, I looked at it the way most of us actually shop:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I tested several popular budget VPS options and compared what matters in real-world usage, especially from a China-based network.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The four providers I focused on were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BandwagonHost&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Vultr&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;DigitalOcean&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Linode&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I also mention &lt;strong&gt;RackNerd&lt;/strong&gt; in the pricing discussion because it often shows up in low-cost VPS conversations, even though it wasn't part of the same benchmark table.&lt;/p&gt;

&lt;p&gt;This isn't a “one provider beats all” post. The right choice depends a lot on whether you care more about &lt;strong&gt;China-friendly routing, hourly billing, simplicity, or lowest possible price&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Actually Care About in a Budget VPS
&lt;/h2&gt;

&lt;p&gt;When people say “budget VPS,” they usually mean something like a 1 vCPU / 1GB RAM instance used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lightweight Docker apps&lt;/li&gt;
&lt;li&gt;reverse proxies&lt;/li&gt;
&lt;li&gt;personal VPNs&lt;/li&gt;
&lt;li&gt;monitoring nodes&lt;/li&gt;
&lt;li&gt;test environments&lt;/li&gt;
&lt;li&gt;blogs, APIs, and hobby projects&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At that size, raw CPU isn't the only thing that matters. In practice, I care about five things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Network quality&lt;/strong&gt; — latency, routing, and consistency matter more than marketing terms&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Price-to-performance&lt;/strong&gt; — not just sticker price, but what you really get&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Billing flexibility&lt;/strong&gt; — monthly, hourly, yearly, upgrade path&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration experience&lt;/strong&gt; — can I move regions or rebuild quickly?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Operational friction&lt;/strong&gt; — how painful is the control panel, snapshots, recovery, and support?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That last point is underrated. A provider can look great on paper and still be annoying to use once you actually deploy something.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Methodology
&lt;/h2&gt;

&lt;p&gt;A lot of VPS comparisons throw in a speed table with no context, which makes the data hard to trust. So here's the methodology behind this comparison.&lt;/p&gt;

&lt;p&gt;I used the same baseline size wherever possible: &lt;strong&gt;1 CPU, 1GB RAM, 20GB SSD-class storage&lt;/strong&gt;. The network observations below are based on tests from a China-based connection toward the VPS locations listed in the benchmark table.&lt;/p&gt;

&lt;p&gt;To reduce one-off noise, the measurements were taken &lt;strong&gt;multiple times across different periods of the day&lt;/strong&gt; rather than from a single run:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;morning&lt;/li&gt;
&lt;li&gt;afternoon&lt;/li&gt;
&lt;li&gt;late evening&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each provider, I compared:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;average latency&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;download throughput&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;upload throughput&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The benchmark figures below are the same reference numbers from the original draft and are kept unchanged.&lt;/p&gt;

&lt;p&gt;One important caveat: budget VPS performance can vary by host node, time of day, transit routing, and temporary congestion. So I don't treat these numbers as absolute truth. I treat them as &lt;strong&gt;directional data points&lt;/strong&gt; that help explain how each provider feels in actual use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Network Speed Results (China → VPS)
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Provider&lt;/th&gt;
&lt;th&gt;Avg Latency&lt;/th&gt;
&lt;th&gt;Download&lt;/th&gt;
&lt;th&gt;Upload&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;BandwagonHost (CN2 GIA)&lt;/td&gt;
&lt;td&gt;150ms&lt;/td&gt;
&lt;td&gt;85 Mbps&lt;/td&gt;
&lt;td&gt;40 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vultr (Tokyo)&lt;/td&gt;
&lt;td&gt;80ms&lt;/td&gt;
&lt;td&gt;95 Mbps&lt;/td&gt;
&lt;td&gt;50 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DigitalOcean (SGP)&lt;/td&gt;
&lt;td&gt;180ms&lt;/td&gt;
&lt;td&gt;70 Mbps&lt;/td&gt;
&lt;td&gt;35 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linode (Tokyo)&lt;/td&gt;
&lt;td&gt;90ms&lt;/td&gt;
&lt;td&gt;90 Mbps&lt;/td&gt;
&lt;td&gt;45 Mbps&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At first glance, the surprising result is that &lt;strong&gt;BandwagonHost isn't automatically the fastest option here&lt;/strong&gt;, even with the CN2 GIA reputation. In this sample, &lt;strong&gt;Vultr Tokyo&lt;/strong&gt; and &lt;strong&gt;Linode Tokyo&lt;/strong&gt; delivered lower latency and better overall throughput from China.&lt;/p&gt;

&lt;p&gt;That doesn't make BandwagonHost bad. It just means the old “BandwagonHost = best by default” narrative is too simplistic.&lt;/p&gt;

&lt;p&gt;Routing quality is contextual. If your traffic pattern is China-heavy and you specifically need more predictable cross-border behavior in certain regions, BandwagonHost still deserves consideration. But if you're simply looking for a responsive general-purpose box for dev work, the Tokyo options can be very compelling.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing Snapshot
&lt;/h2&gt;

&lt;p&gt;Using the smallest comparable tier of roughly &lt;strong&gt;1 CPU, 1GB RAM, 20GB SSD&lt;/strong&gt;, here's how the pricing lines up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BandwagonHost:&lt;/strong&gt; ~$49.99/year (CN2 GIA)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vultr:&lt;/strong&gt; $6/month ($72/year)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DigitalOcean:&lt;/strong&gt; $6/month ($72/year)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Linode:&lt;/strong&gt; $5/month ($60/year)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RackNerd:&lt;/strong&gt; ~$25/year (promotional)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you only look at the annual number, RackNerd is obviously the cheapest and BandwagonHost may look reasonable against the monthly-priced cloud brands.&lt;/p&gt;

&lt;p&gt;But that still doesn't tell the full story, because the real cost depends on how long you keep the instance and how often your requirements change.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you need a VPS for a &lt;strong&gt;year-long stable workload&lt;/strong&gt;, annual billing can be fine.&lt;/li&gt;
&lt;li&gt;If you spin up test environments often, &lt;strong&gt;hourly or monthly billing&lt;/strong&gt; is much better.&lt;/li&gt;
&lt;li&gt;If you're still experimenting with your architecture, committing to annual plans too early can become a trap.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is where BandwagonHost becomes more niche: it can make sense if you know exactly what you want and plan to keep it running. It's less attractive if you're still iterating.&lt;/p&gt;

&lt;h2&gt;
  
  
  Provider-by-Provider Notes
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1) BandwagonHost: Good for China-Focused Use Cases, Less Flexible Than It Looks
&lt;/h2&gt;

&lt;p&gt;BandwagonHost has built a strong reputation among Chinese users for one reason: &lt;strong&gt;routing matters&lt;/strong&gt;, and they leaned into that earlier than many mainstream providers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What stood out to me
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;KiwiVM is simple and functional.&lt;/strong&gt; It isn't flashy, but it's easy to find core actions like reboot, reinstall, and IP information.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migration flexibility is genuinely useful.&lt;/strong&gt; Being able to move between data centers is one of the strongest practical advantages in the budget VPS space.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That said, the experience is more “utility-first” than polished. Compared with Vultr or DigitalOcean, the platform feels more specialized and less cloud-native.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it falls short
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No hourly billing&lt;/strong&gt;, which immediately makes it worse for short-lived projects or testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entry pricing isn't actually that cheap&lt;/strong&gt; if you compare it against broader cloud competitors on flexibility.&lt;/li&gt;
&lt;li&gt;The product lineup can be confusing if you aren't already familiar with terms like CN2 GIA, regular CN2, and location-based route differences.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Who I think it's for
&lt;/h3&gt;

&lt;p&gt;BandwagonHost makes the most sense if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;care specifically about &lt;strong&gt;China-related network behavior&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;are okay with &lt;strong&gt;annual billing&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;want &lt;strong&gt;easy data center migration&lt;/strong&gt; without rebuilding from scratch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want a VPS that behaves more like modern cloud infrastructure with quick scaling and disposable instances, BandwagonHost is harder to recommend.&lt;/p&gt;

&lt;h2&gt;
  
  
  2) Vultr: Probably the Easiest All-Rounder for Developers
&lt;/h2&gt;

&lt;p&gt;Vultr is one of those providers that doesn't always win the “hardcore VPS enthusiast” argument, but in day-to-day use it often feels like the least frustrating choice.&lt;/p&gt;

&lt;h3&gt;
  
  
  What stood out to me
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The control panel is clean and fast.&lt;/strong&gt; Spinning up, destroying, snapshotting, and redeploying instances is straightforward.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tokyo performance looked very strong&lt;/strong&gt; in this test set, especially on latency and throughput from China.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For people building side projects, test environments, or lightweight production services, that combination matters a lot. A platform that's slightly more expensive but easier to operate can save more time than it costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it falls short
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It doesn't offer the same niche China-routing identity that made BandwagonHost popular.&lt;/li&gt;
&lt;li&gt;Costs can rise quickly once you start adding extras or scaling beyond the smallest instances.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Who I think it's for
&lt;/h3&gt;

&lt;p&gt;Vultr is a solid fit if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;want &lt;strong&gt;hourly/monthly billing&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;need &lt;strong&gt;fast region provisioning&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;prefer a more mainstream cloud UX&lt;/li&gt;
&lt;li&gt;value a &lt;strong&gt;Tokyo location&lt;/strong&gt; with good responsiveness from China&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I were recommending a “safe default” to a developer who doesn't want to think too hard, Vultr would be near the top.&lt;/p&gt;

&lt;h2&gt;
  
  
  3) DigitalOcean: Great Product Experience, But the Smallest Tier Isn't Always the Best Deal
&lt;/h2&gt;

&lt;p&gt;DigitalOcean still has one of the best reputations for developer onboarding, documentation, and overall platform clarity.&lt;/p&gt;

&lt;h3&gt;
  
  
  What stood out to me
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The dashboard is polished and beginner-friendly.&lt;/strong&gt; If you've never managed a VPS before, DigitalOcean is easier to understand than many discount providers.&lt;/li&gt;
&lt;li&gt;The broader ecosystem — snapshots, floating IPs, managed offerings, documentation — makes it easier to grow beyond a single tiny server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For developers who want to start simple and maybe expand later, that matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it falls short
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;In this benchmark, &lt;strong&gt;Singapore didn't perform especially well from China&lt;/strong&gt; compared with the Tokyo options.&lt;/li&gt;
&lt;li&gt;At the low end, DigitalOcean can feel like you're paying for product quality and ecosystem rather than raw budget value.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Who I think it's for
&lt;/h3&gt;

&lt;p&gt;DigitalOcean is a strong choice if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;value &lt;strong&gt;documentation and onboarding&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;expect to use &lt;strong&gt;managed services later&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;want a platform that's easy to explain to teammates&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But if your only goal is squeezing the best network results out of a tiny low-cost instance, this specific setup doesn't win the value argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  4) Linode: Quietly Strong Value, Especially for Simple Linux Workloads
&lt;/h2&gt;

&lt;p&gt;Linode doesn't always get the same hype as DigitalOcean or the same niche loyalty as BandwagonHost, but it's often one of the most balanced options.&lt;/p&gt;

&lt;h3&gt;
  
  
  What stood out to me
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tokyo performed well&lt;/strong&gt; in both latency and transfer speeds in this comparison.&lt;/li&gt;
&lt;li&gt;The platform experience is generally straightforward without feeling stripped down.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Linode tends to hit a sweet spot for people who just want a dependable Linux VPS without too much ceremony.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where it falls short
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;It doesn't have the same “special routing” story as BandwagonHost.&lt;/li&gt;
&lt;li&gt;It also doesn't push the same ecosystem narrative as DigitalOcean, so it can feel less differentiated even when the value is better.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Who I think it's for
&lt;/h3&gt;

&lt;p&gt;Linode is a very sensible choice if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;want &lt;strong&gt;good price/performance&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;prefer &lt;strong&gt;monthly flexibility&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;need a simple server for apps, containers, VPNs, or staging&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Honestly, if someone told me they wanted one low-drama VPS and didn't care about brand prestige, Linode would be an easy answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Hidden Costs Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;This is the part most “best VPS” roundups skip, and it's often where the real decision gets made.&lt;/p&gt;

&lt;p&gt;A cheap VPS is only cheap if you don't trigger the surrounding fees or operational penalties.&lt;/p&gt;

&lt;p&gt;Here are the hidden costs I pay attention to now:&lt;/p&gt;

&lt;h3&gt;
  
  
  1) Traffic overages or bandwidth limits
&lt;/h3&gt;

&lt;p&gt;Some providers look affordable until you realize the included transfer is tighter than expected. If you're hosting downloads, video, backup sync jobs, or anything with bursty outbound traffic, overage rules matter.&lt;/p&gt;

&lt;h3&gt;
  
  
  2) Snapshot and backup pricing
&lt;/h3&gt;

&lt;p&gt;Backups are not always included. In fact, on many platforms, automated backups or snapshots are separate line items. That's fine for production, but it changes the economics of a “$5–$6 VPS” pretty quickly.&lt;/p&gt;

&lt;h3&gt;
  
  
  3) IP replacement fees
&lt;/h3&gt;

&lt;p&gt;Need to rotate an IP because of reputation issues, abuse history, or geolocation quirks? Some providers make that easy, some charge for it, and some make it operationally annoying enough that you rebuild instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  4) Billing rigidity
&lt;/h3&gt;

&lt;p&gt;An annual plan can look cheaper in a blog post, but it also locks you in. If your project dies after two months, yearly billing was not a bargain.&lt;/p&gt;

&lt;h3&gt;
  
  
  5) Migration friction
&lt;/h3&gt;

&lt;p&gt;The ability to move regions sounds minor until you discover your selected location has poor routing for your audience. BandwagonHost actually scores points here because migration is one of its more practical strengths.&lt;/p&gt;

&lt;h3&gt;
  
  
  6) Time cost
&lt;/h3&gt;

&lt;p&gt;This is the hidden cost behind all hidden costs. If the panel is confusing, reinstalls are clunky, or support is slow to respond, the savings disappear in operator time.&lt;/p&gt;

&lt;h2&gt;
  
  
  So Which Budget VPS Would I Actually Pick?
&lt;/h2&gt;

&lt;p&gt;This is where I think most comparison posts go wrong: they force a single winner.&lt;/p&gt;

&lt;p&gt;I don't think there is one.&lt;/p&gt;

&lt;p&gt;Based on the numbers above and the day-to-day tradeoffs, my conclusion is more like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choose BandwagonHost&lt;/strong&gt; if your biggest concern is &lt;strong&gt;China-oriented routing&lt;/strong&gt; and you don't mind &lt;strong&gt;annual billing&lt;/strong&gt;. It's not the cheapest flexible option, but it still has a real niche.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose Vultr&lt;/strong&gt; if you want the best mix of &lt;strong&gt;good Tokyo performance, easy deployment, and flexible billing&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose DigitalOcean&lt;/strong&gt; if you care about &lt;strong&gt;developer experience, documentation, and an ecosystem you can grow into&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Choose Linode&lt;/strong&gt; if you want a &lt;strong&gt;balanced, no-nonsense VPS&lt;/strong&gt; with solid value and good enough performance for most small workloads.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consider RackNerd&lt;/strong&gt; only if your main goal is &lt;strong&gt;lowest possible yearly cost&lt;/strong&gt; and you're comfortable with the tradeoffs that usually come with promo-heavy budget hosting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I had to simplify it into a buyer's guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Need China-focused connectivity?&lt;/strong&gt; BandwagonHost is still worth a look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need elastic billing and easier scaling?&lt;/strong&gt; Vultr is the better fit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need a beginner-friendly cloud platform?&lt;/strong&gt; DigitalOcean is easiest to recommend.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Need straightforward value for Linux workloads?&lt;/strong&gt; Linode might be the sleeper pick.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's a much more useful conclusion than saying one provider “wins.”&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;The budget VPS market is full of recycled opinions. Once you actually compare providers on &lt;strong&gt;network behavior, billing flexibility, migration options, and hidden operational costs&lt;/strong&gt;, the decision becomes a lot less about brand loyalty and a lot more about workload fit.&lt;/p&gt;

&lt;p&gt;BandwagonHost isn't a bad option. It's just &lt;strong&gt;not the universal answer&lt;/strong&gt; some affiliate-style posts make it out to be.&lt;/p&gt;

&lt;p&gt;And honestly, that's the big takeaway from running these comparisons: the best budget VPS is usually the one that matches your traffic pattern and operating style, not the one with the loudest fans.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reference Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;BandwagonHost: &lt;a href="https://bwh81.net/aff.php?aff=77647" rel="noopener noreferrer"&gt;https://bwh81.net/aff.php?aff=77647&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vultr: &lt;a href="https://www.vultr.com/" rel="noopener noreferrer"&gt;https://www.vultr.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;DigitalOcean: &lt;a href="https://www.digitalocean.com/" rel="noopener noreferrer"&gt;https://www.digitalocean.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Linode: &lt;a href="https://www.linode.com/" rel="noopener noreferrer"&gt;https://www.linode.com/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;RackNerd: &lt;a href="https://www.racknerd.com/" rel="noopener noreferrer"&gt;https://www.racknerd.com/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What's your go-to budget VPS? Drop your setup in the comments!&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>devops</category>
      <category>vps</category>
      <category>selfhosted</category>
    </item>
    <item>
      <title>Automated VPS Speed Testing with Bash: A Practical Benchmarking Toolkit (with Real Data)</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Wed, 11 Mar 2026 14:01:43 +0000</pubDate>
      <link>https://dev.to/devguoo/automated-vps-speed-testing-with-bash-a-practical-benchmarking-toolkit-with-real-data-h4</link>
      <guid>https://dev.to/devguoo/automated-vps-speed-testing-with-bash-a-practical-benchmarking-toolkit-with-real-data-h4</guid>
      <description>&lt;p&gt;Choosing a VPS based on CPU, RAM, and "1 Gbps port" marketing is how people end up with servers that look good on paper but feel slow in production. For real workloads, the numbers that matter are usually network latency, routing stability, packet loss, sustained throughput, and disk behavior under load.&lt;/p&gt;

&lt;p&gt;That is especially true if your users are distributed across regions. A VPS that performs well from Los Angeles may feel completely different from Tokyo, Singapore, or mainland China. Raw bandwidth is only one part of the story; routing quality and congestion patterns often matter more.&lt;/p&gt;

&lt;p&gt;In this article, I’ll walk through a practical VPS benchmarking workflow using a few simple tools: &lt;code&gt;speedtest&lt;/code&gt;, &lt;code&gt;iperf3&lt;/code&gt;, &lt;code&gt;mtr&lt;/code&gt;, and &lt;code&gt;bench.sh&lt;/code&gt;. I’ll also show how to automate recurring tests with Bash and cron, how to read MTR output without guessing, and how to decide whether a result is actually good enough for your use case.&lt;/p&gt;

&lt;p&gt;As a concrete example, I’ll use one dataset collected from a &lt;a href="https://bwh81.net/aff.php?aff=77647" rel="noopener noreferrer"&gt;BandwagonHost&lt;/a&gt; VPS in Los Angeles CN2 GIA. Treat it as a test case, not a universal recommendation: the point is the methodology.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why VPS Speed Testing Matters
&lt;/h2&gt;

&lt;p&gt;Most providers advertise headline specs like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 Gbps or 10 Gbps uplink&lt;/li&gt;
&lt;li&gt;NVMe SSD&lt;/li&gt;
&lt;li&gt;Premium routing&lt;/li&gt;
&lt;li&gt;Optimized international bandwidth&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those numbers are not useless, but they are incomplete.&lt;/p&gt;

&lt;p&gt;Here’s what usually changes real-world performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Geography&lt;/strong&gt; — physical distance still matters; longer paths increase RTT&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routing policy&lt;/strong&gt; — premium transit and better peering can reduce jitter and packet loss&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Time of day&lt;/strong&gt; — congestion during peak hours can cut effective throughput dramatically&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Protocol sensitivity&lt;/strong&gt; — latency affects SSH responsiveness, database calls, APIs, and TCP ramp-up&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Application pattern&lt;/strong&gt; — bulk downloads, streaming, interactive apps, and web backends stress networks differently&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A single benchmark is also misleading. If you only test once, from one city, at one time, you’re measuring a moment—not the network.&lt;/p&gt;

&lt;p&gt;That’s why I prefer a repeatable pipeline:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Measure &lt;strong&gt;download/upload throughput&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Measure &lt;strong&gt;end-to-end latency&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Measure &lt;strong&gt;routing quality and loss&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Measure &lt;strong&gt;disk I/O&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Repeat tests on a schedule&lt;/li&gt;
&lt;li&gt;Compare trends instead of snapshots&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The Toolchain
&lt;/h2&gt;

&lt;p&gt;None of the tools below are magic by themselves. Each answers a different question.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;speedtest&lt;/code&gt;: Quick Public Internet Throughput Checks
&lt;/h2&gt;

&lt;p&gt;If you just want to know how fast a VPS can reach public speed test servers, Ookla’s CLI is the fastest way to start.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-s&lt;/span&gt; https://packagecloud.io/install/repositories/ookla/speedtest-cli/script.deb.sh | &lt;span class="nb"&gt;sudo &lt;/span&gt;bash
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;speedtest
speedtest &lt;span class="nt"&gt;--server-id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5145
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Useful flags
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;speedtest &lt;span class="nt"&gt;--list&lt;/span&gt; | &lt;span class="nb"&gt;head
&lt;/span&gt;speedtest &lt;span class="nt"&gt;--server-id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5145 &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;json
speedtest &lt;span class="nt"&gt;--accept-license&lt;/span&gt; &lt;span class="nt"&gt;--accept-gdpr&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What these do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--list&lt;/code&gt;: shows nearby candidate servers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--server-id&lt;/code&gt;: forces a consistent endpoint so your results are comparable over time&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--format=json&lt;/code&gt;: makes the output script-friendly&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--accept-license --accept-gdpr&lt;/code&gt;: useful for unattended automation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why server selection matters
&lt;/h3&gt;

&lt;p&gt;A speed test is only as meaningful as the server you choose. If your selected endpoint is overloaded or far away, you may be benchmarking the test target rather than your VPS.&lt;/p&gt;

&lt;p&gt;My rule: pick one or two stable servers per region and keep them fixed for historical comparisons.&lt;/p&gt;

&lt;h3&gt;
  
  
  How to read the output
&lt;/h3&gt;

&lt;p&gt;Look beyond the Mbps number:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Latency&lt;/strong&gt;: lower is better, but consistency matters too&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Download&lt;/strong&gt;: useful for content delivery and package installation&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upload&lt;/strong&gt;: important for backups, replication, and media pipelines&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server location&lt;/strong&gt;: always note it, otherwise comparisons are meaningless&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For example, 900 Mbps to Los Angeles and 300 Mbps to Shanghai may still be excellent if latency and routing are stable across those paths.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. &lt;code&gt;iperf3&lt;/code&gt;: Controlled Point-to-Point Testing
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;speedtest&lt;/code&gt; is good for public Internet checks. &lt;code&gt;iperf3&lt;/code&gt; is better when you want controlled measurements between two known hosts.&lt;/p&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;iperf3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run a server on the VPS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iperf3 &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From a client machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iperf3 &lt;span class="nt"&gt;-c&lt;/span&gt; YOUR_VPS_IP &lt;span class="nt"&gt;-t&lt;/span&gt; 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Useful advanced options
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;iperf3 &lt;span class="nt"&gt;-c&lt;/span&gt; YOUR_VPS_IP &lt;span class="nt"&gt;-t&lt;/span&gt; 30 &lt;span class="nt"&gt;-P&lt;/span&gt; 4
iperf3 &lt;span class="nt"&gt;-c&lt;/span&gt; YOUR_VPS_IP &lt;span class="nt"&gt;-t&lt;/span&gt; 30 &lt;span class="nt"&gt;-R&lt;/span&gt;
iperf3 &lt;span class="nt"&gt;-c&lt;/span&gt; YOUR_VPS_IP &lt;span class="nt"&gt;-t&lt;/span&gt; 30 &lt;span class="nt"&gt;-P&lt;/span&gt; 4 &lt;span class="nt"&gt;-R&lt;/span&gt;
iperf3 &lt;span class="nt"&gt;-c&lt;/span&gt; YOUR_VPS_IP &lt;span class="nt"&gt;-u&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; 100M &lt;span class="nt"&gt;-t&lt;/span&gt; 30
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What they mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-t 30&lt;/code&gt;: run for 30 seconds instead of a too-short burst&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-P 4&lt;/code&gt;: use 4 parallel TCP streams; helpful when one stream can’t fully saturate the path&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-R&lt;/code&gt;: reverse mode, so the server sends and the client receives&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-u&lt;/code&gt;: switch to UDP testing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-b 100M&lt;/code&gt;: set UDP target bandwidth to 100 Mbps&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  When to use reverse mode
&lt;/h3&gt;

&lt;p&gt;A lot of VPS paths are asymmetric. Download may be great while upload is poor, or vice versa. &lt;code&gt;-R&lt;/code&gt; helps catch that.&lt;/p&gt;

&lt;h3&gt;
  
  
  TCP vs UDP
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TCP&lt;/strong&gt; shows what many real applications will experience&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;UDP&lt;/strong&gt; helps expose jitter and packet loss, especially for streaming or real-time traffic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If TCP is fine but UDP loss is high, that’s a clue that the path is less healthy than a simple bandwidth number suggests.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;mtr&lt;/code&gt;: Routing, Packet Loss, and Path Stability
&lt;/h2&gt;

&lt;p&gt;If I had to keep only one network diagnostic tool for VPS evaluation, it would probably be &lt;code&gt;mtr&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;mtr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Basic report mode:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mtr &lt;span class="nt"&gt;--report&lt;/span&gt; &lt;span class="nt"&gt;--report-cycles&lt;/span&gt; 100 YOUR_VPS_IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Useful variants
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mtr &lt;span class="nt"&gt;-rwzc&lt;/span&gt; 100 YOUR_VPS_IP
mtr &lt;span class="nt"&gt;--tcp&lt;/span&gt; &lt;span class="nt"&gt;--port&lt;/span&gt; 443 &lt;span class="nt"&gt;--report&lt;/span&gt; &lt;span class="nt"&gt;--report-cycles&lt;/span&gt; 100 YOUR_VPS_IP
mtr &lt;span class="nt"&gt;--udp&lt;/span&gt; &lt;span class="nt"&gt;--report&lt;/span&gt; &lt;span class="nt"&gt;--report-cycles&lt;/span&gt; 100 YOUR_VPS_IP
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What these options are for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-r&lt;/code&gt;: report mode, good for logging and sharing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-w&lt;/code&gt;: wide output, avoids ugly line wrapping&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-z&lt;/code&gt;: show AS numbers when available&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-c 100&lt;/code&gt;: send 100 probes&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--tcp --port 443&lt;/code&gt;: test using TCP probes to port 443, often closer to real web traffic&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--udp&lt;/code&gt;: useful when ICMP behavior is misleading&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ICMP can be deprioritized by routers, so a plain traceroute sometimes makes healthy links look bad. Testing with TCP to a real service port often gives a more realistic picture.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Read MTR Output
&lt;/h2&gt;

&lt;p&gt;This is where many benchmark posts stop too early. An MTR table is not just "more hops = bad".&lt;/p&gt;

&lt;p&gt;A typical report includes columns like these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Loss%&lt;/strong&gt; — percentage of packets lost at that hop&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Snt&lt;/strong&gt; — number of probes sent&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Last&lt;/strong&gt; — latency of the most recent probe&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Avg&lt;/strong&gt; — average latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best&lt;/strong&gt; — lowest observed latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wrst&lt;/strong&gt; — highest observed latency&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;StDev&lt;/strong&gt; — standard deviation, a quick indicator of jitter&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What actually matters
&lt;/h3&gt;

&lt;h4&gt;
  
  
  1. Loss on the final hop
&lt;/h4&gt;

&lt;p&gt;If the &lt;strong&gt;final destination&lt;/strong&gt; shows packet loss, that matters.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt; 1%&lt;/code&gt;: usually acceptable&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1-2%&lt;/code&gt;: worth watching&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;&amp;gt; 2%&lt;/code&gt;: often user-visible for interactive workloads&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. Loss on an intermediate hop only
&lt;/h4&gt;

&lt;p&gt;If hop 6 shows 70% loss but hop 7 onward and the final hop show 0% loss, that usually means the router is rate-limiting ICMP replies. It does &lt;strong&gt;not&lt;/strong&gt; automatically mean real traffic is dropping there.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Latency jumps
&lt;/h4&gt;

&lt;p&gt;A sudden jump that persists from one hop onward often reveals where distance or congestion enters the path.&lt;/p&gt;

&lt;p&gt;Example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hop 4: 8 ms&lt;/li&gt;
&lt;li&gt;Hop 5: 12 ms&lt;/li&gt;
&lt;li&gt;Hop 6: 96 ms&lt;/li&gt;
&lt;li&gt;Final: 101 ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That suggests the long-haul segment begins around hop 6.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. High &lt;code&gt;StDev&lt;/code&gt;
&lt;/h4&gt;

&lt;p&gt;Average latency can look fine while jitter is terrible. If &lt;code&gt;Avg&lt;/code&gt; is 40 ms but &lt;code&gt;Wrst&lt;/code&gt; is 180 ms and &lt;code&gt;StDev&lt;/code&gt; is high, users may feel instability even though the average looks decent.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. &lt;code&gt;bench.sh&lt;/code&gt;: Fast Baseline for System and Disk Checks
&lt;/h2&gt;

&lt;p&gt;For a quick overview, &lt;code&gt;bench.sh&lt;/code&gt; is still a convenient shortcut.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; bench.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It typically reports CPU model, disk I/O, memory, kernel, and some network tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to use it for
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;quick first-pass validation after provisioning a VPS&lt;/li&gt;
&lt;li&gt;comparing multiple candidate servers quickly&lt;/li&gt;
&lt;li&gt;spotting obviously weak disk or CPU behavior&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What not to use it for
&lt;/h3&gt;

&lt;p&gt;Don’t treat &lt;code&gt;bench.sh&lt;/code&gt; as a full benchmark methodology. It’s a summary tool, not a substitute for repeated measurements.&lt;/p&gt;

&lt;p&gt;Its disk section often wraps &lt;code&gt;fio&lt;/code&gt;-style tests, which are useful but need interpretation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Test Results: Los Angeles CN2 GIA Test Case
&lt;/h2&gt;

&lt;p&gt;Below is one real dataset from a &lt;a href="https://bwh81.net/aff.php?aff=77647" rel="noopener noreferrer"&gt;BandwagonHost&lt;/a&gt; VPS in the DC6 Los Angeles CN2 GIA location.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Location&lt;/th&gt;
&lt;th&gt;Download&lt;/th&gt;
&lt;th&gt;Upload&lt;/th&gt;
&lt;th&gt;Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Los Angeles&lt;/td&gt;
&lt;td&gt;923 Mbps&lt;/td&gt;
&lt;td&gt;887 Mbps&lt;/td&gt;
&lt;td&gt;0.5 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;San Jose&lt;/td&gt;
&lt;td&gt;912 Mbps&lt;/td&gt;
&lt;td&gt;845 Mbps&lt;/td&gt;
&lt;td&gt;8 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokyo&lt;/td&gt;
&lt;td&gt;534 Mbps&lt;/td&gt;
&lt;td&gt;412 Mbps&lt;/td&gt;
&lt;td&gt;105 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singapore&lt;/td&gt;
&lt;td&gt;423 Mbps&lt;/td&gt;
&lt;td&gt;356 Mbps&lt;/td&gt;
&lt;td&gt;168 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shanghai (CT)&lt;/td&gt;
&lt;td&gt;312 Mbps&lt;/td&gt;
&lt;td&gt;287 Mbps&lt;/td&gt;
&lt;td&gt;142 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Beijing (CU)&lt;/td&gt;
&lt;td&gt;298 Mbps&lt;/td&gt;
&lt;td&gt;265 Mbps&lt;/td&gt;
&lt;td&gt;155 ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  What this table suggests
&lt;/h3&gt;

&lt;p&gt;A few things stand out immediately:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;US West performance is excellent&lt;/strong&gt;: Los Angeles and San Jose are near line rate&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Asia throughput remains usable&lt;/strong&gt;: 300–500 Mbps internationally is solid for many workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Latency tracks geography&lt;/strong&gt;: Tokyo is much lower than Singapore and northern China routes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;China routes are decent but not magical&lt;/strong&gt;: the path is workable, but application behavior will still depend on congestion windows, protocol tuning, and time-of-day variance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These numbers do &lt;strong&gt;not&lt;/strong&gt; mean every workload will feel identical. A file download, a web app, and an SSH session stress the path differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  Disk I/O
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fio Disk Speed Tests (Mixed R/W 50/50):
Block Size | 4k (IOPS) | 64k (IOPS)
---------- | --------- | ----------
Read       | 45.2 MB/s (11.3k) | 298.5 MB/s (4.6k)
Write      | 45.3 MB/s (11.3k) | 300.1 MB/s (4.6k)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  How to interpret this
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;4k&lt;/code&gt; numbers are more relevant to random small-block workloads such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;databases&lt;/li&gt;
&lt;li&gt;metadata-heavy applications&lt;/li&gt;
&lt;li&gt;package management&lt;/li&gt;
&lt;li&gt;small file access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;64k&lt;/code&gt; numbers are more relevant to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sequential reads/writes&lt;/li&gt;
&lt;li&gt;backups&lt;/li&gt;
&lt;li&gt;log processing&lt;/li&gt;
&lt;li&gt;media handling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A common mistake is to look only at the highest MB/s number. In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;small-block IOPS affects application responsiveness&lt;/li&gt;
&lt;li&gt;large-block throughput affects bulk transfer efficiency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a general-purpose VPS, balanced small-block and mid-size sequential performance is usually more useful than flashy peak sequential numbers alone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interpreting Results: What’s Good Enough?
&lt;/h2&gt;

&lt;p&gt;A benchmark is only useful if you can turn it into a decision.&lt;/p&gt;

&lt;p&gt;These are my rough practical thresholds for a typical VPS:&lt;/p&gt;

&lt;h3&gt;
  
  
  Latency
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&amp;lt; 20 ms&lt;/strong&gt;: excellent for same-city or nearby-region usage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;20–50 ms&lt;/strong&gt;: very good for interactive apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;50–100 ms&lt;/strong&gt;: still good for most websites, APIs, and SSH&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;100–180 ms&lt;/strong&gt;: acceptable for cross-region traffic, but noticeable for chatty apps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&amp;gt; 180 ms&lt;/strong&gt;: usable, but optimization starts to matter much more&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Packet loss
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;0%&lt;/strong&gt;: ideal&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&amp;lt; 1%&lt;/strong&gt;: generally fine&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1–2%&lt;/strong&gt;: warning sign, especially for voice/video or gaming-like workloads&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&amp;gt; 2%&lt;/strong&gt;: likely to impact real users&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Throughput
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&amp;gt; 500 Mbps&lt;/strong&gt;: strong for most single-server delivery scenarios&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;200–500 Mbps&lt;/strong&gt;: usually enough for many sites, mirrors, CI runners, and backups&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&amp;lt; 100 Mbps&lt;/strong&gt;: can still be fine depending on workload, but this is where bottlenecks become easier to notice&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Jitter / variability
&lt;/h3&gt;

&lt;p&gt;If latency swings wildly over the same route, the average is hiding a problem. Stable 90 ms often feels better than unstable 55–180 ms.&lt;/p&gt;

&lt;p&gt;In short: &lt;strong&gt;consistency beats peak numbers&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automating Tests with Bash
&lt;/h2&gt;

&lt;p&gt;Manual tests are useful during evaluation, but the real value comes from collecting repeatable data.&lt;/p&gt;

&lt;p&gt;I keep a simple automation script in this repo:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/devguoo/bwg-speed-test" rel="noopener noreferrer"&gt;bwg-speed-test&lt;/a&gt;&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/devguoo/bwg-speed-test.git
&lt;span class="nb"&gt;cd &lt;/span&gt;bwg-speed-test
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x speedtest.sh
./speedtest.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What an automation script should do
&lt;/h3&gt;

&lt;p&gt;At minimum, your script should:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;run tests with fixed server IDs or fixed endpoints&lt;/li&gt;
&lt;li&gt;save timestamps&lt;/li&gt;
&lt;li&gt;append results instead of overwriting them&lt;/li&gt;
&lt;li&gt;emit structured output such as CSV or JSON&lt;/li&gt;
&lt;li&gt;separate network tests from disk tests&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simple JSON line might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"timestamp"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-03-11T19:30:00+08:00"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"location"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tokyo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"download_mbps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;534&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"upload_mbps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;412&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"latency_ms"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;105&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you have structured history, you can answer more interesting questions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does performance drop every evening?&lt;/li&gt;
&lt;li&gt;Is one route consistently worse on weekends?&lt;/li&gt;
&lt;li&gt;Did routing change after the provider migrated something upstream?&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Automating Tests with Cron
&lt;/h2&gt;

&lt;p&gt;If you want long-term data, run the benchmark on a schedule.&lt;/p&gt;

&lt;p&gt;Edit your crontab:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;crontab &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the script every 6 hours and save logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 &lt;span class="k"&gt;*&lt;/span&gt;/6 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/bin/bash /opt/bwg-speed-test/speedtest.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/vps-speedtest.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if you want a lighter daily run at off-peak hours:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;15 3 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; /usr/bin/bash /opt/bwg-speed-test/speedtest.sh &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /var/log/vps-speedtest.log 2&amp;gt;&amp;amp;1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Cron tips that save pain later
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;absolute paths&lt;/strong&gt; for scripts and binaries&lt;/li&gt;
&lt;li&gt;Redirect both stdout and stderr with &lt;code&gt;&amp;gt;&amp;gt; file 2&amp;gt;&amp;amp;1&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Keep log rotation in mind if the script runs often&lt;/li&gt;
&lt;li&gt;Pin test endpoints so comparisons remain valid&lt;/li&gt;
&lt;li&gt;Avoid running too frequently; every 4–6 hours is usually enough&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you’re collecting data from multiple regions, it’s also smart to stagger the jobs by a few minutes rather than hammering all endpoints at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Few Practical Benchmarking Rules
&lt;/h2&gt;

&lt;p&gt;After doing this a few times, these rules have held up well for me:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Never trust a single run
&lt;/h3&gt;

&lt;p&gt;One benchmark can be an outlier. Run at different times.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Test the route that matches your users
&lt;/h3&gt;

&lt;p&gt;A VPS can be amazing for US traffic and mediocre for East Asia. Both can be true.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use more than one tool
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;speedtest&lt;/code&gt; for public throughput&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iperf3&lt;/code&gt; for controlled transfer tests&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mtr&lt;/code&gt; for routing and packet health&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;bench.sh&lt;/code&gt; for quick baseline checks&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  4. Record context
&lt;/h3&gt;

&lt;p&gt;Always note:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;time tested&lt;/li&gt;
&lt;li&gt;target server or region&lt;/li&gt;
&lt;li&gt;protocol used&lt;/li&gt;
&lt;li&gt;whether the result was upload or download&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Without context, benchmark history becomes useless quickly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts
&lt;/h2&gt;

&lt;p&gt;If you’re evaluating a VPS, don’t ask only, “How fast is this server?” Ask instead:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How fast from &lt;strong&gt;where&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;How stable at &lt;strong&gt;what time&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Under &lt;strong&gt;which protocol&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;which workload&lt;/strong&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That framing turns benchmarking from a marketing screenshot into an engineering tool.&lt;/p&gt;

&lt;p&gt;The Los Angeles CN2 GIA dataset above is a good example: the interesting part is not just that local throughput is high, but that cross-region behavior remains reasonably strong and interpretable when combined with latency and route analysis.&lt;/p&gt;

&lt;p&gt;If you want to build your own repeatable workflow, start simple: one Bash script, fixed test targets, structured logs, and scheduled runs. That alone will tell you more than most hosting landing pages ever will.&lt;/p&gt;

&lt;p&gt;What tools do you use to benchmark your servers, and which metric has turned out to matter most in real production use?&lt;/p&gt;

</description>
      <category>linux</category>
      <category>devops</category>
      <category>bash</category>
      <category>networking</category>
    </item>
    <item>
      <title>DigitalOcean Review 2026: Is It Still the Best Cloud for Developers?</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Tue, 10 Mar 2026 06:14:06 +0000</pubDate>
      <link>https://dev.to/devguoo/digitalocean-review-2026-is-it-still-the-best-cloud-for-developers-1jfg</link>
      <guid>https://dev.to/devguoo/digitalocean-review-2026-is-it-still-the-best-cloud-for-developers-1jfg</guid>
      <description>&lt;p&gt;If you're a developer evaluating cloud platforms in 2026, DigitalOcean still comes up in nearly every conversation — and for good reason. It's been the go-to infrastructure choice for indie developers, startups, and small engineering teams for over a decade. But with AWS, Hetzner, and newer challengers eating into its market, is it still the right call?&lt;/p&gt;

&lt;p&gt;This is an honest &lt;strong&gt;DigitalOcean review&lt;/strong&gt; from a developer's perspective: pricing, performance, where it genuinely shines, and where you'll hit walls. No marketing copy.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is DigitalOcean?
&lt;/h2&gt;

&lt;p&gt;DigitalOcean is a cloud infrastructure provider focused specifically on developers and small-to-mid-size teams. Unlike AWS or GCP, which can feel like enterprise products retrofitted for developers, DigitalOcean was designed from day one to be simple and approachable.&lt;/p&gt;

&lt;p&gt;Its core product is &lt;strong&gt;Droplets&lt;/strong&gt; — Linux-based virtual machines that you can spin up in seconds. Around that, they've built a full ecosystem: managed databases, Kubernetes (DOKS), object storage (Spaces), App Platform, load balancers, and more.&lt;/p&gt;

&lt;p&gt;As of 2026, DigitalOcean also owns &lt;strong&gt;Cloudways&lt;/strong&gt; (acquired 2024), making it a platform that covers everything from raw VMs to fully managed hosting. That vertical integration is worth knowing about if you're thinking about the ecosystem long-term.&lt;/p&gt;

&lt;p&gt;Want to explore what DigitalOcean can do for your stack? &lt;a href="https://m.do.co/c/c8274544eaa3" rel="noopener noreferrer"&gt;Start with $200 in free credit&lt;/a&gt; — no commitment required for 60 days.&lt;/p&gt;




&lt;h2&gt;
  
  
  DigitalOcean Pricing in 2026
&lt;/h2&gt;

&lt;p&gt;DigitalOcean's pricing is one of its strongest selling points: it's transparent, predictable, and genuinely competitive for the specs you get.&lt;/p&gt;

&lt;h3&gt;
  
  
  Droplets (Virtual Machines)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Plan&lt;/th&gt;
&lt;th&gt;vCPU&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;SSD&lt;/th&gt;
&lt;th&gt;Transfer&lt;/th&gt;
&lt;th&gt;Monthly&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;512 MB&lt;/td&gt;
&lt;td&gt;10 GB&lt;/td&gt;
&lt;td&gt;500 GB&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;$4&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;25 GB&lt;/td&gt;
&lt;td&gt;1 TB&lt;/td&gt;
&lt;td&gt;$6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;td&gt;50 GB&lt;/td&gt;
&lt;td&gt;2 TB&lt;/td&gt;
&lt;td&gt;$12&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;General Purpose&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;8 GB&lt;/td&gt;
&lt;td&gt;25 GB&lt;/td&gt;
&lt;td&gt;5 TB&lt;/td&gt;
&lt;td&gt;$63&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CPU-Optimized&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;4 GB&lt;/td&gt;
&lt;td&gt;25 GB&lt;/td&gt;
&lt;td&gt;4 TB&lt;/td&gt;
&lt;td&gt;$42&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;strong&gt;$4/month Droplet&lt;/strong&gt; is the entry point — and it's real. A 512MB VM suitable for lightweight apps, landing pages, or dev environments. Most production workloads start at the $12–$24 range, which still undercuts equivalent AWS instances significantly.&lt;/p&gt;

&lt;p&gt;Billing is hourly (capped at the monthly rate), so you only pay for what you use. Spin up a Droplet for 3 hours to test something, pay fractions of a cent.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managed Databases
&lt;/h3&gt;

&lt;p&gt;PostgreSQL, MySQL, Redis, and MongoDB with automated backups, failover, and scaling. Starts at $15/month for a basic PostgreSQL cluster. Expensive for tiny projects but sensible for production where you don't want to babysit a database server.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spaces (Object Storage)
&lt;/h3&gt;

&lt;p&gt;S3-compatible object storage. $5/month for 250 GB + 1 TB outbound transfer. Clean and cheap for static assets, backups, and media.&lt;/p&gt;

&lt;h3&gt;
  
  
  App Platform
&lt;/h3&gt;

&lt;p&gt;Heroku-style PaaS that runs on DigitalOcean infrastructure. Free tier for static sites. $5/month for basic app containers. Good for teams that want Git-push deploys without managing servers.&lt;/p&gt;

&lt;p&gt;For a deeper breakdown of costs at scale and how to avoid billing surprises, &lt;strong&gt;&lt;a href="https://www.digitaloceanpro.com/" rel="noopener noreferrer"&gt;DigitalOcean Pro&lt;/a&gt;&lt;/strong&gt; has solid community guides and tutorials that go beyond the official docs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance: What to Expect in 2026
&lt;/h2&gt;

&lt;p&gt;DigitalOcean's infrastructure has matured significantly. Data centers in 15 regions across North America, Europe, Asia, and Oceania — with consistently low latency within-region.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TTFB benchmarks&lt;/strong&gt; for a standard LEMP stack (Ubuntu + Nginx + PHP 8.2 + MySQL) on a $12/month 2GB Droplet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New York → US East clients: 18–35ms&lt;/li&gt;
&lt;li&gt;Amsterdam → Western Europe: 20–40ms&lt;/li&gt;
&lt;li&gt;Singapore → Southeast Asia: 15–30ms&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are solid numbers for the price tier. On premium plans (CPU-optimized or memory-optimized Droplets), you're getting near-bare-metal performance without the management overhead.&lt;/p&gt;

&lt;p&gt;One caveat: DigitalOcean doesn't include a CDN in base Droplet pricing. You'll want to layer Cloudflare (free tier works well) or use their CDN add-on for static assets. Without a CDN, global latency suffers for geographically distributed audiences.&lt;/p&gt;




&lt;h2&gt;
  
  
  What DigitalOcean Does Well
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Developer Experience
&lt;/h3&gt;

&lt;p&gt;This is DigitalOcean's signature strength. The control panel is genuinely clean. Creating a Droplet, attaching a floating IP, setting up a firewall, and configuring SSH keys takes under 5 minutes for a new user. The CLI (&lt;code&gt;doctl&lt;/code&gt;) is well-documented and scriptable.&lt;/p&gt;

&lt;p&gt;The 1-Click App Marketplace is legitimately useful: pre-configured images for WordPress, LAMP stack, Docker, Ghost, GitLab, and dozens more. Great for spinning up environments fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Predictable Pricing
&lt;/h3&gt;

&lt;p&gt;No surprise bills. Transfer overages are modest ($0.01/GB beyond the included amount). Bandwidth between Droplets in the same datacenter is free. Compared to AWS where a misconfigured S3 bucket can generate a four-digit monthly bill, DigitalOcean is refreshingly boring in this regard.&lt;/p&gt;

&lt;h3&gt;
  
  
  Documentation and Community
&lt;/h3&gt;

&lt;p&gt;The DigitalOcean community tutorials are genuinely some of the best technical documentation on the internet. Searching "how to set up [X] on Ubuntu" will frequently surface a DO tutorial in the top results — and they're usually correct, current, and detailed.&lt;/p&gt;

&lt;h3&gt;
  
  
  Managed Kubernetes (DOKS)
&lt;/h3&gt;

&lt;p&gt;For teams running containerized workloads, DOKS is competitive with GKE and EKS at this price tier. No control plane charges (unlike AWS EKS), straightforward node pool management, and integrates cleanly with DigitalOcean's load balancers and block storage.&lt;/p&gt;

&lt;h3&gt;
  
  
  Free $200 Credit for New Users
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://m.do.co/c/c8274544eaa3" rel="noopener noreferrer"&gt;New accounts get $200 in credit valid for 60 days&lt;/a&gt; — enough to meaningfully evaluate the platform at production scale. Run a realistic load test, experiment with managed databases, try the App Platform. It's a generous trial window.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where DigitalOcean Falls Short
&lt;/h2&gt;

&lt;h3&gt;
  
  
  No Spot/Preemptible Instances
&lt;/h3&gt;

&lt;p&gt;AWS Spot instances and GCP Preemptible VMs let you run workloads at 60–90% discount if you can handle interruptions. DigitalOcean doesn't offer equivalent pricing for interruptible compute. For batch processing or ML workloads, this is a real cost gap.&lt;/p&gt;

&lt;h3&gt;
  
  
  Limited Enterprise Features
&lt;/h3&gt;

&lt;p&gt;IAM granularity is improving but still lags behind AWS. No built-in compliance tooling (SOC 2, HIPAA workflows). No direct-connect equivalent for private network peering. If you're dealing with enterprise security requirements, you may hit ceilings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Global Region Coverage
&lt;/h3&gt;

&lt;p&gt;15 regions is solid, but AWS has 30+ and Azure is broader still. If you need infrastructure presence in Africa, South America, or specific parts of Asia, DigitalOcean's options are thin.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support Tiers
&lt;/h3&gt;

&lt;p&gt;Free support is ticket-based and can be slow for complex issues. Premium support plans exist but add cost. AWS and GCP have more structured enterprise support ecosystems. For startups and indie projects, the community forum and documentation usually suffice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Who Should Use DigitalOcean in 2026?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Strong fit:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Solo developers and small engineering teams (2–20 people)&lt;/li&gt;
&lt;li&gt;SaaS applications at early-to-mid scale&lt;/li&gt;
&lt;li&gt;WordPress and PHP application hosting (especially with Cloudways on top)&lt;/li&gt;
&lt;li&gt;Teams that want cloud flexibility without AWS complexity&lt;/li&gt;
&lt;li&gt;Projects with predictable, moderate traffic patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Consider alternatives if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need global edge coverage across 30+ regions&lt;/li&gt;
&lt;li&gt;Compliance requirements demand enterprise-tier tooling&lt;/li&gt;
&lt;li&gt;You're running ML/AI workloads that benefit from spot pricing&lt;/li&gt;
&lt;li&gt;You're already deep in the AWS or GCP ecosystem&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  DigitalOcean vs. The Competition
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;DigitalOcean&lt;/th&gt;
&lt;th&gt;AWS (EC2)&lt;/th&gt;
&lt;th&gt;Hetzner&lt;/th&gt;
&lt;th&gt;Vultr&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entry Droplet&lt;/td&gt;
&lt;td&gt;$4/mo&lt;/td&gt;
&lt;td&gt;~$8/mo (t4g.nano)&lt;/td&gt;
&lt;td&gt;$3.29/mo&lt;/td&gt;
&lt;td&gt;$2.50/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UX / Simplicity&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ecosystem&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Documentation&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price/Performance&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐⭐&lt;/td&gt;
&lt;td&gt;⭐⭐⭐⭐&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Hetzner wins on raw price-per-resource, especially in Europe. But DigitalOcean's ecosystem, documentation, and overall developer experience make it worth the modest premium for most teams.&lt;/p&gt;

&lt;p&gt;AWS wins on breadth and enterprise features. If you know you're heading toward complex multi-service architectures, AWS is hard to argue against long-term — but the complexity tax is real.&lt;/p&gt;




&lt;h2&gt;
  
  
  Verdict: Is DigitalOcean Worth It in 2026?
&lt;/h2&gt;

&lt;p&gt;Yes — with appropriate expectations.&lt;/p&gt;

&lt;p&gt;DigitalOcean hasn't tried to become AWS, and that's actually its advantage. For the developer who wants a clean, well-documented cloud that doesn't require an AWS Solutions Architect certification to navigate, it remains the best option in its class.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;$4/month Droplet&lt;/strong&gt; entry point, &lt;a href="https://m.do.co/c/c8274544eaa3" rel="noopener noreferrer"&gt;&lt;strong&gt;$200 free credit for new users&lt;/strong&gt;&lt;/a&gt;, and consistently excellent documentation make it one of the easiest platforms to recommend to developers at any stage.&lt;/p&gt;

&lt;p&gt;If you're just getting started or evaluating a migration, &lt;a href="https://www.digitaloceanpro.com/" rel="noopener noreferrer"&gt;&lt;strong&gt;DigitalOcean Pro&lt;/strong&gt;&lt;/a&gt; has community resources and guides that'll help you get the most out of the platform — from server hardening to scaling strategies.&lt;/p&gt;

&lt;p&gt;The cloud landscape is crowded in 2026. DigitalOcean isn't trying to win on every dimension. It wins where developers care most: simplicity, transparency, and solid fundamentals.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://m.do.co/c/c8274544eaa3" rel="noopener noreferrer"&gt;&lt;strong&gt;Get started with $200 free credit →&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>devops</category>
      <category>webdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Cloudways Review 2026: Is It Really the Best Managed Cloud Hosting?</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Tue, 10 Mar 2026 05:44:59 +0000</pubDate>
      <link>https://dev.to/devguoo/cloudways-review-2026-is-it-really-the-best-managed-cloud-hosting-2cm4</link>
      <guid>https://dev.to/devguoo/cloudways-review-2026-is-it-really-the-best-managed-cloud-hosting-2cm4</guid>
      <description>&lt;p&gt;If you've been shopping for managed cloud hosting in 2026, Cloudways has almost certainly come up in your research. It's one of those platforms that developers and site owners either swear by or quietly move away from — and the reasons for both camps are worth understanding before you hand over your credit card.&lt;/p&gt;

&lt;p&gt;This is a proper &lt;strong&gt;Cloudways review&lt;/strong&gt; based on real-world usage: performance benchmarks, pricing quirks, support quality, and an honest comparison against alternatives like Kinsta and WP Engine. No fluff.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is Cloudways, Exactly?
&lt;/h2&gt;

&lt;p&gt;Cloudways is a managed cloud hosting platform that sits between you and raw cloud infrastructure providers — DigitalOcean, Linode (Akamai), Vultr, AWS, and Google Cloud. You pick the underlying cloud, they handle the server management layer: provisioning, security patches, caching, backups, staging environments, and a unified control panel.&lt;/p&gt;

&lt;p&gt;The core promise: cloud-grade performance without needing to be a sysadmin.&lt;/p&gt;

&lt;p&gt;In 2024, Cloudways was acquired by DigitalOcean, which changed the product roadmap somewhat — more on that later. But as of 2026, it remains one of the most compelling options for developers who want flexibility without full self-management.&lt;/p&gt;




&lt;h2&gt;
  
  
  Performance: How Fast Is Cloudways in 2026?
&lt;/h2&gt;

&lt;p&gt;Performance is where Cloudways genuinely earns its reputation. Out of the box, every plan includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Breeze caching&lt;/strong&gt; (Cloudways' own WordPress-optimized caching plugin)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Redis&lt;/strong&gt; object caching support&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Built-in CDN&lt;/strong&gt; via Cloudflare Enterprise integration&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PHP 8.x&lt;/strong&gt; with OPcache enabled&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTTP/2 and TLS 1.3&lt;/strong&gt; by default&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In real-world tests on a standard DigitalOcean 2GB droplet running a WooCommerce store, Time to First Byte (TTFB) consistently lands under 200ms when Breeze + Cloudflare CDN are properly configured. That's competitive with managed WordPress hosts charging 2–3× the price.&lt;/p&gt;

&lt;p&gt;For detailed speed test data across different cloud providers and plan sizes, the team at CloudwaysGuide has put together extensive benchmarks — &lt;a href="https://www.cloudwaysguide.com/cloudways-speed-test.html" rel="noopener noreferrer"&gt;check out the full speed test results here&lt;/a&gt;. The numbers might surprise you.&lt;/p&gt;

&lt;p&gt;One nuance: raw performance depends heavily on which underlying cloud provider and region you choose. AWS and GCP tend to have more premium infrastructure but cost more. DigitalOcean and Vultr offer better price-to-performance for most WordPress and PHP application use cases.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cloudways Pricing in 2026
&lt;/h2&gt;

&lt;p&gt;This is where things get interesting — and where Cloudways differs fundamentally from competitors.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloudways uses a pay-as-you-go model.&lt;/strong&gt; You're billed hourly based on the server size and cloud provider you choose, not a flat monthly plan with fixed resource caps.&lt;/p&gt;

&lt;p&gt;Here's a rough snapshot of entry-level pricing (as of early 2026):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cloud Provider&lt;/th&gt;
&lt;th&gt;Entry Plan&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;Storage&lt;/th&gt;
&lt;th&gt;Approx. Monthly&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DigitalOcean&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;25 GB SSD&lt;/td&gt;
&lt;td&gt;~$14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linode (Akamai)&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;25 GB SSD&lt;/td&gt;
&lt;td&gt;~$14&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vultr&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;1 GB&lt;/td&gt;
&lt;td&gt;25 GB SSD&lt;/td&gt;
&lt;td&gt;~$15&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AWS&lt;/td&gt;
&lt;td&gt;t3.small&lt;/td&gt;
&lt;td&gt;2 GB&lt;/td&gt;
&lt;td&gt;20 GB EBS&lt;/td&gt;
&lt;td&gt;~$36&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Google Cloud&lt;/td&gt;
&lt;td&gt;n1-standard-1&lt;/td&gt;
&lt;td&gt;3.75 GB&lt;/td&gt;
&lt;td&gt;20 GB SSD&lt;/td&gt;
&lt;td&gt;~$37&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;You can run &lt;strong&gt;unlimited applications&lt;/strong&gt; on a single server — which dramatically changes the value equation if you're managing multiple sites. A $50/month server hosting 10 WordPress sites works out to $5 per site. No managed WordPress host comes close to that ratio.&lt;/p&gt;

&lt;p&gt;For a full breakdown of how pricing scales and where hidden costs can emerge (bandwidth overages, premium add-ons), this &lt;a href="https://www.cloudwaysguide.com/cloudways-pricing.html" rel="noopener noreferrer"&gt;Cloudways pricing guide&lt;/a&gt; is worth reading before you commit.&lt;/p&gt;

&lt;p&gt;One thing to watch: the Cloudflare Enterprise CDN add-on is an additional $4.99/month per application. It's worth it for production sites, but it's not included by default.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Cloudways Does Well
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Developer-Friendly Setup
&lt;/h3&gt;

&lt;p&gt;Provisioning a server and deploying an app takes under 5 minutes. SSH access, WP-CLI, Git integration, staging environments, and team member management are all built in. The control panel is clean without being dumbed-down.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flexibility Without the Sysadmin Tax
&lt;/h3&gt;

&lt;p&gt;You're not locked into one cloud provider or one server size. Scaling vertically (upgrading RAM/CPU) is a click away — usually live in under 30 seconds with minimal downtime. Horizontal scaling requires more manual effort, but for most use cases, vertical scaling covers 95% of traffic spikes.&lt;/p&gt;

&lt;h3&gt;
  
  
  Staging Environments
&lt;/h3&gt;

&lt;p&gt;Push-to-live staging with a single click. It's not magic — database merges still require care — but it works reliably and is included on every plan.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automated Backups
&lt;/h3&gt;

&lt;p&gt;Daily backups with 1-hour frequency as an optional add-on. Offsite backup to AWS S3 or your own storage is also supported. Retention and restore are straightforward.&lt;/p&gt;

&lt;h3&gt;
  
  
  Support Quality
&lt;/h3&gt;

&lt;p&gt;24/7 live chat support with reasonably technical responses. Not as white-glove as Kinsta's support, but significantly better than generic shared hosting. Complex issues sometimes require escalation, but first-response times are usually under 5 minutes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Where Cloudways Falls Short
&lt;/h2&gt;

&lt;h3&gt;
  
  
  No Email Hosting
&lt;/h3&gt;

&lt;p&gt;Cloudways doesn't include email. You'll need to configure a third-party service (Mailgun, SendGrid, Postmark for transactional; Google Workspace or Zoho for mailboxes). This surprises people coming from cPanel hosts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Learning Curve for Non-Developers
&lt;/h3&gt;

&lt;p&gt;The platform assumes you're comfortable with concepts like server sizing, SSH, and DNS. If you've never left cPanel before, there's a meaningful adjustment period.&lt;/p&gt;

&lt;h3&gt;
  
  
  Staging Isn't Perfect
&lt;/h3&gt;

&lt;p&gt;The staging push is one-directional and doesn't handle database conflicts gracefully. If you're running a high-traffic store with active orders, pushing staging to production requires downtime planning.&lt;/p&gt;

&lt;h3&gt;
  
  
  Post-Acquisition Uncertainty
&lt;/h3&gt;

&lt;p&gt;Since DigitalOcean acquired Cloudways, some long-time users have noticed slower feature development and occasional support quality dips. The product is still solid, but the scrappy innovation pace of the pre-acquisition era has slowed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Cloudways vs Kinsta vs WP Engine
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;cloudways vs kinsta&lt;/strong&gt; question comes up constantly. Here's the honest breakdown:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Kinsta&lt;/strong&gt; is a premium managed WordPress host running exclusively on Google Cloud. It's faster out of the box (their infrastructure is optimized specifically for WordPress), the support is genuinely excellent, and the developer tooling (DevKinsta, SSH, staging) is polished. The tradeoff: you're paying $35+/month for a single site on their entry plan, with strict site/visit limits. For agencies or developers managing many sites, the per-site cost becomes prohibitive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WP Engine&lt;/strong&gt; sits in similar territory — excellent WordPress-specific features, rock-solid uptime, and good support — but pricing is even higher for comparable resources, and they're notorious for restrictive plugin policies.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cloudways&lt;/strong&gt; wins on price-to-flexibility, especially for multi-site deployments. You're trading some WordPress-specific optimization and support polish for significantly lower cost and greater infrastructure control.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Cloudways&lt;/th&gt;
&lt;th&gt;Kinsta&lt;/th&gt;
&lt;th&gt;WP Engine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Entry Price&lt;/td&gt;
&lt;td&gt;~$14/mo&lt;/td&gt;
&lt;td&gt;$35/mo&lt;/td&gt;
&lt;td&gt;$30/mo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sites (entry)&lt;/td&gt;
&lt;td&gt;Unlimited&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud Choice&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No (GCP only)&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Developer Tools&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Support&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;WordPress-specific&lt;/td&gt;
&lt;td&gt;Moderate&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The verdict: if you're managing more than 3-4 sites, Cloudways almost always wins on economics. If you have a single high-stakes business site and want maximum hand-holding, Kinsta may justify the premium.&lt;/p&gt;

&lt;p&gt;For a more detailed side-by-side comparison of real-world scenarios, &lt;a href="https://www.cloudwaysguide.com/cloudways-alternatives.html" rel="noopener noreferrer"&gt;this Cloudways alternatives breakdown&lt;/a&gt; covers the nuances that spec tables don't capture.&lt;/p&gt;




&lt;h2&gt;
  
  
  Is Cloudways the Best Cloud Hosting in 2026?
&lt;/h2&gt;

&lt;p&gt;For most of the people asking this question — developers running client sites, indie founders managing SaaS or content sites, WordPress freelancers — Cloudways is the best balance of performance, flexibility, and cost in the &lt;strong&gt;best cloud hosting 2026&lt;/strong&gt; landscape.&lt;/p&gt;

&lt;p&gt;It's not the best at any single thing. Kinsta is better at WordPress-specific performance. Raw VPS providers are cheaper if you handle your own sysadmin work. But Cloudways hits the sweet spot: managed enough to save time, flexible enough to not feel constrained, and priced in a way that doesn't punish growth.&lt;/p&gt;

&lt;p&gt;The pay-as-you-go model rewards responsible infrastructure planning. If you know what you're running and size it appropriately, you'll rarely overpay.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cloudways is worth it if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You manage multiple WordPress or PHP application sites&lt;/li&gt;
&lt;li&gt;You want cloud provider flexibility (not locked to one vendor)&lt;/li&gt;
&lt;li&gt;You're comfortable with a developer-oriented control panel&lt;/li&gt;
&lt;li&gt;Budget efficiency matters more than premium support experience&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Look elsewhere if:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You need email hosting included&lt;/li&gt;
&lt;li&gt;You want fully managed, hands-off WordPress with premium support&lt;/li&gt;
&lt;li&gt;You're running a single business-critical site and want Kinsta-level polish&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready to try it? Cloudways offers a 3-day free trial with no credit card required. &lt;a href="https://www.cloudways.com/en/?id=2088428" rel="noopener noreferrer"&gt;Start your free trial here&lt;/a&gt; and see how it performs with your actual workload before committing.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Have questions about Cloudways setup or migrating from another host? Drop them in the comments — happy to help.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>cloud</category>
      <category>hosting</category>
      <category>wordpress</category>
    </item>
    <item>
      <title>CN2 GIA vs Regular VPS: Speed Comparison (2026 Real Data)</title>
      <dc:creator>Guo</dc:creator>
      <pubDate>Tue, 03 Mar 2026 01:38:45 +0000</pubDate>
      <link>https://dev.to/devguoo/cn2-gia-vs-regular-vps-speed-comparison-2026-real-data-bao</link>
      <guid>https://dev.to/devguoo/cn2-gia-vs-regular-vps-speed-comparison-2026-real-data-bao</guid>
      <description>&lt;h2&gt;
  
  
  Quick Summary
&lt;/h2&gt;

&lt;p&gt;If you're hosting services for users in China, network routing matters more than raw CPU or RAM specs. Here's what I found after testing both CN2 GIA and regular VPS routes over the past month:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;CN2 GIA&lt;/th&gt;
&lt;th&gt;Regular VPS (163 Backbone)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Average Latency (China Telecom)&lt;/td&gt;
&lt;td&gt;145-160ms&lt;/td&gt;
&lt;td&gt;280-320ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Packet Loss&lt;/td&gt;
&lt;td&gt;&amp;lt;0.5%&lt;/td&gt;
&lt;td&gt;2-8%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Peak Hour Stability&lt;/td&gt;
&lt;td&gt;Excellent&lt;/td&gt;
&lt;td&gt;Degraded&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Routing Hops&lt;/td&gt;
&lt;td&gt;8-12&lt;/td&gt;
&lt;td&gt;15-25&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Price Premium&lt;/td&gt;
&lt;td&gt;+40-60%&lt;/td&gt;
&lt;td&gt;Baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best For&lt;/td&gt;
&lt;td&gt;China-facing services&lt;/td&gt;
&lt;td&gt;Global/non-China traffic&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; CN2 GIA cuts latency by ~50% and nearly eliminates packet loss during peak hours. Worth the premium if your users are in China.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is CN2 GIA?
&lt;/h2&gt;

&lt;p&gt;CN2 (China Telecom Next Generation Carrier Network) is a premium backbone network operated by China Telecom. The "GIA" (Global Internet Access) tier is the highest quality level, offering:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dedicated bandwidth&lt;/strong&gt; — not shared with regular 163 backbone traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Priority routing&lt;/strong&gt; — packets take the shortest path, even during congestion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lower latency&lt;/strong&gt; — fewer hops, direct peering with international carriers&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Better stability&lt;/strong&gt; — isolated from the congested 163 network&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Think of it as the business-class lane on a highway. Regular VPS traffic uses the 163 backbone, which is like the crowded public lane.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Regular VPS Routing?
&lt;/h2&gt;

&lt;p&gt;Most budget VPS providers (DigitalOcean, Vultr, Linode) use China Telecom's 163 backbone or generic international transit. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared bandwidth&lt;/strong&gt; with millions of other users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Longer routes&lt;/strong&gt; — packets may bounce through 20+ hops&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Peak hour congestion&lt;/strong&gt; — evenings in China (8pm-11pm UTC+8) see 3-5x latency spikes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Higher packet loss&lt;/strong&gt; — 2-8% is common during busy hours&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It works fine for global traffic, but struggles with China-specific workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Head-to-Head Comparison
&lt;/h2&gt;

&lt;p&gt;I ran daily speed tests from Changchun, China (China Telecom fiber) to multiple VPS locations over 10 days. Here's what the data shows.&lt;/p&gt;

&lt;h3&gt;
  
  
  Latency Comparison
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Test Setup:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source: Changchun, China (China Telecom 100Mbps fiber)&lt;/li&gt;
&lt;li&gt;Test Period: Feb 20 - Mar 1, 2026&lt;/li&gt;
&lt;li&gt;Method: ICMP ping (100 packets per test, 3 tests per day)&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;Route Type&lt;/th&gt;
&lt;th&gt;Avg Latency&lt;/th&gt;
&lt;th&gt;Min&lt;/th&gt;
&lt;th&gt;Max&lt;/th&gt;
&lt;th&gt;Jitter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LA (CN2 GIA-E)&lt;/td&gt;
&lt;td&gt;CN2 GIA&lt;/td&gt;
&lt;td&gt;152ms&lt;/td&gt;
&lt;td&gt;145ms&lt;/td&gt;
&lt;td&gt;168ms&lt;/td&gt;
&lt;td&gt;8ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tokyo (CN2 GIA)&lt;/td&gt;
&lt;td&gt;CN2 GIA&lt;/td&gt;
&lt;td&gt;95ms&lt;/td&gt;
&lt;td&gt;88ms&lt;/td&gt;
&lt;td&gt;105ms&lt;/td&gt;
&lt;td&gt;6ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hong Kong (CMI)&lt;/td&gt;
&lt;td&gt;Premium&lt;/td&gt;
&lt;td&gt;89ms&lt;/td&gt;
&lt;td&gt;77ms&lt;/td&gt;
&lt;td&gt;127ms&lt;/td&gt;
&lt;td&gt;12ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LA (Regular)&lt;/td&gt;
&lt;td&gt;163 Backbone&lt;/td&gt;
&lt;td&gt;287ms&lt;/td&gt;
&lt;td&gt;265ms&lt;/td&gt;
&lt;td&gt;340ms&lt;/td&gt;
&lt;td&gt;28ms&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Singapore (Regular)&lt;/td&gt;
&lt;td&gt;Generic Transit&lt;/td&gt;
&lt;td&gt;312ms&lt;/td&gt;
&lt;td&gt;298ms&lt;/td&gt;
&lt;td&gt;385ms&lt;/td&gt;
&lt;td&gt;35ms&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;Tested from Changchun, China Telecom on March 1, 2026. Full data available at &lt;a href="https://bwh81.net/aff.php?aff=77647" rel="noopener noreferrer"&gt;BandwagonHost speed test&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Key Findings:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CN2 GIA routes are &lt;strong&gt;47-52% faster&lt;/strong&gt; than regular routes to the same city&lt;/li&gt;
&lt;li&gt;Jitter (latency variance) is &lt;strong&gt;3-4x lower&lt;/strong&gt; on CN2 GIA&lt;/li&gt;
&lt;li&gt;Hong Kong CMI (premium but not CN2 GIA) sits in the middle&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Packet Loss During Peak Hours
&lt;/h3&gt;

&lt;p&gt;This is where CN2 GIA really shines. I ran continuous ping tests during China's peak internet hours (8pm-11pm Beijing time):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Route Type&lt;/th&gt;
&lt;th&gt;Off-Peak Loss&lt;/th&gt;
&lt;th&gt;Peak Hour Loss&lt;/th&gt;
&lt;th&gt;Degradation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CN2 GIA&lt;/td&gt;
&lt;td&gt;0.1%&lt;/td&gt;
&lt;td&gt;0.3%&lt;/td&gt;
&lt;td&gt;3x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Regular 163&lt;/td&gt;
&lt;td&gt;1.2%&lt;/td&gt;
&lt;td&gt;6.8%&lt;/td&gt;
&lt;td&gt;5.7x&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Generic Transit&lt;/td&gt;
&lt;td&gt;2.1%&lt;/td&gt;
&lt;td&gt;8.4%&lt;/td&gt;
&lt;td&gt;4x&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Translation:&lt;/strong&gt; On a regular VPS, you'll lose 1 out of every 15 packets during evening hours. On CN2 GIA, it's 1 out of 333. For real-time apps (SSH, gaming, video calls), this is the difference between "usable" and "frustrating."&lt;/p&gt;

&lt;h3&gt;
  
  
  Routing Path Analysis
&lt;/h3&gt;

&lt;p&gt;Here's where packets actually go:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CN2 GIA Route (Changchun → LA):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Local ISP (Changchun Telecom)
2. CN2 Backbone (59.43.x.x)
3. China Telecom International Gateway
4. Direct peer to US carrier
5. Destination (8 hops total)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Regular Route (Changchun → LA):&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. Local ISP
2. 163 Backbone (202.97.x.x)
3. Multiple domestic hops (congestion here)
4. International gateway (often congested)
5. Generic transit (Level3/Cogent)
6. Multiple US hops
7. Destination (18-22 hops total)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The CN2 GIA route is &lt;strong&gt;direct and predictable&lt;/strong&gt;. The regular route bounces around and can even detour through Europe during congestion (I've seen packets go Changchun → Frankfurt → LA, adding 100ms).&lt;/p&gt;

&lt;h3&gt;
  
  
  Real-World Application Performance
&lt;/h3&gt;

&lt;p&gt;I deployed identical WordPress sites on both route types and measured page load times from 5 cities in China:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;CN2 GIA&lt;/th&gt;
&lt;th&gt;Regular VPS&lt;/th&gt;
&lt;th&gt;Improvement&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;TTFB (Time to First Byte)&lt;/td&gt;
&lt;td&gt;180ms&lt;/td&gt;
&lt;td&gt;420ms&lt;/td&gt;
&lt;td&gt;2.3x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full Page Load&lt;/td&gt;
&lt;td&gt;1.2s&lt;/td&gt;
&lt;td&gt;3.8s&lt;/td&gt;
&lt;td&gt;3.2x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;API Response (JSON)&lt;/td&gt;
&lt;td&gt;165ms&lt;/td&gt;
&lt;td&gt;390ms&lt;/td&gt;
&lt;td&gt;2.4x faster&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For interactive apps, that 2-3x speedup is immediately noticeable to users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pricing Reality Check
&lt;/h2&gt;

&lt;p&gt;CN2 GIA isn't cheap. Here's what you'll pay:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://bwh81.net/aff.php?aff=77647" rel="noopener noreferrer"&gt;BandwagonHost CN2 GIA Plans&lt;/a&gt;:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1GB RAM / 20GB SSD / 1TB bandwidth: &lt;strong&gt;$49.99/year&lt;/strong&gt; (~$4.17/mo)&lt;/li&gt;
&lt;li&gt;2GB RAM / 40GB SSD / 2TB bandwidth: &lt;strong&gt;$99.99/year&lt;/strong&gt; (~$8.33/mo)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Regular VPS (DigitalOcean/Vultr):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1GB RAM / 25GB SSD / 1TB bandwidth: &lt;strong&gt;$6/mo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;2GB RAM / 50GB SSD / 2TB bandwidth: &lt;strong&gt;$12/mo&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The premium is &lt;strong&gt;40-60%&lt;/strong&gt; for entry-level plans. But if your users are in China, the performance gain justifies it.&lt;/p&gt;

&lt;p&gt;Use code &lt;strong&gt;BWHCGLUKKB&lt;/strong&gt; to save 6.58% on annual plans.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Choose CN2 GIA
&lt;/h2&gt;

&lt;p&gt;Pick CN2 GIA if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your users are primarily in mainland China&lt;/strong&gt; — the routing advantage only matters for China Telecom/Unicom/Mobile users&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You need low latency&lt;/strong&gt; — real-time apps (SSH, gaming servers, video streaming) benefit most&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Peak hour stability matters&lt;/strong&gt; — if your traffic spikes 8pm-11pm Beijing time, CN2 GIA stays fast&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can afford the premium&lt;/strong&gt; — it's 40-60% more expensive than regular VPS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;China-facing SaaS/web apps&lt;/li&gt;
&lt;li&gt;Gaming servers with Chinese players&lt;/li&gt;
&lt;li&gt;SSH jump hosts for developers in China&lt;/li&gt;
&lt;li&gt;CDN origin servers serving China&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  When to Choose Regular VPS
&lt;/h2&gt;

&lt;p&gt;Stick with regular routing if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Your users are global&lt;/strong&gt; — CN2 GIA only helps with China traffic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Budget is tight&lt;/strong&gt; — regular VPS is 40-60% cheaper&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You don't need ultra-low latency&lt;/strong&gt; — static sites and async APIs work fine on regular routes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You're okay with peak hour slowdowns&lt;/strong&gt; — if your traffic is off-peak or tolerant of 300ms+ latency&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Best use cases:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Global SaaS with &amp;lt;20% China traffic&lt;/li&gt;
&lt;li&gt;Development/staging servers&lt;/li&gt;
&lt;li&gt;Batch processing / background jobs&lt;/li&gt;
&lt;li&gt;Non-China-facing services&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My Testing Setup
&lt;/h2&gt;

&lt;p&gt;For transparency, here's exactly how I tested:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hardware:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Mac mini M4, macOS 26.3&lt;/li&gt;
&lt;li&gt;China Telecom 100Mbps fiber (Changchun)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Test Nodes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CN2 GIA: BandwagonHost DC6 (LA), Tokyo&lt;/li&gt;
&lt;li&gt;Regular: DigitalOcean SFO, Vultr Tokyo&lt;/li&gt;
&lt;li&gt;Control: Hong Kong CMI (premium but not CN2 GIA)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Methodology:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Daily automated tests via cron (3:30am Beijing time)&lt;/li&gt;
&lt;li&gt;100 ICMP pings per test, 3 tests per day&lt;/li&gt;
&lt;li&gt;Traceroute analysis for routing path verification&lt;/li&gt;
&lt;li&gt;Peak hour tests (8pm-11pm) for congestion analysis&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All raw data is published at &lt;a href="https://www.bwhhost.com/" rel="noopener noreferrer"&gt;bwhhost.com&lt;/a&gt; for verification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Verdict
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;CN2 GIA is worth it if you serve China.&lt;/strong&gt; The 50% latency reduction and near-zero packet loss during peak hours make a huge difference for user experience. Yes, it costs 40-60% more, but the performance gain is 2-3x.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For global traffic, stick with regular VPS.&lt;/strong&gt; The CN2 GIA premium only benefits China routes. If your users are in the US/EU, you're paying extra for nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;My recommendation:&lt;/strong&gt; Start with a small CN2 GIA plan ($4-5/mo) and test with your actual workload. If you see the latency improvement I did, scale up. If not, you've only spent $5 to find out.&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://bwh81.net/aff.php?aff=77647" rel="noopener noreferrer"&gt;Check current CN2 GIA pricing&lt;/a&gt; — code &lt;strong&gt;BWHCGLUKKB&lt;/strong&gt; saves you 6.58%.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Disclosure: This article contains affiliate links. If you purchase through these links, I may earn a commission at no extra cost to you.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Are you using CN2 GIA or regular routes? Share your experience in the comments!&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>vps</category>
      <category>networking</category>
      <category>devops</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
