Skip to main content

Local LXC/Incus container management for chatmail relay development and testing

Project description

cmlxc -- local chatmail container management and testing

Manage local Incus containers for chatmail relay development and testing. cmlxc spins up lightweight LXC containers, deploys chatmail relay services into them via cmdeploy or madmail, and runs integration tests -- all without touching the host system. See Architecture for more internal details.

Prerequisites

Incus installed and configured on the host. Usually only being part of the "incus" group is necessary, as containers can run with user privileges.

[!TIP] On Debian or Ubuntu, it is recommended to use the Zabbly Incus repository to ensure you have a recent version.

You can verify your incus installation like this:

incus launch images:debian/12 local-my-setup

If this command fails, please check the incus documentation. If you get an error about "Failed instance creation", you might be running into https://github.com/lxc/incus/issues/916 and need to ensure there is no component (mullvad) for example, that messes up container networking.

Installation

With pip:

python -m venv venv
source venv/bin/activate
pip install cmlxc

Or with uv:

uv venv venv
source venv/bin/activate
uv pip install cmlxc

Usage

Initialize the environment (base image, DNS container, builder container):

cmlxc init

Re-initialize from scratch (destroys everything first):

cmlxc init --reset

Deploy chatmail relays (creates containers if needed, then deploys). The --source argument controls where the code comes from:

cmlxc deploy-cmdeploy --source @main cm0
cmlxc deploy-madmail  --source @main mad1
cmlxc deploy-madmail  --source @main --with-webadmin mad1
cmlxc deploy-madmail  --source @main --ipv4-only mad1
Form Meaning
@ref Clone default remote at branch/tag ref
/path or ./path Sync from a local checkout
URL@ref Clone a custom remote at ref

Examples with local checkouts or feature branches:

cmlxc deploy-cmdeploy --source ../relay cm0
cmlxc deploy-madmail  --source @lmtp-rework mad0
cmlxc deploy-cmdeploy --source @fix-dovecot cm1

Each deploy-* invocation initialises the driver's source in the builder (wipe-and-reclone).

Run integration tests inside the builder:

cmlxc test-mini cm0
cmlxc test-mini cm0 cm1          # cross-relay tests (domain-based)
cmlxc test-mini cm0 mad1         # cross-relay tests (mixed)
cmlxc test-cmdeploy cm0 cm1
cmlxc test-madmail mad1

SSH into a deployed relay:

ssh -F ~/.config/cmlxc/ssh-config cm0

Lifecycle commands:

cmlxc status                # show all containers
cmlxc status cm0            # show only cm0
cmlxc status cm0 mad1       # show multiple containers
cmlxc status --host         # show DNS/SSH setup instructions
cmlxc start cm0             # restart a stopped relay
cmlxc stop cm0 cm1          # stop relays
cmlxc destroy cm0           # stop + delete
cmlxc destroy --all         # destroy relays, keep DNS/builder

Increase verbosity with -v or -vv:

cmlxc deploy-cmdeploy --source @main -vv cm1

Shell Completion

cmlxc supports Bash tab-completion for subcommands, options, and container names.

Enable for the current session:

eval "$(register-python-argcomplete cmlxc)"

Enable permanently:

activate-global-python-argcomplete --user

Architecture

cmlxc manages four kinds of containers, each with a distinct role:

    cmlxc init / deploy-* / test-*
        |
        v
   +-----------------+   +------------------------+   +--------------------+
   | ns-localchat    |   | builder-localchat      |   | relay containers   |
   | (PowerDNS)      |   | (repos, venvs, builds) |   | (cm0, mad1, ...)   |
   +-----------------+   +------------------------+   +--------------------+
           ^                        |                           ^
           |      DNS zones         |        SSH / SCP          |
           +------------------------+---------------------------+

Base image (localchat-base) -- a Debian 12 image with SSH and Python pre-installed. All other containers are launched from this image (or from a cached relay image).

