UFW will purge any MANUALLY added rule in
/etc/ufw/user.rules which is NOT prefaced with a comment:
### tuple ### allow tcp 80 0.0.0.0/0 any 0.0.0.0/0 out
-A ufw-user-output -p tcp --dport 80 -j ACCEPT
When UFW sanity checks the rules on startup, it expects an accompanying comment. If it's NOT present, even if the syntax of the rule is correct, UFW will still purge it.
And don't just use any arbitrary comment: it MUST be the comment that UFW would insert when creating a user rule via the cli, ie:
sudo ufw allow http/tcp
So if you want to pre-seed a series of rules in a ruleset in a simple file, you'd still need to create the rules via UFW's CLI interface to learn the syntax of the comments it expects for the rule to pass validation and persist.
Try the foregoing with and WITHOUT the comment and reload the specimen HTTP rule above; you'll remark only with the comment does the manually added rule survive a restart (ufw enable) of UFW.
This is really counter-intuitive behaviour and not documented at all.