Custom ArchLinux LXC Templates for Proxmox

For a long time I used ESXi to virtualize my local network services, suddenly one day I ran out of memory. The Intel NUC I was using had 16G of memory which should have been plenty but was exhausted by the number of virtual machines I was running. After dismissing a hardware upgrade due to cost I discovered Proxmox and its first class LXC container support. Suddenly my 16G of memory was ample even double the services running.

Creating a new container, I usually ran a few commands before SSH was available (my preferred ArchLinux image does not have it enabled by default) and subsequently executed an Ansible playbook. This grew tiresome over the last few years but I never did it enough to worry about automating it. Well now in 2021 I’m preparing for a full Proxmox rebuild due to hardware changes and decided to look into making things a little easier.

The following is a guide on how I setup my custom ArchLinux LXC template to be deployed at a moments notice.

Create LXC Container

First create your LXC container as you normally would. I personally prefer ArchLinux as that is what I’m use too:

Create ArchLinux LXC Container

Ensure you enable nesting under the options:

Enable Nesting

Configure ArchLinux

Modify /etc/locale.gen and uncomment “en_US.UTF-8 UTF-8”:

#en_SG.UTF-8 UTF-8  
#en_SG ISO-8859-1  
en_US.UTF-8 UTF-8  
#en_US ISO-8859-1  
#en_ZA.UTF-8 UTF-8

Set that locale in /etc/locale.conf and generate:

echo 'LANG=en_US.UTF-8' > /etc/locale.conf 
locale-gen

Setup pacman-key and populate:

pacman-key --init && pacman-key --populate archlinux

Uncomment a relevant mirror in /etc/pacman.d/mirrorlist (in my case Australian):

## Arch Linux repository mirrorlist
## Generated on 2020-04-11
##

## Worldwide
#Server = http://mirrors.evowise.com/archlinux/$repo/os/$arch
#Server = http://mirror.rackspace.com/archlinux/$repo/os/$arch
#Server = https://mirror.rackspace.com/archlinux/$repo/os/$arch

## Australia
Server = https://mirror.aarnet.edu.au/pub/archlinux/$repo/os/$arch
#Server = http://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch
#Server = https://archlinux.mirror.digitalpacific.com.au/$repo/os/$arch

First retrieve the latest archlinux-keyring to ensure keys are up to date:

pacman -Sy archlinux-keyring

Perform a full system upgrade to bring everything up to date. Additionally installing:

  • vim - for easier configuration file modifications
  • pacman-contrib - to resolve conflicts
  • sudo - for setup later
pacman -Su vim pacman-contrib sudo

Resolve any pacnew files with pacdiff, keeping the base image nice and clean. Configure your local timezone:

ln -sf /usr/share/zoneinfo/Australia/State /etc/localtime

Configure System

Change root password with passwd if required (Note: This password is overridden each time the container is created inside Proxmox).

Create sudo group:

groupadd sudo

Add a regular user account:

useradd -G users,sudo -s /bin/bash -m username

Change the password with passwd username. Next update the sudoers file by running visudo and un-commenting the following line:

## Uncomment to allow members of group sudo to execute any command
%sudo   ALL=(ALL) ALL

This will allow the regular account sudo access. Add your SSH key to the authorized_keys file:

mkdir /home/username/.ssh
cp authorized_keys /home/username/.ssh/
chown -R username:username /home/username/.ssh
chmod 700 /home/username/.ssh
chmod 600 /home/username/.ssh/authorized_keys

Check SSHD settings are correct and secure in /etc/ssh/sshd_config. Do not allow password authentication or root access:

PermitRootLogin no
PasswordAuthentication no
UserPAM no
AllowUsers username

Enable SSHD on startup:

systemctl enable sshd

Security Settings

Some extra security settings that I apply. /dev/null several files that may contain sensitive information:

ln -sf /dev/null /root/.viminfo
ln -sf /dev/null /root/.bash_history
su - username
ln -sf /dev/null /home/username/.viminfo
ln -sf /dev/null /home/username/.bash_history

Install common utilities

Install utilities I find myself using on most machines:

pacman -S base-devel htop git vim curl wget reflector

Setup Reflector to keep mirrorlist up to date. Append settings too /etc/xdg/reflector/reflector.conf:

--save /etc/pacman.d/mirrorlist
--country Australia
--protocol https
--latest 5

Enable the systemd timer as described in the wiki entry: https://wiki.archlinux.org/index.php/Reflector. Add NoExtract to /etc/pacman.conf to stop pacnew entries for mirrorlist. Reflector now takes care of this for us.

NoExtract = etc/pacman.d/mirrorlist

Bugs

Due to this issue for some reason systemd-journald-audit.socket fails causing systemd to report a degraded system. Temporary fix is to:

systemctl mask systemd-journald-audit.socket

I’m still not sure why this happens or is required.

Cleanup

Remove any .ssh directory for the root user to ensure SSH keys do not exist:

rm -r /root/.ssh

Revert /etc/resolve.conf to ensure current settings do not persist.

Once you are happy with your base image, shutdown the container.

Creating the template

Back in your Proxmox interface select the container then “Backup” and select “Backup Now”. Ensure you select “GZIP” compression and hit “Backup”:

Backup the container

This may take a few minutes. In the output of status window you can see the path to the backup file:

Backup complete

Once complete you can copy the file over to your Proxmox templates directory /var/lib/vz/template/cache/ via the shell feature with:

cp /var/lib/vz/dump/vzdump-lxc-[CT#]-[TIMESTAMP].tar.gz /var/lib/vz/template/cache/template.tar.gz

You should now see your new template when you go to create a new container:

Template

That’s it! Your new container should start with your pre-configured base image making things a little easier.

Every year I refresh the base image in case I ever need to re-build a container.

Notes

  • Specifying the root password in the Proxmox interface will overwrite the base image password
  • Make sure you enable “nesting” each time you create a container
  • Updating the base image is as simple as creating a new container, updating and executing the backup procedure again