Skip to main content

pyinfra-lxd-api-connector

A pyinfra connector that targets LXD containers via the LXD HTTPS API directly — no SSH hop, no paramiko, no lxc CLI subprocess, no websockets. One kept-alive HTTPS connection per host, exec via the container_exec_recording API extension, file transfers via the native files API.

Why?

The obvious way to drive pyinfra against LXD is to shell out to the lxc CLI for every command. That approach pays a per-command cost of ~6–7 fresh TCP+TLS connections (capabilities probe + events websocket + exec POST + 4× stdio websockets + operation poll) — measured at ~870 ms per command over Tailscale from a remote laptop.

Talking to the API directly with record-output: true mode collapses all of that to a single kept-alive HTTPS connection with zero websockets. Measured at ~150 ms per run_shell_command from the same vantage point — ~5–6× faster, and within the same order of magnitude as warm SSH-multiplex.

Tracks pyinfra issue #677. Per pyinfra's contributing guide, connectors live as separate packages rather than in the pyinfra core repo.

Performance

Per-call latency over Tailscale from a remote laptop (~27 ms RTT to the cluster), measured via smoke_test.py against a real container:

Operation Wall time
connect() (cold TLS + capability probe + container check) ~335 ms
run_shell_command (warm, kept-alive) ~130–260 ms
put_file (small payload) ~80 ms
get_file (small payload) ~30 ms

For comparison, an lxc exec-based connector pays ~870 ms per run_shell_command from the same vantage point. From a node inside the cluster the difference doesn't matter; from a laptop driving deploys over a WAN it dominates wall time.

Install

uv tool install pyinfra --with pyinfra-lxd-api-connector

Usage

Prereq: an lxc remote configured locally:

lxc remote add mycluster https://your-cluster:8443 --token <token>
lxc list mycluster:        # verify

The connector reads the standard LXD client config at ~/.config/lxc/:

  • config.yml — remote URL
  • client.crt + client.key — mTLS client identity
  • servercerts/<remote>.crt — pinned server cert

Inventory:

hosts = [
    "@lxd_api/mycluster:php01",       # explicit remote
    "@lxd_api/web1",                   # uses default-remote from lxc config
    "@lxd_api/some-other-cluster:web1",
]

A bare @lxd_api/<container> resolves the remote via the default-remote field in ~/.config/lxc/config.yml — the same field lxc itself consults when called without a remote qualifier. Switch the default with lxc remote switch <name>. If no default is set, the connector raises an InventoryError pointing you at the qualified form.

Requirements

  • LXD server with the container_exec_recording API extension (LXD 5.0+).
  • Local LXD client config at ~/.config/lxc/. lxc remote add sets all of this up.

Status

Beta. In production use against a 32-container LXD cluster since 2026-04-28. Feedback / bug reports welcome.

Known limitations

  • No interactive / PTY support — the connector raises NotImplementedError if _get_pty=True. pyinfra never needs PTY for facts/operations, so this is fine in practice; if you need an interactive shell, use lxc shell directly.
  • Per-command stdout/stderr is buffered, not streamed — record-output mode means output arrives at the end of the command. For pyinfra's typical workload (facts and one-shot operations) this is invisible; for long-running commands you won't see live progress.
  • POST /exec retries only connect-class errors by default — run_shell_command retries ConnectError / ConnectTimeout / PoolTimeout (the request provably never reached the server), but fails fast on ReadTimeout / 5xx, since the command may already be running on the container. File transfers (put_file / get_file) and the operation/output polls retry on any transient error. Set lxd_exec_retry_on_read_errors=True on a host (or globally) if all your deploy steps are independently idempotent and duplicate execution is safe.

AI assistance

Per the pyinfra AI usage policy, disclosing how this package was authored:

The initial draft of pyinfra_lxd_api_connector.py was generated by Claude (Anthropic) in collaboration with the maintainer (Christian Rishøj). Specifically:

  • Christian identified the original problem — a silent SFTP-truncation bug in an earlier lxc exec-based driver — and ran the empirical analysis showing that the LXD record-output: true API path was the right fast alternative.
  • Claude drafted the connector module against pyinfra's BaseConnector interface.
  • Christian reviewed every line, integrated and ran it against a 32-container production cluster, and iterated through several rounds of correctness, latency, and ergonomics fixes.
  • All subsequent maintenance is human-driven.

The code in this repository is fully understood and reviewed by the maintainer; AI assistance is a drafting tool, not a substitute for human judgment.

License

MIT — see LICENSE.

Metadata

Release files for pyinfra-lxd-api-connector 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyinfra-lxd-api-connector 0.2.0
File Size Uploaded
pyinfra_lxd_api_connector-0.2.0.tar.gz 18.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyinfra-lxd-api-connector 0.2.0
File Interpreter ABI Platform
pyinfra_lxd_api_connector-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.2 kB

Release files / pyinfra_lxd_api_connector-0.2.0.tar.gz

Download URL pyinfra_lxd_api_connector-0.2.0.tar.gz
Size 18.8 kB
Tags Source
SHA-256 checksum
How to use checksums
ce75cdfcbda520b890744d496da55316de124d851db75e56b1cac0add804e42c
BLAKE2b-256 checksum
How to use checksums
c38a9d5353b0deb609c9b22352b34a73f6b0cf91c7d6a1de4ecef1b4756d7796
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 10, 2026.

Transparency log

Release files / pyinfra_lxd_api_connector-0.2.0-py3-none-any.whl

Download URL pyinfra_lxd_api_connector-0.2.0-py3-none-any.whl
Size 11.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
04b40accabad8e01934dbf4c69192f92f0f7ed0ac5e233d6acca41dc2ed3e471
BLAKE2b-256 checksum
How to use checksums
9f4a5d594d7dc910c0a2ba578b53954a3abe0e312c39b2589796ca96a3100b64
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.1

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page