Linux Container Primer (LXD/LXC and Multipass)
Quick Reference
- Launch
- incus launch images:debian/12/cloud debian-instancename-01 --profile default --profile gui2 -d root,size=30GiB
- incus launch images:nixos/unstable nixos-instancename-01 -c security.nesting=true
- incus launch images:ubuntu/22.04/cloud ubuntu-instancename-01 --profile default --profile gui2
- Connect
- incus exec instancename-01 -- su --login debian
- incus exec instancename-01 bash #connects via root
- incus exec instancename-01 -- sudo -u debian bash -c 'cd /home/debian/code && exec bash' #connects to a specific user and directory - useful for
go-xyzstyle aliases
LXD vs Incus
Please note that Canonical (maker of Ubuntu) created quite the disruption with the LXD team. As a result, Incus was created as a fork of LXD. All future ERP Academy use will involve Incus (not LXD). You can use all below commands by simply replacing "lxd" with "incus".
References:
LXC/LXD Linux Container Quick Start
If you are running Linux as your main operating system, LXC - Linux containers are an amazing tool! They are much lighter weight than virtual machines. Be aware that LXC/LXD has much overlap with Multipass functionality. If you are new to Linux, I would start with Multipass. More advanced users will want to start with LXC/LXD
Below are resources to help you get started with containers.
- First Look and Demo
- Getting started playlist
- Running GUI apps (firefox/eclipse) in a container (see below for exact configuration details)
- https://wiki.idempiere.org/w-en/index.php?title=LXD/LXC_Containers
- Docker (see below docker section for most recent)
- Creating projects in LXD
Incus Installation
See: https://github.com/zabbly/incus
Incus Cheatsheet
- Cheatsheets
- Common commands
- Images
- Find list of possible images
- incus image list images:
- Find list of possible images
- Cluster
- incus cluster list
- Container
- incus list
- #-c or --columns
- Example:
- lxc list -c nsapt
- lxc list type=container location=lxd0 status=running
- List of columns identifiers
- 4 - IPv4 address
- 6 - IPv6 address
- a - Architecture
- c - Creation date
- n - Name
- p - PID of the container's init process
- P - Profiles
- s - State
- S - Number of snapshots
- t - Type (persistent or ephemeral)
- incus launch
- example debian 12 with cloud init
- incus launch images:debian/12/cloud YourInstanceName
- You can check the status of the init script from within the instance using: cloud-init status
- Quick check status from host
- lxc exec YourInstanceName -- cloud-init status
- View cloud-init log from inside guest
- cat /var/log/cloud-init-output.log
- Add the following to the launch/init command to set drive, cpu and memory limiits
- -c limits.memory=4GiB -c limits.cpu=4 -d root,size=30GiB
- example specifying a specific node in a cluster
- incus launch --target yournodehere images:debian/12/cloud YourInstanceName
- example with profile
- incus launch imageserver:imagename instancename -p SomeOtherProfile -p default
- see below notes 'add profile to existing'
- see below notes about 'GuiContainer'
- example debian 12 with cloud init
- lxc connect
- lxc exec instancename -- su --login debian
- lxc execute command
- lxc exec YourInstanceName -- cloud-init status
- lxc exec YourInstanceName -- su --login debian bash -c "cd delme; tar -xvf sometarfile.tar"
- lxc export
- Notes
- ssh into running instance to see size: df -h
- sudo mkdir -p /root/backup/lxd/
- lxc export id-sand02 /root/backup/lxd/id-sand02--$(date +'%m-%d-%Y').tar.gz
- by default: backups located in /var/lib/incus/backups/instances
- by default: the file type is .tar.gz
- Notes
- lxc copy
- lxc copy id-source id-target --storage=remote --target=lxd0
- storage=remote is a storage pool defined previously. It is not required.
- target=lxd0 indicates the node in the cluster
- lxc copy id-source id-target --storage=remote --target=lxd0
- Add profile to existing container
- lxc profile add containter-name profile-name
- incus config
- incus config edit <container_name> # edit things like description
- lxc config set YourInstanceName security.protection.delete true
- incus list
- Storage pools - note that you can over allocate a pool (148GB allocated of 100GB pool)
- List pools
- incus storage list
- List details in pool (referencing the 'default' pool)
- incus storage info default
- Note: I do not know the difference between 'show' and 'info'
- Change the size of a pool (only increase)
- incus storage set default size=200GiB
- List pools
- Storage volumes (logical unit in a pool)
- List volumes of the default pool
- incus storage volume list default
- Delete an unused volume (an iso volume in the default pull in this case)
- incus storage volume delete default iso-nixos-volume
- List volumes of the default pool
- Images
Docker
As of incus 6.3, you can run docker images straight from the incus cli. Said another way, you no longer need to create an incus image and install the docker tools. Here are some example commands.
Add docker as a remote:
incus remote add docker https://docker.io --protocol=oci
List remotes:
incus remote list
Launch your first docker (where 'console' shows text and 'ephemeral' said delete yourself after execution complete):
incus launch docker:hello-world --console --ephemeral
Connect to your instances as root:
incus exec instance-name-here -- bash
Launch a docker container with an environment file (incus 6.4 feature):
incus launch docker:mysql mysql --environment-file mysql.env
Launching in iDempiere instance:
incus launch docker:idempiereofficial/idempiere --console
Notes about iDempiere deployment:
- The idempiereofficial/idempiere image does not install postgresql.
- Instead, it you need to create a postgresql instance first and pass in the environment to the respective instance using the above --environment-file example.
- I do not recommend running postgresql as a docker image. A database is much more permanent than an idempiere app server instance (which contains no real state).
Reference:
- https://blog.simos.info/running-oci-images-i-e-docker-directly-in-incus/
- https://discuss.linuxcontainers.org/t/incus-6-4-has-been-released/21323
Github Container Registry
The Github Container Repository (ghcr.io) is like dockerhub in that it hosts containers.
To add the ghcr.io to your Incus:
incus remote add github https://ghcr.io --protocol=oci
To launch an image from hgcr.oi:
incus launch github:open-webui/open-webui:main openwebui07 -d root,size=30GiB -c limits.cpu=2 -c limits.memory=4GiB --environment-file openwebui.env
Note: openwebui.env is where you put all the containers environment variables.
LXD Passthrough Route Making Services Accessible
Reference: https://www.youtube.com/watch?v=TmGvbXfwJEA&t=832s
Command to add pass through for 443 (https):
lxd config device add ContainerName www proxy listen=tcp:192.168.1.214:443 connect=tcp:0.0.0.0:443
Where:
- Change the "ContainerName" to the name of your container
- Change 192.168.1.214 to the IP of your host machine (found via "ip a" command)
- Change 443 to the port you wish to use.
- Note: the ports do not need to be the same (ex: host port 444 can map to container port 443)
- I tried to use "proxy_protocol=true" like the video; however, I kept getting a ssl error.
Command to remove the above pass through:
lxd config device remove ContainerName www
Command to show pass through:
incus config device show ContainerName
LXD Profile Management
Create a new profile in LXD:
lxc profile create cloud-profile1
Apply a pre-created textfile to the new profile:
cat cloud-profile1.profile | lxc profile edit cloud-profile1
Apply the profile during incus launch with flag -p:
incus launch imageserver:image instancename -p cloud-profile1
LXD/LXC GuiContainer Example Profile
The following profile enables you to run gui apps from inside your container as if they are actually running on your host machine. The advantage of this configuration is that you can install and run a vpn and firefox from within your container without touching your host networking configuration. Said another way, you can run many containers at the same time... Each running a different vpn to different destinations.
Notes about the following profile:
- The three config => security values allow for firefow sandboxing and docker
- This profile shows using debian (not ubuntu) (search and replace if needed)
- This profile references GPU. There is more needed to use a gpu.
- For reference from previous testing - additional host commands needed:
- lxc config device add ContainerName gpu gpu gputype=physical
- lxc config set ContainerName nvidia.runtime=true
- restart the container after running this command to share the nvidia drivers and be able to run: nvidia-smi
- Inside the container install pciutils to make the gpu observable via tools like ollama
- sudo apt install pciutils
- For reference from previous testing - additional host commands needed:
? incus profile show gui2
config:
environment.DISPLAY: :1
raw.idmap: both 1000 1000
security.nesting: "true"
security.syscalls.intercept.mknod: "true"
security.syscalls.intercept.setxattr: "true"
user.user-data: |
#cloud-config
runcmd:
# Create symlinks in expected locations (works even if /tmp is tmpfs)
- mkdir -p /tmp/.X11-unix
- ln -sf /mnt/host-x11/X1 /tmp/.X11-unix/X1
- mkdir -p /tmp/.pulse-native
- ln -sf /mnt/host-pulse/native /tmp/.pulse-native/
- 'sed -i "s/; enable-shm = yes/enable-shm = no/g" /etc/pulse/client.conf'
- 'echo export PULSE_SERVER=unix:/tmp/.pulse-native | tee --append /home/debian/.profile'
- [su, debian, -c, "git clone https://github.com/chuboe/chuboe-system-configurator /home/debian/chuboe-system-configurator"]
- [su, debian, -c, "cd /home/debian/chuboe-system-configurator/; ./init.sh"]
packages:
- x11-apps
- mesa-utils
- pulseaudio
- git
- ufw
description: GUI LXD profile
devices:
PASocket:
path: /mnt/host-pulse/
source: /run/user/1000/pulse/
type: disk
X0:
path: /mnt/host-x11/
source: /tmp/.X11-unix/
type: disk
mygpu:
type: gpu
name: gui2
You can launch a new gui container with the following command:
incus launch images:debian/12/cloud YourInstanceName --profile default --profile gui2
Notes:
- My system uses Display :1. I know this fact by issuing the command from my host:
- echo $DISPLAY
- If your system returns :0 update the profile accordingly.
- environment.DISPLAY: :0
- path: /tmp/.X11-unix/X0
- source: /tmp/.X11-unix/X0
- The above launch command uses a 'cloud' version of the container. This is important because the cloud versions include CloudInit. The gui profile uses CloudInit to install important software.
- You can use the following command to see if the CloudInit script is complete
- cloud-init status
- You can add as many profiles to an instance/container as you wish/need.
- Enter your container using this command:
- lxc exec zito2 -- su --login debian
- Echo your display in your container to see if it is set. If not, set is manually:
- export DISPLAY=:1
- Run firefox
- firefox &
- You should now see firefox as if it is running natively on your system.
- Firefox on debian
- sudo apt install firefox-esr -y
- Firefox on Ubuntu: There are times when you need to install firefox manually from the mozilla ppa. Here are the commands:
- echo $'\n' | sudo add-apt-repository ppa:mozillateam/ppa
- echo "Package: firefox*" | sudo tee -a /etc/apt/preferences.d/mozillateamppa
- echo "Pin: release o=LP-PPA-mozillateam" | sudo tee -a /etc/apt/preferences.d/mozillateamppa
- echo "Pin-Priority: 501" | sudo tee -a /etc/apt/preferences.d/mozillateamppa
- sudo apt update
- sudo apt install -y firefox
Cloud Init Details
- Quick status
- incus exec <container-name> -- cloud-init status
- Wait until done
- incus exec <container-name> -- cloud-init status --wait
- View cloud-init logs
- incus exec <container-name> -- cat /var/log/cloud-init.log
- View cloud-init logs (the actual script)
- incus exec <container-name> -- cat /var/log/cloud-init-output.log
- Tail cloud-init logs (the actual script)
- incus exec <container-name> -- tail -f /var/log/cloud-init-output.log
Incus Shared Directory
keywords: drive,folder.map
- Make a host (local) directory
- mkdir -p ~/share-incus/SomeContainer
- Make a guest directory
- connect to the guest container (SomeContainer)
- mkdir -p ~/share-host/
- Exit from the guest container and issue the following from the host:
- incus config device add SomeContainer SomeContainer-Share disk source=/home/cb/share-incus/SomeContainer/ path=/home/debian/share-host/
- append
shift=trueto the end of theincus config device add ...command to aid in mapping uids. This is needed when the container's directory is locked/read-only.
- Summary commands => simply update CHUBOE_CONTAINER variable:
- cd ~/ # go to home directory
- CHUBOE_CONTAINER=SomeContainer
- CHUBOE_CONTAINER_SHAREDIR="/home/debian" # debian or
- CHUBOE_CONTAINER_SHAREDIR="/root" # for nixos
- CHUBOE_PWD=$(pwd)
- mkdir -p $CHUBOE_PWD/share-incus/$CHUBOE_CONTAINER
- incus exec $CHUBOE_CONTAINER -- mkdir -p $CHUBOE_CONTAINER_SHAREDIR/share-host
- incus config device add $CHUBOE_CONTAINER "$CHUBOE_CONTAINER"-share disk source=$CHUBOE_PWD/share-incus/$CHUBOE_CONTAINER/ path=$CHUBOE_CONTAINER_SHAREDIR/share-host/
Add a New Remote Server
#on the client - get/generate a certificate incus remote get-client-certificate | tee /tmp/cert.txt #on the server - add the client certificate incus config trust add-certificate /path/to/cert.txt #on the client - give the remote a meaningful name incus remote add <remote-name> https://<server-ip>:<port:8443>
Connect and Manage Remote Clusters
Connect to a remote server:
incus remote switch some-remote
Confirm looking at remote to ensure you see different instances:
incus list
While in the remote cluster, you might belong to more than one project. To list projects:
incus project list
The table will indicate the current project. To switch projects:
incus project switch some-other-project
Switch back to your local cluster/server:
incus remote switch local
Incus Debian Desktop
Here are the steps to install a Debian 12 gnome desktop:
- Launch a new incus virtual machine
- incus launch images:debian/12/cloud delme-debian-desktop-03 --vm -d root,size=25GiB -c limits.cpu=2 -c limits.memory=4GiB
- delme-debian-desktop-03 > Install gnome
- sudo apt update
- sudo apt install gnome
- delme-debian-desktop-03 > If error: addgroup: The group netdev already exists and is not a system group. Exiting.
- sudo groupmod -n netdev_backup netdev
- sudo apt install gnome # will finish now
- Restart virtual machine
- incus restart delme-debian-desktop-03
- Conncet to virtual machine
- incus console delme-debian-desktop-03 --type vga
Incus on Mac/Apple
$brew install colima
$brew install incus
$colima start —runtime incus
INFO[0000] starting colima
INFO[0000] runtime: incus
INFO[0002] creating and starting … context=vm
INFO[0002] downloading disk image … context=vm
INFO[0033] provisioning … context=incus
INFO[0034] starting … context=incus
INFO[0035] done
Gives me:
$incus version
~> incus version
Client version: 6.17
Server version: 6.16
~> incus list
+------+-------+------+------+------+-----------+
| NAME | STATE | IPV4 | IPV6 | TYPE | SNAPSHOTS |
+------+-------+------+------+------+-----------+
Network Isolation Between Hosts/Projects/Bridges
Discussion: https://discuss.linuxcontainers.org/t/network-isolation-by-project-on-single-server-incus-host/19843
I have a single incus host server (not clustered). I want to be able to isolate instances (from a networking perspective) to only communicate with containers in the same project and still be able to communicate with the outside world. My assumption is that you can create a bridged network per project; however, I do not know the best way to prevent communication between network bridges.
I also believe that incus firewall management can interfere with any iptable rules that I create (and visa versa).
Block traffic:
sudo iptables -A FORWARD -i incusbr+ -o incusbr+ -j REJECT
Allow for exceptions if needed:
sudo iptables -A FORWARD -i incusbr4 -o incusbr8 -j ACCEPT
I know that you can create complete isolation using the following; however, I believe this leaves me using a tailscale type solution to support networking. Is this correct?
security.ipv4_filtering=true (prevents IPv4 spoofing) security.ipv6_filtering=true (prevents IPv6 spoofing) security.mac_filtering=true (prevents MAC spoofing) security.port_isolation=true (prevents container to container traffic)
Here was Stephane's response to the above prompt. It includes an example of how to create and manipulate an ACL rule.
Quick experiment for isolating network bridging and incus backup - worth recording for future reference. This was execute in phoenixnap.com => bare metal server:
9 sudo adduser ubuntu incus-admin 10 exit 17 incus admin init 18 incus list 19 incus network list 20 incus network show incusbr0 21 incus network create incusbr1 22 incus network show incusbr1 23 ip a 25 df -h 27 lsblk 28 incus launch images:debian/12/cloud delme-01-br0 30 incus list 31 incus launch images:debian/12/cloud delme-02-br0 32 incus list 33 incus launch images:debian/12/cloud delme-02-br0 -n incusbr1 34 incus launch images:debian/12/cloud delme-03-br1 -n incusbr1 36 incus list 37 incus network list 38 incus instance show delme-01-br0 39 incus show delme-01-br0 42 incus exec delme-01-br0 -- bash 46 incus network show incusbr0 47 incus network show incusbr1 48 sudo iptables -A FORWARD -i incusbr+ -o incusbr+ -j REJECT 53 sudo shutdown -r now # reset iptables since not persisted 56 incus network acl list 57 incus network acl show 59 incus network 60 incus network show 61 incus network list 62 incus network acl create delme-acl-01 63 incus network acl list 64 incus network acl show 65 incus network acl show delme-acl-01 66 incus network list 67 incus network show incusbr1 69 incus network show incusbr1 70 sudo iptables -A FORWARD -i incusbr+ -o incusbr+ -j REJECT 74 sudo shutdown -r now 81 cd 82 mkdir delme 83 cd delme 84 incus admin sql local .dump > some_dump 85 cat some_dump 86 cat some_dump | grep inc 87 incus admin sql global .dump > some_dump 88 cat some_dump 90 cat /etc/subgid
Multipass
This section was created before the above Incus announcement. I do not have an opinion on Multipass now that that I use Incus. I will update this section accordingly.
Multipass is a Canonical/Ubuntu product designed to allow Windows, Mac and Linux users to run Ubuntu servers. Eventually, Multipass will sit on top of LXD in the case of Linux. This is exciting because we may be able to share containers from Linux developers in LXD to Windows users of Multipass.
My experience with Multipass on both Mac and Windows is positive. It is much better the WSL in my experience. Be aware that Multipass is a terminal based tool where you issue commands like:
- multipass launch --name some-server-name
- multipass list
- multipass shell some-server-name
- multipass stop some-server-name
- multipass delete some-server-name
- multipass purge
- multipass launch --name idv1 --cpus 2 --disk 20G --memory 4G
Even though Multipass is administered via a command line, the commands are easy to understand and well documented by most people.
Windows users can make use of VcXsrv to run applications in a Linux container and have the application appear near-natively in Windows. Ideally, a developer using Linux can create an development environment image that is consumed by a windows => multipass user. The windows user just fires up the container from the dev image, fires up the dev environment (eclipse for example) inside the container and uses it as if it was local to the machine.
References:
- https://multipass.run/docs/how-to-guides (installs on window, mac, linux)
- https://multipass.run/docs/docker-tutorial (gives you near-local docker and compose capabilities through a specially created multipass docker image)