I am learning of some limitations of using LXD to containerize apps. I mean, LXD is nice, and it works great as designed. I have tried the default configuration, with lxdbr0 bridge and filesystem backing, and found I get more use out of one where I run the host’s ethernet interface as a slave to bridge br0, and add br0 (unmanaged) to the LXD default profile instead of lxdbr0, so each container gets peer level networking on it’s host. I also attach an external disk as LVM backing for LXD, so each of my containers get take advantage of LVM snapshots.
You have to be careful with firewall rules with LXD, but you do have the option of 1) simple ufw rules on each container, or 2) host level ufw rules that control all the forwarding. You already have to add some lines to /etc/ufw/before.rules and before6.rules, to NAT all outbound IP traffic and DNAT inbound ports to services you run. I found the DNAT rules painful for IPV6 addresses, and think I probably need to enable both DHCP6 support, and static IP v4 and v6 addresses for each container.
Or, I could go old school, rebuild the host and isntead of LXD, run LXC version 1 to containerize the applications using the host’s native IP network stacks, meaning no NAT or DNAT will be needed. Just plain simple ufw rules at the host level. LXC is more complex to setup, but is more flexible in some ways.
Or, I could go REALLY old school, and do it 80s style, where postfix, mysqld, and nginx all run native on the host. The absolute least amount of overhead, only 1 set of iptables rules, which might make more sense on a 1 vcpu/1 GB RAM/25 GB disk VPS, but somewhat less secure.