PARALLAX

v0.4.0pre-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.

Install
curl -fsSLo parallax https://gitlab.com/api/v4/projects/23297967/packages/generic/parallax/v0.4.0/parallax-linux-amd64 && chmod +x parallax

Release v0.4.0 for Linux amd64. macOS, Windows and arm64 builds are on the releases page.

or from a checkout

git clone https://gitlab.com/digitalxero/parallax.git && cd parallax && go install ./cmd/parallax
Module pathModule pathSchematic of the Parallax module path: on the controller, the playbook, inventory and vault are parsed as-is, each module is compiled once with go build into a static binary and kept in the module cache; the binary is copied to the target over SSH, executed and removed. The target needs only a shell; Python and an agent are not required.ControllerPlaybook · Inventory · Vaultparsed as-isgo build → static binarycompiled onceModule cacheone binary on the controllerssh · copy · exec · removestatic binaryno libc · no pythonTargetPythonnot requiredAgentnot requiredShellrun · then removea shell, nothing elsedwgParallaxtitleModule pathsheet1 / 1revv0.4.0Module pathSchematic of the Parallax module path: on the controller, the playbook, inventory and vault are parsed as-is, each module is compiled once with go build into a static binary and kept in the module cache; the binary is copied to the target over SSH, executed and removed. The target needs only a shell; Python and an agent are not required.ControllerPlaybook · Inventory · Vaultparsed as-isgo build → static binarycompiled onceModule cachessh · copyexec · removestatic binaryno libc · no pythonTargetShellrun · then removePythonnot requiredAgentnot requireda shell, nothing else
What happens to one task: parsed on the controller, compiled once, shipped over the connection you already have, run, removed.
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"
Recorded
parallax play -i inventory.ini play.yml
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. The playbook on the left is the file that produced the output on the right.

01 – 03

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 →

Measured

What we've actually measured

> 100× faster to reuse a cached module binary than to compile it

Parallax compiles each module to a static binary once and caches it. In our benchmark a warm cache hit was measured at more than 100× faster than recompiling the module. That is the conservative bound we are willing to put on a homepage; the full figures are in the linked results.

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.

Compared

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 collections133 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.4.0, pre-1.0
Known limits

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 133 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.4.0. The module SDK interfaces are still changing, so out-of-tree plugins may need updating between releases.

Everything on this list was verified against the binary at v0.3.0.

Try it

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. 01 · Install

    Install

    curl -fsSLo parallax https://gitlab.com/api/v4/projects/23297967/packages/generic/parallax/v0.4.0/parallax-linux-amd64 && chmod +x parallax

    or from a checkout

    git clone https://gitlab.com/digitalxero/parallax.git && cd parallax && go install ./cmd/parallax
  2. 02 · Files

    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. 03 · Run

    Run it

    parallax play -i inventory.ini play.yml
Search
    ↑ ↓ move↵ openesc close