Preface
LXD provides the facility to store and apply "profiles".
A profile is a yaml structure which includes cloud-config
which can be parsed and implemented by cloud-init
.
cloud-config
specifies how to create an instance by allowing us to set such things as hostname, timezone, packages to install and custom actions to perform.
There are a great many configuration options; I've added a link to the docs in the Resources.
I created a profile called Homelab which I used for my new instances, wherever possible. It enables me to do some very cool things very quickly.
Overview
I'll provide a summary of what my profile does.
- Update and upgrade system packages to their latest versions.
- Reboot the instance if required.
- Set the timezone to UTC to avoid daylight savings time.
- Set the APT sources to use my archive.
- Install some useful applications.
- Attach my Ubuntu Pro token.
- Add rsyslog configuration to replicate logs to a central instance (I use this for logging experiments).
- Add a vimrc for all new users.
- Add a bashrc for all new users.
- Add a psqlrc for all new users.
- Instructs cloud-init to write a message to the console when complete, which I'll see when I watch the logs.
#cloud-config
package_update: true
package_upgrade: true
package_reboot_if_required: true
timezone: Etc/UTC
apt:
sources_list: |
Types: deb
URIs: http://_gateway.lxd:801/ubuntu/
Suites: noble noble-updates noble-backports
Components: main universe restricted multiverse
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
packages:
- apt:
- curl
- htop
- nginx
- rsync
- rsyslog
- tmux
- tree
- vim-gtk3
ubuntu_pro:
token: MY-TOKEN # Your token here
write_files:
- content: |
module(load="imjournal")
*.* action(type="omfwd" target="log-01.lxd" port="514" protocol="tcp" action.resumeRetryCount="100" queue.type="linkedList" queue.size="10000")
path: /etc/rsyslog.d/99-shipper.conf
append: false
- content: |
set tabstop=4
set shiftwidth=4
set expandtab
set ruler
set number relativenumber
set is hls
set title
set nospell
set nocompatible
set list
set listchars=eol:$,tab:>-,trail:~,extends:>,precedes:<
set ttymouse=xterm2
set mouse=a
cmap w!! w !sudo tee % >/dev/null
set nowrap
set breakindent
set scrolloff=10
set mouse=a
set mouse=a
set ttymouse=sgr
path: /etc/skel/.vimrc
append: false
permissions: 0600
- content: |
export EDITOR=vim
export VISUAL=vim
function set-pane-name() {
printf '\033]2;%s\033\\' "[host: $(hostname)]"
}
path: /etc/skel/.bashrc
permissions: 0600
append: true
- content: |
\pset pager on
\set ECHO_HIDDEN on
\pset null '[NULL]'
\timing on
\set fullexplain EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
path: /etc/skel/.psqlrc
permissions: 0600
append: true
- content: |
bind-key -n C-a send-prefix
set -g status-bg lightblue
set -g status-fg darkblue
bind-key -n C-a send-prefix
set -g status-bg lightblue
set -g status-fg darkblue
set -g default-terminal screen-256color
set -g default-shell /bin/bash
set -g history-limit 100000
# Borders
set -g pane-active-border-style bg=default,fg=lightblue
set -g pane-border-style fg=pink
set -g pane-border-status
set -g pane-border-format '#{pane_title}'
set -g status off
set -g mouse on
path: /etc/skel/.tmux.conf
permissions: 0640
append: true
final_message: |
Setup complete!
You will notice that I do not, by default, create users and upload SSH keys. I tend to rely on the default ubuntu user and not to generate new ones or to upload SSH keys. I will however review this in the near future as I have plans!
Is that it?
No, actually. The cloud-config is only one part of the profile.
More specifically, it is the config.user.vendor-data
part.
vendor-data losely translates from nerd-speak to "The stuff the organization that spawns the instance will set".
Here's a broader overview of the profile, minus the parts already presented.
name: Homelab
description: LXD profile to set up a variety of useful facilities
config:
user.vendor-data: |
#cloud-config
# ... you've seen this part ...
devices:
eth0:
name: eth0
network: lxdbr0
type: nic
root:
path: /
pool: zfs-lxd
type: disk
config
There is a myriad of other config
options which can be set, including config.user.user-data
which losely translates to "The stuff the administrator spawning the instance will set".
My apt-sources archive could potentially be moved into a separate yaml file and supplied at init/ launch time as user-data.
If we wanted to create a profile used to spin up an instance used to host docker/ podman, we could add config equivilent to the commands instructed by setup guide.
lxc config set docker-instance \
security.nesting=true \
security.syscalls.intercept.mknod=true \
security.syscalls.intercept.setxattr=true
as
config:
security.nesting: "true"
security.syscalls.intercept.mknod=true
security.syscalls.intercept.setxattr=true
Devices
The profile can also contain configuration for devices such as networking and storage. I use the LXD managed bridge NIC, lxdbr0 and my ZFS storage pool. The Instance Options documentation notes more of the possibilities.
Snapcraft
LXD is used to spawn containers in which to build snaps.
It's a great way to create an environment and separate it from one's system.
snapcraft
crates a LXD project of the same name which is home to the container(s).
The implication is that there is an associate profile therefore we can set up a custom profile with which to build snaps.
Round-up
Profiles are convenient and they are powerful. This was only a quick post to demonstrate how I tend to use LXD profiles and possibly encourage others to use them. If you use LXD but not profiles, I recommend you read the docs and see if they would improve your quality of life when spinning up instances.
Resources