Installing Calculate in LXC
Updated 7 November 2026

cl-lxc is a command-line utility for creating, configuring and updating LXC containers with Calculate Linux. This page is for those who deploy and maintain such containers on a host running Calculate Linux: the container is prepared once and is then updated with prepared images - the same way Calculate Linux does on the host itself.
This page applies to cl-lxc 0.1.5.
Purpose of the utility
Compared with building a container by hand, cl-lxc has several features:
- the container is updated with prepared images: first the new rootfs image is prepared (--prepare), then the container is switched to it (--upgrade) - the same way Calculate Linux installed on the host is updated; the switch itself takes seconds;
- the container's /var/calculate is moved out to a separate directory next to the container and bind-mounted into it - the same scheme as the default partitioning of Calculate Linux itself, where /var/calculate is on a separate partition: it is larger and stays untouched when the distribution is reinstalled. The container data thus survives the image switch;
- the container is created together with Calculate templates that carry over the settings of the binary mirror and the network service;
- the base image is downloaded from the mirror once and cached - each new container does not access the network;
- the Portage trees, binary packages and distfiles are mounted from the host (bind), so a fresh container takes about 1.5 GB instead of a full copy of the system; for reference, one shared Portage tree takes roughly 600 MB - that is exactly how much is not duplicated in every container, while the package and distfile cache grows to several gigabytes over time;
- on btrfs the cache and the containers are created as subvolumes, and the rootfs is copied using reflink.
The utility comes from calculate-toolkit, where it appeared in 2022, and was later split out into a separate package.
Installation
Install the app-containers/cl-lxc package:
emerge -a cl-lxc
The utility works together with app-containers/lxc, which is installed automatically as a dependency. All commands on this page are run as root.
Host requirements
cl-lxc keeps containers in the directory set by the global LXC option lxc.lxcpath, which must point to /var/calculate/lxc. In Calculate Linux this value is set for you: when app-containers/lxc is installed, /etc/lxc/lxc.conf is merged by a template from the distribution.
The Portage trees, binary packages and distfiles are mounted into the container from the host - on Calculate Linux they are there by default and you do not need to create them: these are the repositories in /var/db/repos, the binary packages in /var/cache/edb/binhost and the package archives in /var/calculate/packages and /var/calculate/distfiles. The Portage trees and /var/cache/edb/binhost are mounted read-only - the container cannot change them. Writing is allowed to packages and distfiles, but tampering is not a risk there: the Portage trees record the checksums of the source packages, and the binary packages have their checksums in the /var/cache/edb/binhost index.
A network bridge on the host is not required. If there is no bridge, the container stays in the network namespace of the host and uses its addresses. If /sys/class/net contains an interface matching br*, the container is given a virtual veth interface with a random locally administered MAC address, bound to that bridge.
Syntax and options
Usage: cl-lxc [OPTIONS?] <container_name>. The container name is the only positional argument; it goes at the end of the line. The name must start with a letter and may contain Latin letters, digits, underscores, hyphens and dots; the name scratch is reserved by the base image.
| Option | Argument | Action |
|---|---|---|
| -P, --profile | required | Container profile; the special value list prints the list of profiles |
| --url | required | URL of the profiles git repository |
| -c, --create | — | Create a new container |
| --path | required | Store the container at the specified path (a link remains in /var/calculate/lxc) |
| -p, --prepare | — | Prepare a rootfs image for update |
| -u, --upgrade | — | Update the container from the prepared image |
| --diff | — | Show the package differences between versions |
| -R, --clear_cache | — | Clear the cached images |
| -m, --mirror | required | Calculate Linux mirror, by default mirror.calculate-linux.org |
| -h, --help | — | Full help on the options |
Not all options have a short form: --url, --path and --diff are available in the long form only.
Creating a container
Create a container:
* Downloading Calculate Linux ... [ ok ] Creating container test * Copying base rootfs ... [ ok ] * Preparing base mount paths ... [ ok ] * Moving /var/calculate to a separate mount point ... [ ok ] * Running container setup ... [ ok ] * Configuring utility variables ... [ ok ] * Preparing first boot setup script ... [ ok ] * Creating templates ... [ ok ] Your container is ready. To start it, please run 'lxc-start test'.
The "Downloading Calculate Linux" line appears on the first creation only - the downloaded base image stays in the cache, and the following containers are built from it. If a container with the same name already exists, the utility refuses to create it.
After creation, the following appear on disk:
- /var/calculate/lxc/test/config - the LXC container configuration;
- /var/calculate/lxc/test/rootfs/ - the root filesystem;
- /var/calculate/lxc/test/calculate/ - the directory that is mounted inside the container as /var/calculate (as in the default partitioning on full systems): update variables, templates and the container data;
- /var/calculate/lxc/.cache/scratch/ - the base image cache.
The generated container configuration file looks like this:
#------------------------------------------------------------------------------ # Modified cl-lxc-0.1.5 # For additional config options, please look at lxc.container.conf(5) #------------------------------------------------------------------------------ #------------------------------------------------------------------------------ # Distribution configuration #------------------------------------------------------------------------------ lxc.include = /usr/share/lxc/config/common.conf lxc.arch = x86_64 #------------------------------------------------------------------------------ # Network configuration #------------------------------------------------------------------------------ lxc.net.0.type = none #------------------------------------------------------------------------------ # Security configuration #------------------------------------------------------------------------------ #lxc.cap.drop = #------------------------------------------------------------------------------ # Lifecycle and shutdown behavior #------------------------------------------------------------------------------ # Use SIGPWR for halt signal to stop container instantly with openrc-init lxc.signal.halt = SIGPWR #------------------------------------------------------------------------------ # Filesystem / rootfs #------------------------------------------------------------------------------ lxc.rootfs.path = dir:/var/calculate/lxc/test/rootfs lxc.uts.name = test # Mount /dev/shm as tmpfs lxc.mount.entry = none dev/shm tmpfs rw,nosuid,nodev,create=dir # Bind mount points lxc.mount.entry = /var/calculate/lxc/test/calculate var/calculate none rw,bind 0 0 lxc.mount.entry = /var/calculate/repos/container var/db/repos/container none ro,bind 0 0 lxc.mount.entry = /var/db/repos/calculate var/db/repos/calculate none ro,bind 0 0 lxc.mount.entry = /var/db/repos/gentoo var/db/repos/gentoo none ro,bind 0 0 lxc.mount.entry = /var/cache/edb/binhost var/cache/edb/binhost none ro,bind 0 0 lxc.mount.entry = /var/calculate/packages var/calculate/packages none rw,bind 0 0 lxc.mount.entry = /var/calculate/distfiles var/calculate/distfiles none rw,bind 0 0
In this file the container name occurs in the rootfs paths and in the system name; the lxc.mount.entry lines mount into the container the host Portage trees (read-only), the package and distfiles directories (writable) and the separate /var/calculate. The container inherits the domains and DNS addresses from the host.
Before the first start, the container is given a /etc/local.d/firststart.start script, which at boot deletes itself and the temporary /etc/calculate/calculate.env - this way the initial configuration is not repeated on the next boots.
Start the container and connect to it right away to see how the services came up:
Runlevel: default net.eth0 [ stopped ] local [ started ] Dynamic Runlevel: hotplugged Dynamic Runlevel: needed/wanted Dynamic Runlevel: manual
lxc-info gives a summary of the container - you can specify the name without any option:
Name: test State: RUNNING PID: 745677 IP: 10.2.8.18
The IP in the output equals the address of the host: there is no bridge on the host, and the container uses its network namespace. To get a shell inside the container, run attach without a command:
lxc-attach test
Your own package set and image preparation
The system in the container is maintained the same way as a regular Calculate: package sets and Portage masks are configured inside the container. For example, add vim to the custom set:
lxc-attach test -- sh -c 'echo app-editors/vim >> /etc/portage/sets/custom; cat /etc/portage/sets/custom'
lxc-attach test -- emerge -q app-editors/vim
The package was installed from the host's binary packages. When the settings inside the container are brought to the package composition you need, prepare the rootfs image for future updates (the output below is abridged: the download progress and the list of packages are omitted):
Creating container test_new * Copying base rootfs ... [ ok ] * Preparing base mount paths ... [ ok ] * Running container setup ... [ ok ] * Configuring utility variables ... [ ok ] * Preparing first boot setup script ... [ ok ] * Moving installed packages, flags and masks from main container ... [ ok ] * Creating mount points ... [ ok ] * Preparing first boot setup script ... [ ok ] * Starting container ... [ ok ] Repositories synchronization * Checking current binhost ... [ ok ] * Checking Distros updates ... [ ok ] * Checking Calculate updates ... [ ok ] * Checking Gentoo updates ... [ ok ] * Synchronization finished System configuration * Fixing the settings ... [ ok ] * Updating configuration files ... [ ok ] * System sync finished! Launching package update Updating packages * Listing packages for installation * 6 packages will be installed, 0 KiB will be downloaded * Fetching... ... [ ok ] * (1 of 6) Manages the /usr/bin/vi symlink * Emerging app-eselect/eselect-vi-20221122 ... [ ok ] * Installing app-eselect/eselect-vi-20221122 ... [ ok ] * (2 of 6) Portable fork of NaCl, a higher-level cryptographic library * Emerging dev-libs/libsodium-1.0.22 ... [ ok ] * Installing dev-libs/libsodium-1.0.22 ... [ ok ] * (3 of 6) Standalone version of Vim's xxd * Emerging dev-util/xxd-2025.08.24-r1 ... [ ok ] * Installing dev-util/xxd-2025.08.24-r1 ... [ ok ] * (4 of 6) Vim and gvim shared files * Emerging app-editors/vim-core-9.2.1202 ... [ ok ] * Installing app-editors/vim-core-9.2.1202 ... [ ok ] * (5 of 6) Vim, an improved vi-style text editor * Emerging app-editors/vim-9.2.1202 ... [ ok ] * Installing app-editors/vim-9.2.1202 ... [ ok ] * (6 of 6) Vim plugin: Gentoo and Portage syntax highlighting * Emerging app-vim/gentoo-syntax-16 ... [ ok ] * Installing app-vim/gentoo-syntax-16 ... [ ok ] Cleaning the system from needless packages * Calculating dependencies ... [ ok ] Rebuilding dependent modules * Updating preserved libraries ... [ ok ] * Updating configuration files ... [ ok ] * System update finished! * Stopping container ... [ ok ] * Saving image for update ... [ ok ] * Removing temporary container directory test_new ... [ ok ] * Image for update ready. Now you can run 'cl-lxc -u test' to update.
Preparation moves the user's Portage settings into the image - the custom files in the /etc/portage/package.accept_keywords/, package.mask/, package.unmask/ and package.use/ directories, the /etc/portage/sets/custom set and /etc/portage/repos.conf/custom.conf - it sets the host name and the system parameters, updates the system inside the container and then saves the finished image to the cache next to the container. For your container the image will be stored in /var/calculate/lxc/.cache/rootfs-name - in the example above this is rootfs-test; each new preparation replaces the previous image. The temporary container that the image was built in is deleted after the preparation.
Updating the container
The update switches the container to the prepared image:
Preparing to start new root * Moving image to test/rootfs2 ... [ ok ] * Copying network settings from main container ... [ ok ] * Copying hash of root password ... [ ok ] * Copying ssh keys ... [ ok ] * Copying nftables rules ... [ ok ] * Preparing first boot setup script ... [ ok ] * Setting up new root ... [ ok ] * Stopping container test [ ok ] * Launching container test [ ok ] Container updated.
Without a prepared image the update does not start: run the preparation first (--prepare), and only then --upgrade; these two operations can be combined in a single run.
During the update, the network settings from /etc/conf.d/net, the root password hash, the ssh keys of the host and the nftables rules are carried over into the new rootfs. The new root filesystem gets a sequence number: rootfs1, rootfs2, and the container configuration is rewritten to it; the old root is not deleted and keeps occupying space - clean it up manually once you are sure that no rollback will be needed.
The first boot of the new system performs the installation: the templates are merged and the network is configured; after that the startup script deletes itself. If the container was running, it is restarted on the new rootfs automatically; a stopped one stays stopped and is brought up with the lxc-start command.
Container profiles
In Calculate Linux a profile is a set of templates and preconfigured values of the calculate.env and ini.env variables; it pulls in the packages it needs through a system update. The profiles are described in the profiles/profiles.desc file of the container profiles repository; by default https://github.com/calculatelinux/container is used, and another repository is set with the --url option. The utility itself always prints the list of available profiles - the set changes over time, so check against its output:
cl-lxc -P list
Profile: * scratch/amd64/20 (stable) * steam/amd64/20 (exp)
The profile is applied when the container is created: -P with a profile name from the list is given together with -c. The container is brought up, the profile is switched inside it and the profile packages are installed through cl-update; if the profile templates added lines to the LXC configuration, the container is restarted to apply them. You can switch the profile of an already created container when preparing the image: -P together with --prepare applies the new profile if the current one in the rootfs differs.
Without an explicit -P, the first profile from the list is selected. The directory to search is named after the last element of the repository URL, given by --url or the one used by default. The utility looks for the profiles file in three places in turn: /var/db/repos/[HTML_REMOVED], then /var/calculate/repos/[HTML_REMOVED]; if it finds it in neither place, it clones the repository into /var/calculate/tmp/update/[HTML_REMOVED] and on subsequent runs only pulls it. The order is the same with and without --url: a repository with that name already synchronized on the host wins, and it never gets to cloning.
Cache clearing and general notes
The cached images (the base scratch and the prepared rootfs) take up space and sometimes need to be reset. Use the -R option for clearing, together with another operation - for example, together with creating a new container. First the options and the state of the containers are checked, and only then are the caches deleted; after the clearing, the base image is downloaded again on the next creation.
Only one cl-lxc can run at a time.
Container templates
When the container is created, two Calculate templates and a file describing the properties of the directory are laid into its future /var/calculate - they are merged into the system at the first boot.
The templates header directory /var/calculate/lxc/test/calculate/templates/default/.calculate_directory:
/var/calculate/lxc/test/calculate/templates/default/.calculate_directory
# Calculate env=install ac_install_live==on append=skip
The directory records are applied at the system installation stage (env=install) and only when the system is booted in live mode (ac_install_live==on); append=skip does not contribute to the merge path.
The binary mirror template portage.binhost. It has no fixed address: when the container is prepared, the PORTAGE_BINHOST line is taken from the host system - the utility copies it from the output of emerge --info so that the container updates from the same binary mirror as the host. For example, when the host has the https://mirror.koddos.net/calculate-linux mirror set (the Netherlands from the official mirror list), the template looks like this:
/var/calculate/lxc/test/calculate/templates/default/portage.binhost
# Calculate mergepkg(sys-apps/portage)!= path=/etc/portage/make.conf name=binhost protected comment=# PORTAGE_BINHOST="https://mirror.koddos.net/calculate-linux/grp/x86_64"
The PORTAGE_BINHOST line is written into the /etc/portage/make.conf file of the container; the mergepkg(sys-apps/portage)!= condition names the package the template is bound to, sys-apps/portage (it fires when this package is being configured), while protected keeps the file from being deleted together with that package.
The network service template runlevel.eth0:
/var/calculate/lxc/test/calculate/templates/default/runlevel.eth0
# Calculate mergepkg(sys-apps/openrc)!= path=/etc/runlevels/default name=net.eth0 protected link=/etc/init.d/net.lo symbolic
The template creates a symbolic net.eth0 link in the default runlevel of the container, pointing to the network startup script /etc/init.d/net.lo, so that the interface comes up at boot.
For more information on the template merge mechanism, see the Templates article.
btrfs features
If the containers directory is on btrfs, cl-lxc uses the capabilities of the filesystem: the cache and each container are created as separate subvolumes, and copying the rootfs (from the base image and during the update) is done with reflink - the new copy shares blocks with the original until it starts changing them. The subvolume is created not for a single rootfs but for the whole container directory: it holds the rootfs, rootfs1, … root filesystems, the config settings file, and the calculate directory that is mounted inside the container as /var/calculate. A snapshot of one subvolume therefore captures the whole container - this makes backups easier. Removing the cache and the temporary containers on btrfs is also done with the standard subvolume tools.