@p_7 Yes, that is correct.
Tailscale is no longer clobbering my resolv.conf. you might need to re-symlink it as per YunoHost Diagnosis.
My /etc/resolv.conf is symlinked to /run/resolvconf/resolv.conf
@p_7 Yes, that is correct.
Tailscale is no longer clobbering my resolv.conf. you might need to re-symlink it as per YunoHost Diagnosis.
My /etc/resolv.conf is symlinked to /run/resolvconf/resolv.conf
I am glad I found this thread because I found a lot of information that was not in the documentation.
That’s why I decided to improve the documentation and now I would like you to review my PR and tell me what I could have missed, what is wrong or what could be better written.
Edit: already merged, no need for review
This is not a hook, but a customisation : Get notified when new updates are available
#!/bin/bash
# Place this file in /etc/cron.daily and make it executable
# Configuration
RECIPIENT="root"
SUBJECT="YunoHost Updates Available - $(date +'%Y-%m-%d')"
# Run update check
OUTPUT=$(yunohost tools update --json 2>&1)
JSON=$(echo "$OUTPUT" | grep -E '^\{"system"|^\{' | head -1)
# Verify JSON
if ! jq -e . >/dev/null 2>&1 <<<"$JSON"; then
echo "Error parsing JSON output" | mail -s "YunoHost Update Error" "$RECIPIENT"
exit 1
fi
# Check for updates
SYSTEM_UPDATES=$(jq -e '.system | length > 0' <<<"$JSON")
APP_UPDATES=$(jq -e '[.apps[] | select(.upgradable == "yes")] | length > 0' <<<"$JSON")
YUNOHOST_UPDATE=$(jq -e '.important_yunohost_upgrade == true' <<<"$JSON")
# Exit if no updates
if ! $SYSTEM_UPDATES && ! $APP_UPDATES && ! $YUNOHOST_UPDATE; then
exit 0
fi
# Generate HTML content
HTML_HEADER='
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
* { font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; }
body { background-color: #f7f9fc; margin: 0; padding: 20px; }
.container { max-width: 650px; margin: 0 auto; }
.card { background: white; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); margin-bottom: 25px; overflow: hidden; }
.header { background: #3f51b5; padding: 25px; text-align: center; }
h1 { color: white; font-weight: 600; font-size: 22px; margin: 0; }
.update-section { padding: 25px 30px; }
h2 { color: #2c3e50; font-size: 18px; margin-top: 0; margin-bottom: 18px; padding-bottom: 10px; border-bottom: 1px solid #eee; }
table { width: 100%; border-collapse: collapse; }
th { text-align: left; color: #7f8c8d; font-weight: 500; font-size: 13px; text-transform: uppercase; padding: 8px 10px; }
td { padding: 10px; border-bottom: 1px solid #f1f5f9; }
tr:last-child td { border-bottom: none; }
.package-name { color: #2c3e50; font-weight: 500; }
.version { font-family: "SFMono-Regular", Consolas, monospace; font-size: 13px; }
.current-version { color: #e74c3c; }
.new-version { color: #27ae60; font-weight: 500; }
.badge { display: inline-block; padding: 3px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; }
.badge-system { background: #e3f2fd; color: #1565c0; }
.badge-app { background: #e8f5e9; color: #2e7d32; }
.badge-critical { background: #ffebee; color: #c62828; }
.footer { text-align: center; padding: 20px; color: #95a5a6; font-size: 12px; }
.update-count { background: #4caf50; color: white; border-radius: 50%; width: 22px; height: 22px; display: inline-block; text-align: center; line-height: 22px; font-size: 12px; margin-left: 8px; }
.no-updates { color: #7f8c8d; font-style: italic; text-align: center; padding: 20px; }
</style>
</head>
<body>
<div class="container">
<div class="card">
<div class="header">
<h1>YunoHost Server Updates</h1>
</div>
<div class="update-section">
<h2>System Packages'
# System updates section
if $SYSTEM_UPDATES; then
COUNT=$(jq '.system | length' <<<"$JSON")
HTML_HEADER+="<span class='update-count'>$COUNT</span>"
fi
HTML_HEADER+='</h2>'
if $SYSTEM_UPDATES; then
HTML_SYSTEM='<table><tr><th>Package</th><th>Current Version</th><th>New Version</th></tr>'
while read -r row; do
name=$(jq -r '.name' <<<"$row")
current=$(jq -r '.current_version' <<<"$row")
new=$(jq -r '.new_version' <<<"$row")
HTML_SYSTEM+="<tr><td class='package-name'><span class='badge badge-system'>system</span> $name</td>"
HTML_SYSTEM+="<td class='version current-version'>$current</td>"
HTML_SYSTEM+="<td class='version new-version'>$new</td></tr>"
done < <(jq -c '.system[]' <<<"$JSON")
HTML_SYSTEM+='</table>'
else
HTML_SYSTEM='<div class="no-updates">No system package updates available</div>'
fi
# Application updates section
HTML_APPS='<div class="card"><div class="update-section"><h2>Applications'
if $APP_UPDATES; then
COUNT=$(jq '[.apps[] | select(.upgradable == "yes")] | length' <<<"$JSON")
HTML_APPS+="<span class='update-count'>$COUNT</span>"
fi
HTML_APPS+='</h2>'
if $APP_UPDATES; then
HTML_APPS+='<table><tr><th>Application</th><th>Domain</th><th>Current Version</th><th>New Version</th></tr>'
while read -r row; do
name=$(jq -r '.name' <<<"$row")
domain=$(jq -r '.domain_path' <<<"$row")
current=$(jq -r '.current_version' <<<"$row")
new=$(jq -r '.new_version' <<<"$row")
HTML_APPS+="<tr><td class='package-name'><span class='badge badge-app'>app</span> $name</td>"
HTML_APPS+="<td>$domain</td>"
HTML_APPS+="<td class='version current-version'>$current</td>"
HTML_APPS+="<td class='version new-version'>$new</td></tr>"
done < <(jq -c '.apps[] | select(.upgradable == "yes")' <<<"$JSON")
HTML_APPS+='</table>'
else
HTML_APPS+='<div class="no-updates">No application updates available</div>'
fi
HTML_APPS+='</div></div>'
# Critical update section
HTML_CRITICAL=''
if $YUNOHOST_UPDATE; then
HTML_CRITICAL='<div class="card"><div class="update-section"><h2><span class="badge badge-critical">Critical</span> YunoHost Core Update Available</h2><p>Important YunoHost system update is available. Please upgrade as soon as possible.</p></div></div>'
fi
# Footer
HTML_FOOTER="<div class='footer'><p>Generated on $(date +'%Y-%m-%d %H:%M:%S')</p></div></div></body></html>"
# Combine all HTML parts
FULL_HTML="${HTML_HEADER}${HTML_SYSTEM}</div></div>${HTML_APPS}${HTML_CRITICAL}${HTML_FOOTER}"
# Send email
echo "$FULL_HTML" | mail -a "Content-Type: text/html" -s "$SUBJECT" "$RECIPIENT"
And yes, I used some ai help, because I was overwhelmed by work, family, contribs, etc…
Don’t forget to read and understand code before you use it.
Edit : a screenshot of the email notification
Edit 2: update here
Aïe aïe aïe ! Attention, la mise à jour de yunohost 12.1 casse ces hooks !! le règles et les fichiers postfix ont changer de manière notoire !
Il n’y a plus de fichier /etc/postfix/ldap-domains.cf
ni de lignes mx dans les fichiers /etc/dnsmasq.d/domaine.tld
Du coup je cherche a rétablir comment y arriver ??
Hi @jarod5001,
This script only gives me Yunohost packages but not standard packages. I don’t try to debug it, is it the same for you and are you planning to do an update?
yunohost tools update (system only) :
system:
misc utils and libs:
0:
current_version: 10.0.0~dfsg-11+deb12u7
name: ghostscript
new_version: 10.0.0~dfsg-11+deb12u8
1:
current_version: 10.0.0~dfsg-11+deb12u7
name: libgs-common
new_version: 10.0.0~dfsg-11+deb12u8
2:
current_version: 10.0.0~dfsg-11+deb12u7
name: libgs10-common
new_version: 10.0.0~dfsg-11+deb12u8
3:
current_version: 10.0.0~dfsg-11+deb12u7
name: libgs10
new_version: 10.0.0~dfsg-11+deb12u8
4:
current_version: 4.5.0-6+deb12u2
name: libtiff6
new_version: 4.5.0-6+deb12u3
yunohost:
0:
current_version: 12.1.11
name: yunohost-admin
new_version: 12.1.12
1:
current_version: 12.1.27
name: yunohost
new_version: 12.1.28
Thanks anyway for this script, it’s a beautiful presentation of updates. Don’t you think it would be better to make it more visible with a dedicated topic? And why not, propose it for inclusion in Yunohost by default?
Yeah, that’s what I needed because I already have apticron that sends system updates but not yunohost apps updates. I’ll think about it.
Hey everyone,
Thanks a lot for sharing all these examples — this thread is a goldmine for anyone trying to customize YunoHost safely.
I recently started experimenting with hooks too, mainly to:
It’s honestly impressive how flexible the conf_regen mechanism is once you get the hang of it.
If anyone’s curious, I’d also recommend versioning your hooks in a small git repo — it helps track changes and roll back experiments easily.
Thanks again for the inspiration — especially to @metyun and @ljf for the detailed examples and explanations! ![]()