Thunderbird finally added native Exchange Web Services support, no more paying for third-party add-ons. But there is a catch: Linux distributions lag behind by months, and even Mozilla's official Flatpak builds can freeze at older versions indefinitely. I don't want to manually check for updates, so I automated the entire process to pull directly from Mozilla's release archive using a script that also protects against downgrades and profile breakage.
The strategy: query Mozilla's product-details API for the latest Thunderbird release, validate that the .deb (or .rpm, with minor changes) actually exists (because the API sometimes advertises a version before binaries are published), and handle installation automatically. The script includes an intelligent fallback if the API version is not yet available, scraping the release archive for the most recent published non-ESR version, plus extra safeguards to refuse downgrades and create backups of your profile on major upgrades.
Auto-update script with downgrade protection
Create ~/.local/bin/update-thunderbird:
[!COLLAPSE] Show full update script | Hide script
#!/bin/bash
set -euo pipefail
LOG="${HOME}/.local/log/thunderbird-update.log"
mkdir -p "$(dirname "$LOG")"
log() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
error() { echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $*" | tee -a "$LOG" >&2; exit 1; }
TB_PROFILE_ROOT="${HOME}/.thunderbird"
BACKUP_DIR="${HOME}/.thunderbird-backups"
log "Starting Thunderbird update check..."
# --- 0. Sanity: ensure necessary tools ---
for cmd in curl jq dpkg-query dpkg sort grep sed; do
command -v "$cmd" >/dev/null 2>&1 || error "Required command '$cmd' not found in PATH"
done
# --- 1. Get candidate version from API ---
CANDIDATE_VERSION=$(
curl -fsS "https://product-details.mozilla.org/1.0/thunderbird_versions.json" \
| jq -r '.LATEST_THUNDERBIRD_VERSION // empty'
)
if [[ -z "$CANDIDATE_VERSION" ]]; then
error "Failed to fetch LATEST_THUNDERBIRD_VERSION from Mozilla API"
fi
log "Candidate version (API): $CANDIDATE_VERSION"
# --- 2. Validate .deb exists for candidate; if not, fall back to latest published ---
validate_version() {
local v="$1"
local url="https://archive.mozilla.org/pub/thunderbird/releases/${v}/linux-x86_64/en-US/thunderbird-${v}.deb"
local code
code=$(curl -fsSI "$url" | awk 'NR==1 {print $2}')
if [[ "$code" == "200" ]]; then
echo "$v"
return 0
fi
return 1
}
VERSION=""
if validate_version "$CANDIDATE_VERSION" >/dev/null; then
VERSION="$CANDIDATE_VERSION"
log "Using candidate version: $VERSION"
else
log "Binary for $CANDIDATE_VERSION not yet available. Falling back to latest published..."
VERSION=$(
curl -fsS "https://archive.mozilla.org/pub/thunderbird/releases/" \
| grep -oE 'href="[0-9]+\.[0-9]+(\.[0-9]+)?/"' \
| sed 's|href="||; s|/"||' \
| grep -vi 'esr' \
| sort -V \
| tail -n1
)
[[ -z "$VERSION" ]] && error "No non-ESR versions found in releases index"
log "Fallback version: $VERSION"
validate_version "$VERSION" >/dev/null || error "Even fallback version $VERSION has no .deb"
fi
# --- 3. Get installed version (if any) ---
INSTALLED_VERSION=""
if dpkg -l thunderbird &>/dev/null; then
INSTALLED_VERSION=$(
dpkg-query -f '${Version}' -W thunderbird 2>/dev/null \
| sed 's/.*://' \
| cut -d'-' -f1 \
| sed 's/esr.*//'
)
log "Installed version: $INSTALLED_VERSION"
else
log "Thunderbird not installed (fresh install)"
fi
# --- 4. Enforce monotonic version (no downgrade) ---
if [[ -n "$INSTALLED_VERSION" ]]; then
if dpkg --compare-versions "$VERSION" eq "$INSTALLED_VERSION"; then
log "Already up-to-date (installed: $INSTALLED_VERSION, latest: $VERSION)."
exit 0
fi
if dpkg --compare-versions "$VERSION" lt "$INSTALLED_VERSION"; then
log "Refusing to downgrade Thunderbird (installed: $INSTALLED_VERSION, candidate: $VERSION). Aborting."
exit 0
fi
fi
# --- 5. Optional: backup profile on major-version bump ---
if [[ -n "${INSTALLED_VERSION:-}" && -d "$TB_PROFILE_ROOT" ]]; then
MAJOR_INSTALLED="${INSTALLED_VERSION%%.*}"
MAJOR_NEW="${VERSION%%.*}"
if [[ "$MAJOR_NEW" -gt "$MAJOR_INSTALLED" ]]; then
mkdir -p "$BACKUP_DIR"
BACKUP_FILE="${BACKUP_DIR}/profile-$(date +%Y%m%d-%H%M%S)-${INSTALLED_VERSION}.tar.zst"
log "Major upgrade detected (${INSTALLED_VERSION} -> ${VERSION}). Backing up profile to: $BACKUP_FILE"
tar -C "$HOME" -I 'zstd -19' -cf "$BACKUP_FILE" ".thunderbird" || \
log "WARNING: Profile backup failed; proceeding with update anyway."
fi
fi
# --- 6. Download & install ---
DEB_FILE="/tmp/thunderbird-${VERSION}.deb"
DEB_URL="https://archive.mozilla.org/pub/thunderbird/releases/${VERSION}/linux-x86_64/en-US/thunderbird-${VERSION}.deb"
log "Downloading $DEB_URL"
curl -fsSL "$DEB_URL" -o "$DEB_FILE"
if [[ ! -s "$DEB_FILE" ]]; then
error "Downloaded file $DEB_FILE is empty or missing"
fi
log "Installing Thunderbird $VERSION"
if ! sudo dpkg -i "$DEB_FILE"; then
log "dpkg reported issues, attempting to fix dependencies..."
sudo apt-get -f install -y
log "Re-running dpkg -i after fixing dependencies..."
sudo dpkg -i "$DEB_FILE"
fi
log "Thunderbird updated to $VERSION"
rm -f "$DEB_FILE"
# --- 7. Optional: log current profile setup for traceability ---
if [[ -d "$TB_PROFILE_ROOT" && -f "${TB_PROFILE_ROOT}/profiles.ini" ]]; then
log "Profile root: $TB_PROFILE_ROOT"
DEFAULT_PROFILE_PATH=$(
awk -F= '
/^\[Profile[0-9]+\]$/ { sec=1; isdef=0; path="" }
sec && /^Default=1$/ { isdef=1 }
sec && /^Path=/ { path=$2 }
sec && isdef && path { print path; exit }
' "${TB_PROFILE_ROOT}/profiles.ini" || true
)
if [[ -n "$DEFAULT_PROFILE_PATH" ]]; then
log "Default profile path (relative): $DEFAULT_PROFILE_PATH"
COMPAT_FILE="${TB_PROFILE_ROOT}/${DEFAULT_PROFILE_PATH}/compatibility.ini"
if [[ -f "$COMPAT_FILE" ]]; then
LAST_BUILD_ID=$(grep '^LastAppBuildID=' "$COMPAT_FILE" | cut -d= -f2 || true)
[[ -n "$LAST_BUILD_ID" ]] && log "Profile LastAppBuildID: $LAST_BUILD_ID"
fi
else
log "Could not determine default profile from profiles.ini"
fi
else
log "No Thunderbird profile directory found at $TB_PROFILE_ROOT (might be first install or using snap/other layout)"
fi
# --- 8. Desktop notification (optional) ---
if command -v notify-send >/dev/null; then
notify-send "Thunderbird Updated" "v$VERSION installed" --icon=mail-unread || true
fi
log "Done."We have some built-in safety that can save your profile from breakage:
- Refuses to install a version older than what is already installed (no accidental downgrades that might break your profile).
- Takes a compressed backup of
~/.thunderbirdon major version jumps before upgrading, so you can roll back if something goes wrong. - Retries installation after fixing dependencies if
dpkgcomplains.
Set permissions and test:
chmod +x ~/.local/bin/update-thunderbird
mkdir -p ~/.local/log
~/.local/bin/update-thunderbirdAutomating with systemd user timers
systemd user timers handle scheduling more reliably than cron and survive reboots. Create ~/.config/systemd/user/thunderbird-update.timer:
[Unit]
Description=Check for Thunderbird updates weekly
[Timer]
OnCalendar=weekly
Persistent=true
RandomizedDelaySec=3600
[Install]
WantedBy=timers.targetAnd ~/.config/systemd/user/thunderbird-update.service:
[Unit]
Description=Update Thunderbird to latest release
After=network-online.target
[Service]
Type=oneshot
ExecStart=%h/.local/bin/update-thunderbird
Environment=HOME=%hEnable and start the timer:
systemctl --user daemon-reload
systemctl --user enable --now thunderbird-update.timerThe timer runs weekly, persists across reboots, and logs to ~/.local/log/thunderbird-update.log. You can check status with:
systemctl --user status thunderbird-update.timeror view logs with:
journalctl --user -u thunderbird-update.serviceOptional one-liner for manual updates
For quick manual updates, you can still keep a simple alias in ~/.bashrc:
alias tb-update='VERSION=$(curl -s https://product-details.mozilla.org/1.0/thunderbird_versions.json | jq -r .LATEST_THUNDERBIRD_VERSION) && echo "Latest: $VERSION" && wget -qO /tmp/tb.deb "https://download.mozilla.org/?product=thunderbird-$VERSION&os=linux64&lang=en-US" && sudo dpkg -i /tmp/tb.deb || { sudo apt --fix-broken install -y && sudo dpkg -i /tmp/tb.deb; } && rm -f /tmp/tb.deb'Run tb-update anytime you want an immediate bump outside the weekly schedule. With this setup, Thunderbird stays current across all your machines, you get native EWS support as soon as Mozilla ships it, and distribution release cycles, or accidental downgrades stop being a problem.