<?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: Connor D</title>
    <description>The latest articles on DEV Community by Connor D (@c_domainium).</description>
    <link>https://dev.to/c_domainium</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4092911%2F43dbccb7-c400-42a4-813d-5671b9494472.jpg</url>
      <title>DEV Community: Connor D</title>
      <link>https://dev.to/c_domainium</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/c_domainium"/>
    <language>en</language>
    <item>
      <title>I let a domain expire once. Now I check them all with a script.</title>
      <dc:creator>Connor D</dc:creator>
      <pubDate>Mon, 24 Aug 2026 19:58:31 +0000</pubDate>
      <link>https://dev.to/c_domainium/i-let-a-domain-expire-once-now-i-check-them-all-with-a-script-1pmp</link>
      <guid>https://dev.to/c_domainium/i-let-a-domain-expire-once-now-i-check-them-all-with-a-script-1pmp</guid>
      <description>&lt;p&gt;A few years back I let a domain lapse. Not a throwaway one either. It was live, had traffic, and I just forgot the renewal date sitting in some registrar account I hadn't logged into in months. By the time I noticed, it was in redemption and cost me a small fortune to get back.&lt;/p&gt;

&lt;p&gt;The annoying part is that it was completely avoidable. I owned maybe fifteen domains at the time, spread across three registrars, and I had no single place that told me what was expiring and when. So I wrote a script.&lt;/p&gt;

&lt;h2&gt;
  
  
  The manual way is painful
&lt;/h2&gt;

&lt;p&gt;You can check one domain with &lt;code&gt;whois&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;whois example.com | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s2"&gt;"expiry"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That works for one domain. It does not scale to a list. And logging into each registrar to eyeball dates is exactly the habit that let one slip in the first place.&lt;/p&gt;

&lt;h2&gt;
  
  
  The script
&lt;/h2&gt;

&lt;p&gt;Put every domain you own in a file, one per line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com
mysite.net
sideproject.io
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run this against 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="c"&gt;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="c"&gt;# usage: ./check-expiry.sh domains.txt&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; domain&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&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;$domain&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;continue
  &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;whois &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; 2&amp;gt;/dev/null &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-iE&lt;/span&gt; &lt;span class="s1"&gt;'Registry Expiry Date|Expiration Date|paid-till|renewal date'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;head&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="se"&gt;\&lt;/span&gt;
    | &lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-E&lt;/span&gt; &lt;span class="s1"&gt;'s/.*: *//'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
  &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%-30s %s\n'&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="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;expiry&lt;/span&gt;&lt;span class="k"&gt;:-&lt;/span&gt;&lt;span class="nv"&gt;not&lt;/span&gt;&lt;span class="p"&gt; found&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nb"&gt;sleep &lt;/span&gt;2
&lt;span class="k"&gt;done&lt;/span&gt; &amp;lt; &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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get a clean list back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;example.com                    2026-11-04T09:00:00Z
mysite.net                     2027-01-22T00:00:00Z
sideproject.io                 not found
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;sleep 2&lt;/code&gt; matters. Most whois servers rate limit you, and if you hammer them with a big list they will start refusing to answer. Two seconds between lookups keeps you under the radar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put it on a schedule
&lt;/h2&gt;

&lt;p&gt;Once it works, there is no reason to run it by hand. Drop it in cron and have it email you the output once a week:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;0 9 &lt;span class="k"&gt;*&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt; 1 /home/you/check-expiry.sh /home/you/domains.txt | mail &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s2"&gt;"Domain expiry check"&lt;/span&gt; you@example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now every Monday morning you get a list. Anything close to expiry, you go renew.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this falls over
&lt;/h2&gt;

&lt;p&gt;This is good enough for a handful of domains, but it has real limits.&lt;/p&gt;

&lt;p&gt;Whois output is not standardised. Different TLDs use different field names for the expiry date, so the grep misses some and you get "not found" even when the domain is fine. Country-code TLDs are the worst for this.&lt;/p&gt;

&lt;p&gt;It also tells you nothing about DNS health, whether your records still resolve, or whether a domain got hijacked or misconfigured. It only reads the expiry field, and only when the format cooperates.&lt;/p&gt;

&lt;p&gt;Once you get past twenty or thirty domains across several registrars, the script becomes its own small maintenance job. That is roughly the point where I stopped patching it and built &lt;a href="https://usedomainium.com" rel="noopener noreferrer"&gt;Domainium&lt;/a&gt;, which pulls every domain across every registrar into one dashboard and handles the expiry parsing and alerts properly. Free to start if you want to skip the cron job.&lt;/p&gt;

&lt;p&gt;But if you own a few domains and just want to stop forgetting them, the script above will do the job. Copy it, point it at your list, and never lose a domain to a missed renewal again.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>domains</category>
      <category>dns</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
