The firewall script deleted its rules before re-inserting them, and Docker
restarted the stack on its own (restart: unless-stopped) with no ordering
against the firewall unit, so port 8090 could be open to the LAN while the
rules were reapplied.
- mealquest-firewall.sh fails closed: if the chain is not already exactly
right, it inserts the DROP at position 1 first, deletes its other tagged
rules by number, then inserts the two RETURN rules above it. A correct
chain is left untouched.
- New mealquest.service owns the compose stack: Requires= and After=
mealquest-firewall.service, PartOf=docker.service.
- compose.yaml: restart: "no" on both services, so Docker never starts
them before the firewall.
- RUNBOOK: deploy, rollback, restore and key rotation start and stop the
stack with systemctl; documents the stack unit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>