Stagger overnight APT maintenance

master
Carlo Costanzo 21 hours ago
parent 4c033277b2
commit 49267b76f3

@ -9,11 +9,11 @@
# Video: https://youtu.be/aceV7ObKJVA # Video: https://youtu.be/aceV7ObKJVA
# Blog: https://www.vcloudinfo.com/2026/07/using-codex-clean-up-home-assistant-entities.html # Blog: https://www.vcloudinfo.com/2026/07/using-codex-clean-up-home-assistant-entities.html
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# Notes: Hosts run daily read-only APT pending checks plus Mon/Thu 12:00 APT jobs. # Notes: Hosts run daily read-only APT pending checks plus staggered Mon/Thu overnight APT jobs.
# Notes: All Docker hosts use America/New_York so staggered maintenance shares one local window. # Notes: All Docker hosts use America/New_York; maintenance starts at 00:30 and is sequenced by host.
# Notes: Reboots are handled directly on each host by apt_weekly.sh. # Notes: Reboots are handled directly on each host by apt_weekly.sh.
# Notes: Boot reports reuse the registered APT webhook and retry while Home Assistant starts. # Notes: Boot reports reuse the registered APT webhook and retry while Home Assistant starts.
# Notes: Reboot staggering: docker_14, docker_69, docker_17, docker_10. # Notes: Reboot order: docker_14, docker_69, docker_17, docker_10; codex-appliance follows after its SMB dependencies recover.
# Notes: Per-host action-due helpers preserve overdue state across daily checks and HA restarts. # Notes: Per-host action-due helpers preserve overdue state across daily checks and HA restarts.
# Notes: Monitored workload count deduplicates Portainer `_container_2` companion entities. # Notes: Monitored workload count deduplicates Portainer `_container_2` companion entities.
# Notes: Container monitoring is dynamic with binary_sensor status preferred over switch state. # Notes: Container monitoring is dynamic with binary_sensor status preferred over switch state.
@ -154,6 +154,7 @@ switch:
- switch.nebula_sync_container_2 - switch.nebula_sync_container_2
- switch.onenote_indexer_container - switch.onenote_indexer_container
- switch.onenote_indexer_container_2 - switch.onenote_indexer_container_2
- switch.onenote_indexer_stacey_container
- switch.paiges_bookshelf_container - switch.paiges_bookshelf_container
- switch.panel_notes_container - switch.panel_notes_container
- switch.panel_notes_container_2 - switch.panel_notes_container_2
@ -965,24 +966,28 @@ automation:
last_result: input_text.apt_docker_10_last_result last_result: input_text.apt_docker_10_last_result
action_due: input_datetime.apt_docker_10_action_due action_due: input_datetime.apt_docker_10_action_due
reboot_delay: 20 reboot_delay: 20
maintenance_offset: 45
docker_14: docker_14:
last_check: input_datetime.apt_docker_14_last_check last_check: input_datetime.apt_docker_14_last_check
last_update: input_datetime.apt_docker_14_last_update last_update: input_datetime.apt_docker_14_last_update
last_result: input_text.apt_docker_14_last_result last_result: input_text.apt_docker_14_last_result
action_due: input_datetime.apt_docker_14_action_due action_due: input_datetime.apt_docker_14_action_due
reboot_delay: 5 reboot_delay: 5
maintenance_offset: 0
docker_17: docker_17:
last_check: input_datetime.apt_docker_17_last_check last_check: input_datetime.apt_docker_17_last_check
last_update: input_datetime.apt_docker_17_last_update last_update: input_datetime.apt_docker_17_last_update
last_result: input_text.apt_docker_17_last_result last_result: input_text.apt_docker_17_last_result
action_due: input_datetime.apt_docker_17_action_due action_due: input_datetime.apt_docker_17_action_due
reboot_delay: 15 reboot_delay: 15
maintenance_offset: 30
docker_69: docker_69:
last_check: input_datetime.apt_docker_69_last_check last_check: input_datetime.apt_docker_69_last_check
last_update: input_datetime.apt_docker_69_last_update last_update: input_datetime.apt_docker_69_last_update
last_result: input_text.apt_docker_69_last_result last_result: input_text.apt_docker_69_last_result
action_due: input_datetime.apt_docker_69_action_due action_due: input_datetime.apt_docker_69_action_due
reboot_delay: 10 reboot_delay: 10
maintenance_offset: 15
host_helpers: "{{ helpers[host_id] if host_id in helpers else none }}" host_helpers: "{{ helpers[host_id] if host_id in helpers else none }}"
previous_result: "{{ states(host_helpers.last_result) | upper }}" previous_result: "{{ states(host_helpers.last_result) | upper }}"
previous_actionable: >- previous_actionable: >-
@ -997,14 +1002,14 @@ automation:
{{ actionable and {{ actionable and
(not previous_actionable or due_missing or previous_reboot != reboot_required) }} (not previous_actionable or due_missing or previous_reboot != reboot_required) }}
action_due: >- action_due: >-
{% set noon = today_at('12:00') %} {% set maintenance_at = today_at('00:30') + timedelta(minutes=host_helpers.maintenance_offset | int(0)) %}
{% set weekday = now().weekday() %} {% set weekday = now().weekday() %}
{% set maintenance_report = event_kind == 'maintenance' %} {% set maintenance_report = event_kind == 'maintenance' %}
{% set current_window = maintenance_report and weekday in [0, 3] {% set current_window = maintenance_report and weekday in [0, 3]
and now() >= noon and now() < noon + timedelta(hours=4) %} and now() >= maintenance_at and now() < maintenance_at + timedelta(hours=4) %}
{% if current_window %} {% if current_window %}
{% set offset = 0 %} {% set offset = 0 %}
{% elif weekday in [0, 3] and now() < noon %} {% elif weekday in [0, 3] and now() < maintenance_at %}
{% set offset = 0 %} {% set offset = 0 %}
{% elif weekday < 3 %} {% elif weekday < 3 %}
{% set offset = 3 - weekday %} {% set offset = 3 - weekday %}
@ -1012,7 +1017,7 @@ automation:
{% set offset = 7 - weekday %} {% set offset = 7 - weekday %}
{% endif %} {% endif %}
{% set delay = host_helpers.reboot_delay if reboot_required else 0 %} {% set delay = host_helpers.reboot_delay if reboot_required else 0 %}
{{ (noon + timedelta(days=offset, minutes=delay)).strftime('%Y-%m-%d %H:%M:%S') }} {{ (maintenance_at + timedelta(days=offset, minutes=delay)).strftime('%Y-%m-%d %H:%M:%S') }}
result: >- result: >-
{% set security = ' (' ~ security_packages ~ ' SEC)' if security_packages > 0 else '' %} {% set security = ' (' ~ security_packages ~ ' SEC)' if security_packages > 0 else '' %}
{% if not success %} {% if not success %}

@ -1,7 +1,7 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# Twice-weekly APT maintenance for Docker hosts (Mon/Thu 12:00 via systemd timer). # Twice-weekly overnight APT maintenance for Docker hosts (host-specific Mon/Thu timers).
# Posts results to Home Assistant webhook and optionally schedules reboot when required. # Posts results to Home Assistant webhook and optionally schedules reboot when required.
WEBHOOK_URL="$1" WEBHOOK_URL="$1"

@ -1,8 +1,9 @@
[Unit] [Unit]
Description=Run APT maintenance twice weekly Description=Run APT maintenance twice weekly overnight
[Timer] [Timer]
OnCalendar=Mon,Thu *-*-* 12:00:00 # Deploy host-specific overnight slots from docs/agent_ops_baselines.md.
OnCalendar=Mon,Thu *-*-* 00:30:00
Persistent=true Persistent=true
Unit=apt_weekly.service Unit=apt_weekly.service

Loading…
Cancel
Save

Powered by TurnKey Linux.