DNS container (ns-localchat) -- runs PowerDNS authoritative + recursor. Provides .localchat DNS resolution so containers can reach each other by name.

Builder container (builder-localchat) -- the central workhorse. Holds repository templates and per-relay checkouts, Python virtualenvs for cmdeploy and mini-tests, and the compiled maddy binary. All deployment and test operations are executed inside the builder -- the host only needs cmlxc itself.

Relay containers (e.g. cm0-localchat, mad1-localchat) -- ephemeral containers that receive a deployed chatmail service. Each relay is locked to a single deployment driver (cmdeploy or madmail); switching requires destroying and re-creating the container.

Deployment drivers

Drivers live in driver_cmdeploy.py and driver_madmail.py. Each driver module exports its CLI subcommand metadata, builder init, and deploy orchestration. cli.py generates the deploy-* subcommands from a DRIVER_BY_NAME mapping.

  • cmdeploy -- runs cmdeploy run from the builder container over SSH into the relay. Generates DNS zones, loads them into PowerDNS, and verifies records. After the first successful deploy the relay image is cached as localchat-cmdeploy so subsequent containers start pre-populated.

  • madmail -- builds the maddy Go binary inside the builder, pushes it via SCP and runs madmail install --simple --ip <IP>. No DNS entries are needed.

Releasing

Versions are derived from git tags via setuptools-git-versioning. The changelog is generated with git-cliff using the cliff.toml config in the repo root.

To make a new release, use the provided script:

./make_new_release.py

The script automates the following steps:

  1. Test the codebase by running a full tox suite and functional tests (pytest tests/fullrun.py).

  2. Preview unreleased changes with git cliff.

  3. Tag the release (suggesting automatic, micro, or minor bump).

  4. Generate the full changelog into CHANGELOG.md.

  5. Edit the changelog manually (opens your $EDITOR).

  6. Amend the tag commit to include the changelog update.

  7. Force-tag the amended commit.

After the script finishes, push the changes:

git push origin main --tags

The release.yml GitHub workflow triggers on pushed v* tags, builds the sdist + wheel, and publishes to PyPI via trusted publishing (OIDC).

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cmlxc-0.14.1.tar.gz (42.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cmlxc-0.14.1-py3-none-any.whl (43.2 kB view details)

Uploaded Python 3

File details

Details for the file cmlxc-0.14.1.tar.gz.

File metadata

  • Download URL: cmlxc-0.14.1.tar.gz
  • Upload date:
  • Size: 42.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cmlxc-0.14.1.tar.gz
Algorithm Hash digest
SHA256 08d3d06db005fcf0d80ec50c45c827346b749f4c50d272045a56b97d9ddd66ef
MD5 a43af6c2b77ded1b9f272c1679dd9f3a
BLAKE2b-256 4a5f02443c26b81000471ec70085a1caa8ebf75726b6a281d19bbb52e960ab78

See more details on using hashes here.

Provenance

The following attestation bundles were made for cmlxc-0.14.1.tar.gz:

Publisher: release.yml on chatmail/cmlxc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cmlxc-0.14.1-py3-none-any.whl.

File metadata

  • Download URL: cmlxc-0.14.1-py3-none-any.whl
  • Upload date:
  • Size: 43.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for cmlxc-0.14.1-py3-none-any.whl
Algorithm Hash digest
SHA256 87e70a3d0568ca82d776d9d895c6ca6dfa5e8e56682e1d90b36c97fc84f664b2
MD5 43b4edfebd52ce7514a975e4a02fe9ea
BLAKE2b-256 87bdb055ad8831e8eb09f46fccc3e424b48bb90a76b1de4876a6ec9ea8e8f78f

See more details on using hashes here.

Provenance

The following attestation bundles were made for cmlxc-0.14.1-py3-none-any.whl:

Publisher: release.yml on chatmail/cmlxc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page