posted on Tue, May 20 '25 under tags: script,, devops

What if there are domains you care about but the expiry alerts don’t necessarily come to your email address?

Update on 2025-05-26: There’s a bug in this code that causes lines to disappear. Read more at the end of the post.

I’ve had it before. There are people who start websites. And they forget to renew their domain. And then they ask me whether I can recover the domain somehow. Often after the grace period for domain recovery.

So, I decided to write (with ChatGPT) a script that’ll check domain expiry using whois (which has to be installed locally for this to work) and use notify-send to send me an alert if necessary. I then automated running this using systemd. The whole thing is attached.

Script

#!/bin/bash

CSV_FILE="domain_expiry.csv"
TEMP_FILE=$(mktemp)
TODAY=$(date +%s)
WARNING_DAYS=14
SKIP_IF_MORE_THAN_DAYS=90

# Ensure CSV exists
if [[ ! -f "$CSV_FILE" ]]; then
  echo "date,domain" > "$CSV_FILE"
fi

# Read current CSV (excluding header)
tail -n +2 "$CSV_FILE" | while IFS=',' read -r expiry domain; do
  # Check if date is valid (YYYY-MM-DD)
  if [[ "$expiry" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then
    expiry_ts=$(date -d "$expiry" +%s 2>/dev/null)
    if [[ -n "$expiry_ts" ]]; then
      days_remaining=$(( (expiry_ts - TODAY) / 86400 ))

      # Skip domain if expiry is > 90 days from now
      if (( days_remaining > SKIP_IF_MORE_THAN_DAYS )); then
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Skipping $domain — expires in $days_remaining days ($expiry)"
        echo "$expiry,$domain" >> "$TEMP_FILE"
        continue
      fi
    fi
  fi

  echo -n "[$(date '+%Y-%m-%d %H:%M:%S')] Checking $domain... "

  expiry_line=$(whois "$domain" 2>/dev/null | grep -iE "Expiry Date|Expiration Date|renewal date" | head -n 1)
  new_expiry=$(echo "$expiry_line" | grep -oE '[0-9]{4}-[0-9]{2}-[0-9]{2}')

  if [[ -n "$new_expiry" ]]; then
    echo "$new_expiry,$domain" >> "$TEMP_FILE"
    echo "$new_expiry"

    # Notify if it's within warning window
    new_expiry_ts=$(date -d "$new_expiry" +%s 2>/dev/null)
    if [[ -n "$new_expiry_ts" && $(( (new_expiry_ts - TODAY)/86400 )) -le $WARNING_DAYS && $new_expiry_ts -ge $TODAY ]]; then
      notify-send "Domain Expiry Warning" "$domain expires on $new_expiry"
      echo "⚠️  $domain expires within $WARNING_DAYS days!"
    fi
  else
    echo "Could not find expiry date"
  fi
done

# Sort by expiry date (oldest first), deduplicate by domain
sort -t',' -k1,1 "$TEMP_FILE" | uniq > "$TEMP_FILE.sorted"

# Write updated CSV
echo "date,domain" > "$CSV_FILE"
cat "$TEMP_FILE.sorted" >> "$CSV_FILE"

# Cleanup
rm "$TEMP_FILE" "$TEMP_FILE.sorted"

Save this in some file named update.sh or so, perhaps in ``/home/asd/.local/bin and don't forget to do chmod +x update.sh`.

Do note the settings like:

Input/Output file

The input and output are the same file, a CSV file that looks like

date,domain
,example.com
,example.org

This should be named domain_expiry.csv and kept in the same folder as the script (as you can see in the script). Once the script runs, it’ll update the date column and sort it such that the earliest expiring domain will be at the top.

systemd service

$ cat ~/.config/systemd/user/domain-expiry.service 
[Unit]
Description=Check if domains are expiring

[Service]
Type=oneshot
WorkingDirectory=/home/asd/.local/bin
ExecStart=/home/asd/.local/bin/update.sh

systemd timer

$ cat ~/.config/systemd/user/domain-expiry.timer 
[Unit]
Description=Run checkspace every day

[Timer]
OnCalendar=daily

[Install]
WantedBy=timers.target

So I just have to remember to systemctl --user daemon-reload systemctl --user enable domain-expiry.timer

Erratum

Update on 2025-05-26: As promised, LLM code can’t be trusted. In the above, the else clause of if [[ -n "$new_expiry" ]]; then will simply output “Could not find expiry date” and skip inserting that domain into the output. Thus, if some domain expiry couldn’t be discovered, it’ll drop that domain!

To fix this, just add echo "$expiry,$domain" >> "$TEMP_FILE" after the line echo "Could not find expiry date"

Like what you are reading? Subscribe (by RSS, email, mastodon, or telegram)!