LXD Containers are very handy, and I use them for quite a few services on my home hobby & fun server. Here's how I set up my containers after a year of experimenting. Your mileage will vary, of course. You may have very different preferences than I do.
1. Networking:
I use macvlan networking. It's a simple, reliable, low-overhead way to pull an IP address from the network DHCP server (router). I set the IP address of many machines on my network at the router.
The container and server cannot communicate using TCP/UDP with each other. I don't mind that.
You only need to set up this profile once for all containers. Simply specify the profile when creating a new container.
// 'Host:$' means the shell user prompt on the LXD host system. It's not a shell command
// Learn the eth interface: enp3s5 in this example
Host:$ ip route show default 0.0.0.0/0
// Make mistakes on a copy
Host:$ lxc profile copy default lanprofile
// Change nictype field. 'eth0' is a virtual device, not a real eth device
Host:$ lxc profile device set lanprofile eth0 nictype macvlan
// Change parent field to real eth interface
Host:$ lxc profile device set lanprofile eth0 parent enp3s5
// Let's test the changes
Host:$ lxc profile show lanprofile
config: {}
description: Default LXD profile // This field is copied. Not really the default
devices:
eth0: // Virtual device
nictype: macvlan // Correct network type
parent: enp3s5 // Correct real device
type: nic
root:
path: /
pool: containers-disk // Your pool will be different, of course
type: disk
name: lanprofile
2. Creating a Container
Create a new container called 'newcon':
Host:$ lxc launch -p lanprofile ubuntu:focal newcon
// 'Host:$' - user (non-root) shell prompt on the LXD host
// '-p lanprofile' - use the macvlan networking profile
// 'focal' - Ubuntu 20.04. Substitute any release you like
3. Set the Time Zone
The default time zone is UTC. Let's fix that. Here are two easy ways to set the timezone: (source)
// Get a root prompt within the container for configuration
// Then use the classic Debian interactive tool:
Host:$ lxc shell newcon
newcon:# dpkg-reconfigure tzdata
// Alternately, here's a non-interactive way to do it entirely on the host
Host:$ lxc exec newcon -- ln -fs /usr/share/zoneinfo/US/Central /etc/localtime
Host:$ lxc exec newcon -- dpkg-reconfigure -f noninteractive tzdata
4. Remove SSH Server
We can access the container from the server at anytime. So most containers don't need an SSH server. Here are two ways to remove it
// Inside the container
newcon:# apt autoremove openssh-server
// Or from the Host
Host:$ lxc exec newcon -- apt autoremove openssh-server
5. Limit Apt sources to what the container will actually use
Unlike setting the timezone properly, this is *important*. If you do this right, the container will update itself automatically for as long as the release of Ubuntu is supported (mark your calendar!) If you don't get this right, you will leave yourself an ongoing maintenance headache.
// Limit the apt sources to (in this example) main from within the container
newcon:# nano /etc/apt/sources.list
// The final product should look similar to:
deb http://archive.ubuntu.com/ubuntu focal main
deb http://archive.ubuntu.com/ubuntu focal-updates main
deb http://security.ubuntu.com/ubuntu focal-security main
// Alternately, *push* a new sources.list file from the host.
# Create the new sources.list file on the host in /tmp
cat <<EOF > /tmp/container-sources.list
deb http://us.archive.ubuntu.com/ubuntu/ focal main
deb http://us.archive.ubuntu.com/ubuntu/ focal-updates main
deb http://security.ubuntu.com/ubuntu focal-security main
EOF
// *Push* the file from host to container
Host:$ lxc file push /tmp/container-sources.list newcon/etc/apt/sources.list
6. Install the Application
How you do this depends upon the application and how it's packaged.
7. Update Unattended Upgrades
This is the secret sauce that keeps your container up-to-date. First, let's look at a cleaned-up version of the first 20-or-so lines of /etc/apt/apt.conf.d/50unattended-upgrades inside the container:
What it says What it means
------------------------------------------ -----------------------
Unattended-Upgrade::Allowed-Origins {
"${distro_id}:${distro_codename}"; Ubuntu:focal
"${distro_id}:${distro_codename}-security"; Ubuntu:focal-security
// "${distro_id}:${distro_codename}-updates"; Ubuntu:focal-updates
// "${distro_id}:${distro_codename}-proposed"; Ubuntu:focal-proposed
// "${distro_id}:${distro_codename}-backports"; Ubuntu:focal-backports
};
...why, those are just the normal repositories! -security is enabled (good), but -updates is disabled (bad). Let's fix that. Inside the container, that's just using an editor to remove the commenting ("//"). From the host, it's a substitution job for sed:
Host:$ lxc exec newcon -- sed "s\/\ \g" /etc/apt/apt.conf.d/50unattended-upgrades
Third-party sources need to be updated, too. This is usually easiest from within the container. See this post for how and where to update Unattended Upgrades with the third-party source information.
8. Mounting External Media
Some containers need disk access. A classic example is a media server that needs access to that hard drive full of disorganized music.
If the disk is available across the network instead of locally, then use plain old sshfs or samba to mount the network share in /etc/fstab.
If the disk is local, then first mount it on the Host. After it's mounted, use an lxd disk device inside the container. A disk device is an all-in-one service: It creates the mount point inside the container and does the mounting. It's persistent across reboots...as long as the disk is mounted on the host.
// Mount disk on the host and test
Host:$ sudo mount /dev/sda1 /media
Host:$ ls /media
books movies music
// Create disk device called "media_mount" and test
Host:$ lxc config device add newcon media_mount disk source=/media path=/Shared_Media
Host:$ lxc exec newcon -- ls /Shared_Media
books movies music
If the ownership of files on the disk is confused, and you get "permisson denied" errors, then use shiftfs to do the equivalent of remounting without suid
Host:$ lxc exec newcon -- ls /Shared_Media/books
permission denied
// Enable shiftfs in LXD, reload the lxd daemon, and test
Host$ sudo snap set lxd shiftfs.enable=true
Host$ sudo systemctl reload snap.lxd.daemon
Host$ lxc info | grep shiftfs
shiftfs: "true"
// Add shiftsfs to the disk device
Host$ lxc config device set newcon media_mount shift=true
Host:$ lxc exec newcon -- ls /Shared_Media/books
boring_books exciting_books comic_books cookbooks