v0.3.0 pre-1.0 · interfaces still moving

Ansible playbooks,
without the Python.

Parallax is a single Go binary that runs the playbooks, inventories and vaults you already have. Nothing to install on the targets — modules are compiled and shipped as static binaries.

go install go.digitalxero.dev/parallax/cmd/parallax@latest
parallax play -i inventory.ini play.yml recorded
PLAY [Configure web servers] ***************************************************

TASK [Gathering Facts] *********************************************************
ok: [web1]
ok: [web2]

TASK [Ensure document root exists] *********************************************
changed: [web1]
changed: [web2]

TASK [Render the index page] ***************************************************
changed: [web1]
changed: [web2]

TASK [Check the page is readable] **********************************************
ok: [web2]
ok: [web1]

PLAY RECAP *********************************************************************
web1                           : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0
web2                           : ok=4    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Verbatim output of site/transcripts/play.yml, re-recorded by task site:capture.

How it works

  1. 01

    Your playbook is parsed as-is

    The same YAML, the same inventory files, the same vault. Parallax reads Ansible's formats directly — there is no conversion step and no separate project file to maintain.

  2. 02

    Each module is compiled to a static binary

    A task's module is built once for the target's OS and architecture, then cached on the controller. Subsequent runs reuse the cached binary instead of rebuilding it.

  3. 03

    The binary is shipped over the existing connection and run

    It is copied over SSH, executed, and removed. The target needs a shell and nothing else — no Python interpreter, no pip packages, no long-lived agent to install, patch or monitor.

Connections are pooled and reused across tasks. Architecture →

What we've actually measured

3.6 µs to read a cached module binary

Parallax compiles each module to a static binary once and caches it. A warm cache read costs about 3.6 µs; the write that populates it costs about 630 µs for a 1 MB binary. Against the benchmark's modelled 100 ms compile, that makes a cache hit roughly four orders of magnitude cheaper than rebuilding.

Scope: this is a synthetic Go benchmark using mock 1 MB binaries and a simulated 100 ms compile. It compares Parallax's cache to Parallax's own compiler. It is not a comparison against Ansible, and it does not measure a real playbook run. Methodology and full results.

Not measured yet: we have no published end-to-end benchmark of Parallax against Ansible on the same playbook and host count, and no template rendering benchmark. Until those exist we would rather show you one number we can defend than three we cannot.

How it compares

Where Parallax wins, where it matches, and where Ansible is still the better tool.

CapabilityAnsibleParallax
Controller runtime (Parallax ahead)Python 3.x plus pip-installed dependenciesOne static binary
Target requirements (Parallax ahead)A Python interpreter on every managed hostNone — modules ship as compiled binaries
Playbooks, inventory, vault (equivalent)YAML, INI/YAML inventory, AES256 vault (1.1 & 1.2)Same formats, read directly
Templating (mostly compatible, with caveats)Jinja2gonja — Jinja2-compatible, not byte-identical
Connection types (mostly compatible, with caveats)Large plugin ecosystemssh, local, docker, winrm
Handlers (equivalent)Notified at end of play, or now with meta: flush_handlersSame, including mid-play flush
Module library (Ansible ahead)Thousands, via Galaxy collections134 built in
Role & collection distribution (Ansible ahead)ansible-galaxy installVendor roles yourself; no Galaxy client
Custom modules and plugins (Ansible ahead)Write in PythonWrite in Go; existing Python plugins must be ported
Maturity (Ansible ahead)Stable, over a decade of production usev0.3.0, pre-1.0

What Parallax doesn't do yet

Read this before you plan a migration. These are the things that will stop a real playbook, not hypotheticals.

Unsupported modules fail the play
There is no Python fallback. A task using a module outside the 134 built in — anything from community.docker, kubernetes.core, amazon.aws and most other collections — fails with couldn't resolve module/action. Check your playbooks against the module reference first.
No Galaxy client
There is no ansible-galaxy equivalent. Roles are read from disk, so anything you depend on has to be vendored into your repository or installed out of band. galaxy_info in role metadata is parsed but not acted on.
Python plugins must be ported
Custom modules, filters, lookups, callbacks and connection plugins are Go. Parallax loads them over go-plugin RPC; it cannot execute your existing Python ones. See the plugin porting guide.
Templating is compatible, not identical
gonja implements Jinja2 but is a separate engine. Common filters and control flow behave the same; unusual whitespace control, obscure filters and Python-specific expressions inside templates may not.
Pre-1.0
v0.3.0. The module SDK interfaces are still changing, so out-of-tree plugins may need updating between releases.

Try it

The playbook below is the one recorded at the top of this page. Both hosts use the local connection, so it runs on your machine with nothing else set up.

  1. Install

    go install go.digitalxero.dev/parallax/cmd/parallax@latest
  2. Save an inventory and a playbook

    inventory.ini
    [web]
    web1 ansible_connection=local
    web2 ansible_connection=local
    play.yml
    - name: Configure web servers
      hosts: web
      gather_facts: true
      vars:
        docroot: /tmp/parallax-demo/www
      tasks:
        - name: Ensure document root exists
          file:
            path: "{{ docroot }}/{{ inventory_hostname }}"
            state: directory
            mode: "0755"
    
        - name: Render the index page
          copy:
            dest: "{{ docroot }}/{{ inventory_hostname }}/index.html"
            content: "hello from {{ inventory_hostname }}\n"
            mode: "0644"
    
        - name: Check the page is readable
          stat:
            path: "{{ docroot }}/{{ inventory_hostname }}/index.html"
  3. Run it

    parallax play -i inventory.ini play.yml

Quick start guide What the targets need