Migration Guide
Ansible-to-Parallax Migration Guide
This guide covers migrating existing Ansible playbooks and workflows to Parallax.
Overview
Parallax is a Go-based Ansible-compatible playbook executor. Key differences from Ansible:
- Single binary — no Python runtime or pip dependencies on the controller or remote hosts
- Compiled modules — modules compile to Go binaries, shipped and executed on targets
- gonja templating — Go-based Jinja2 implementation (compatible with most Jinja2 syntax)
- Lock-free concurrency — channel-based parallelism instead of Python multiprocessing
Most Ansible playbooks run without modification. This guide documents the areas that need attention.
Installation
# From source
git clone https://go.digitalxero.dev/parallax.git
cd parallax
task build
# Or via go install
go install go.digitalxero.dev/parallax@latest
The parallax binary is the only dependency. No Python, pip, or ansible packages are needed on the controller or targets.
Quick Start
# Run a playbook (same as ansible-playbook)
parallax play playbook.yml
# With inventory
parallax play -i inventory.ini playbook.yml
# Check mode (dry run)
parallax play --check playbook.yml
CLI Flag Mapping
Core Flags
Ansible (ansible-playbook) | Parallax (parallax play) | Notes |
|---|---|---|
-i INVENTORY | -i INVENTORY | Identical |
-C / --check | -C / --check | Identical |
-b / --become | -b / --become | Identical |
--become-user USER | --become-user USER | Identical |
--become-method METHOD | --become-method METHOD | sudo, su, doas, pfexec |
--tags TAGS | --tags TAGS | Comma-separated |
--skip-tags TAGS | --skip-tags TAGS | Comma-separated |
-e KEY=VALUE | -e KEY=VALUE | Supports key=value, @file, JSON |
-l PATTERN | -l PATTERN | Identical |
-D / --diff | -D / --diff | Identical |
--start-at-task NAME | --start-at-task NAME | Identical |
--forks N | --forks N | Default 5 |
SSH Flags
| Ansible | Parallax | Notes |
|---|---|---|
--private-key FILE | --private-key FILE | Identical |
--ask-pass | --ask-pass | Identical |
-u USER | --ssh-user USER | Different flag name |
--ssh-common-args | N/A | Use individual flags below |
| N/A | --ssh-port PORT | Default 22 |
| N/A | --ssh-timeout DURATION | Default 30s |
| N/A | --ssh-retries N | Default 3 |
| N/A | --ssh-retry-interval DURATION | Default 5s |
| N/A | --ssh-keepalive | Default true |
| N/A | --ssh-keepalive-interval DURATION | Default 30s |
| N/A | --strict-host-checking | Default true |
| N/A | --known-hosts FILE | Default ~/.ssh/known_hosts |
| N/A | --use-agent | Default true |
Bastion/Jump Host Flags
| Ansible | Parallax | Notes |
|---|---|---|
--ssh-common-args "-J user@bastion" | --bastion-host HOST | Dedicated flag |
| N/A | --bastion-port PORT | Default 22 |
| N/A | --bastion-user USER | Defaults to –ssh-user |
| N/A | --bastion-key FILE | Bastion-specific key |
Vault Flags
| Ansible | Parallax | Notes |
|---|---|---|
--vault-id label@source | --vault-id label@source | Identical |
--vault-password-file FILE | --vault-password-file FILE | Identical |
--ask-vault-pass | --ask-vault-pass | Identical |
Parallax-Only Flags
These flags have no Ansible equivalent:
| Flag | Default | Description |
|---|---|---|
--connection-pooling | false | Reuse SSH connections across tasks |
--pool-max-connections | 20 | Max pooled connections |
--precompile | false | Pre-build module binaries |
--precompile-background | false | Background precompilation |
--cache | true | Cache compiled module binaries |
--cache-dir | ~/.parallax/module-cache | Cache location |
--facts-parallel | true | Parallel facts gathering |
--facts-concurrency | 10 | Max concurrent fact gathers |
--facts-cache | true | Cache gathered facts |
--facts-incremental | true | Only re-gather changed facts |
--template-cache | true | Cache compiled templates |
--resource-management | false | Module resource limits |
--debug-enabled | false | Enhanced debug output |
--debug-profiling | false | Module execution profiling |
--debug-tracing | false | Execution tracing |
--callback | default | Callback plugin (default, minimal, json, timer, profile_tasks) |
--json | false | JSON output (shortcut for –callback json) |
--validate-features | false | Check Ansible feature compatibility |
Playbook Compatibility
What Works Unchanged
The following Ansible playbook features work identically:
- Play directives:
hosts,name,become,become_user,become_method,gather_facts,vars,vars_files,roles,tasks,handlers,pre_tasks,post_tasks,strategy,serial,max_fail_percentage,any_errors_fatal,force_handlers,order,collections,module_defaults,environment,tags - Task directives:
name,module args,when,register,loop,with_items,with_dict,with_list,with_fileglob,with_indexed_items,with_subelements,with_first_found,notify,tags,become,become_user,ignore_errors,no_log,changed_when,failed_when,until,retries,delay,delegate_to,run_once,check_mode,diff,connection,async,poll,throttle,block,rescue,always - Includes/imports:
import_tasks,include_tasks,import_role,include_vars - Handlers: Standard
notifywith handler names,listen,meta: flush_handlers - Roles: Standard role structure (tasks, handlers, defaults, vars, files, templates, meta)
- Variables: All precedence levels,
set_fact, extra vars (-e), group_vars, host_vars
Known Parser Quirks
notify:must be a YAML list, not a string:
# Works
- name: Restart nginx
service:
name: nginx
state: restarted
notify:
- restart nginx
# Does NOT work
- name: Restart nginx
service:
name: nginx
state: restarted
notify: restart nginx
shell:withcmd:map form can cause issues with multiple handlers. Use the string form:
# Preferred
- name: Run command
shell: "echo hello"
# May cause issues in some contexts
- name: Run command
shell:
cmd: "echo hello"
local_action:runs but does not capture stdout reliably. Usedelegate_to: localhost:
# Preferred
- name: Run locally
command: echo hello
delegate_to: localhost
# Works but limited stdout capture
- name: Run locally
local_action: command echo hello
meta:task syntax is fully supported. Valid actions:flush_handlers,clear_facts,clear_host_errors,noop,end_play,end_host,end_batch,refresh_inventory,reset_connection.
Inventory Compatibility
All Ansible inventory formats are supported:
INI Format
[webservers]
web1.example.com ansible_host=10.0.0.1
web2.example.com ansible_port=2222
[dbservers]
db[1:3].example.com
[webservers:vars]
http_port=80
[all:children]
webservers
dbservers
YAML Format
all:
hosts:
web1.example.com:
ansible_host: 10.0.0.1
children:
webservers:
hosts:
web2.example.com:
ansible_port: 2222
vars:
http_port: 80
Dynamic Inventory
Dynamic inventory scripts (executables that output JSON) work identically.
Host Patterns
All Ansible host patterns are supported:
- Group names:
webservers - Intersection:
webservers:&dbservers - Exclusion:
webservers:!staging - Ranges:
web[0:2].example.com - Regex:
~.*\.web\..* - Comma-separated:
host1,host2,host3
group_vars / host_vars
Standard group_vars/ and host_vars/ directories are loaded from both the inventory directory and the playbook directory, matching Ansible’s behavior. YAML and JSON files are supported. Vault-encrypted files are automatically decrypted when vault passwords are provided.
Module Compatibility
ansible.builtin (71 modules)
All 71 ansible.builtin modules are implemented:
Core: command, shell, copy, file, template, debug, ping, stat, fetch, service, user, group
Package Management: package, apt, yum, dnf, dnf5, pip, rpm_key
System: systemd, systemd_service, sysvinit, gather_facts, setup, service_facts, mount_facts, reboot, hostname
File Operations: lineinfile, replace, blockinfile, assemble
Network & Utilities: get_url, uri, unarchive, cron, mount
Control Flow: set_fact, assert, fail, pause, wait_for, wait_for_connection, meta, add_host, group_by, include_vars, import_tasks, include_tasks, import_role
Advanced: script, raw, tempfile, validate_argument_spec, vars_prompt, subversion, git, slurp, package_facts
Specialized: apt_key, apt_repository, yum_repository, deb822_repository, debconf, dpkg_selections, expect, getent, iptables, known_hosts, async_status, find
ansible.posix (13 modules)
acl, at, authorized_key, firewalld, mount (posix), patch, seboolean, selinux, synchronize, sysctl, rhel_facts, rhel_rpm_ostree, rpm_ostree_pkg
community.general (50 modules)
ini_file, xml, archive, read_csv, json_query, htpasswd, ssh_config, listen_ports_facts, timezone, locale_gen, alternatives, pam_limits, make, modprobe, capabilities, supervisorctl, npm, yarn, gem, composer, cpanm, easy_install, snap, flatpak, homebrew, ufw, nmcli, sefcontext, syslogd, mail, slack, terraform, parted, lvg, lvol, filesystem, lvm_facts, xfs_quota, zfs, postgresql_user, postgresql_privs, mysql_user, redis, rabbitmq_user, jenkins_job, docker_compose, docker_volume, docker_network, fileglob, dconf
Module Naming
Modules can be referenced by short name or FQCN:
# All equivalent
- command: echo hello
- ansible.builtin.command: echo hello
- parallax.builtin.command: echo hello
Templating
Parallax uses gonja, a Go-based Jinja2 implementation. Most Jinja2 syntax works identically.
Supported Features
- Variable interpolation:
{{ variable }} - Control structures:
{% if %},{% for %},{% set %},{% block %},{% macro %} - Comments:
{# comment #} - Whitespace control:
{%- -%},{{- -}} - Template inheritance:
{% extends %},{% include %} - Expressions: math, comparisons, logical operators
Filters
71 custom Ansible-compatible filters plus all gonja built-in filters:
- String: quote, split, regex_search, regex_replace, regex_findall, regex_escape, comment, ternary
- Collection: count, flatten, dict2items, items2dict, combine, union, intersect, difference, symmetric_difference, zip, zip_longest, subelements
- Math: pow, log, root, human_readable, human_to_bytes
- Type: bool, type_debug, mandatory, to_json, to_nice_json, to_yaml, to_nice_yaml, from_json, from_yaml, to_uuid, to_datetime, strftime
- Path: basename, dirname, expanduser, expandvars, realpath, relpath, splitext, win_basename, win_dirname, win_splitdrive
- Crypto: checksum, hash, password_hash, b64encode, b64decode
- Network: ipaddr, ipv4, ipv6, ip4_hex, ipmath, ipwrap, ipsubnet, nthhost, network_in_network, network_in_usable, hwaddr, macaddr, reduce_on_network, slaac
- URL: urlsplit
- Data Query: json_query (JMESPath)
Jinja2 Tests
24 custom tests plus gonja built-ins: truthy, falsy, match, search, regex, contains, startswith, endswith, subset, superset, any, all, version, version_compare, changed, failed, success, succeeded, skipped, unreachable, started, finished
Lookups
16 lookup plugins: file, env, pipe, dict, indexed_items, sequence, password, lines, fileglob, varnames, url, csvfile, ini, first_found, template, config
Known Templating Differences
gonja is highly compatible with Jinja2 but minor edge cases may differ in:
- Complex nested macro definitions
- Unusual whitespace handling in edge cases
- Some Python-specific string methods
If you encounter a template that renders differently, simplify the expression or split it into multiple steps.
Vault
Parallax provides full Ansible Vault compatibility.
Supported Formats
- Vault 1.1:
$ANSIBLE_VAULT;1.1;AES256 - Vault 1.2:
$ANSIBLE_VAULT;1.2;AES256;vault-id
CLI Commands
parallax vault encrypt file.yml
parallax vault decrypt file.yml
parallax vault view file.yml
parallax vault edit file.yml
parallax vault encrypt_string "secret" --name my_var
parallax vault rekey file.yml --new-vault-password-file new_pass.txt
Password Sources
--vault-password-file FILE— read password from file--vault-id label@source— labeled vault identity (file or script)--ask-vault-pass— interactive promptANSIBLE_VAULT_PASSWORDenvironment variable
Inline Vault Variables
Vault-encrypted values embedded in YAML files are automatically decrypted during variable loading. This works in playbook vars, group_vars, host_vars, and vars_files.
Connection Types
| Type | Flag | Description |
|---|---|---|
| SSH | connection: ssh (default) | Standard SSH with agent, key, or password auth |
| Local | connection: local | Execute on controller |
| WinRM | connection: winrm | Windows Remote Management (Basic, NTLM, Certificate, Kerberos) |
| Docker | connection: docker | Execute in Docker/Podman containers |
Features With Behavioral Notes
These features are fully implemented but may have minor behavioral differences:
| Feature | Ansible | Parallax | Notes |
|---|---|---|---|
| Strategy | strategy: free/linear | strategy: free/linear/host_pinned | All three strategies implemented |
| Throttle | throttle: N | throttle: N | Also available via --forks |
| Host order | order: sorted | order: sorted/reverse_sorted/shuffle/reverse_inventory/inventory | All 5 modes |
| Async | async: N / poll: N | async: N / poll: N | Full async support with async_status |
| Serial | serial: N/%/[list] | serial: N/%/[list] | Integer, percentage, and list forms |
| Check mode | --check | --check | Per-task override via check_mode: |
Parallax-Only Features
Features available in Parallax with no Ansible equivalent:
Module Caching
Compiled module binaries are cached to avoid recompilation:
parallax play --cache playbook.yml # enabled by default
parallax play --cache-info stats # show cache statistics
parallax play --cache-clear # clear all cached modules
Connection Pooling
Reuse SSH connections across tasks for improved performance:
parallax play --connection-pooling playbook.yml
parallax play --connection-pooling --pool-max-connections 50 playbook.yml
Parallel Facts Gathering
Gather facts from multiple hosts concurrently:
parallax play --facts-parallel --facts-concurrency 20 playbook.yml
parallax play --facts-incremental playbook.yml # only re-gather changed facts
Module Precompilation
Pre-build module binaries before execution:
parallax play --precompile playbook.yml
parallax play --precompile-background playbook.yml # background precompilation
Debug and Profiling
parallax play --debug-enabled --debug-level verbose playbook.yml
parallax play --debug-profiling playbook.yml # module execution timing
parallax play --debug-report playbook.yml # comprehensive report at end
Resource Management
parallax play --resource-management --max-memory 512 --max-cpu 80 playbook.yml
Template Caching
parallax play --template-cache --template-cache-size 2000 playbook.yml
Callback Plugins
parallax play --callback minimal playbook.yml
parallax play --callback json playbook.yml
parallax play --callback timer playbook.yml
parallax play --callback profile_tasks playbook.yml
Migration Checklist
- Install Parallax — build from source or use
go install - Test existing playbooks — run with
--checkfirst to verify parsing - Review notify syntax — ensure
notify:uses YAML list format - Review local_action usage — switch to
delegate_to: localhostif stdout capture is needed - Review shell tasks — prefer
shell: "string"form overshell: {cmd: "string"} - Test with real execution — run against a staging environment
- Review output — compare task results with Ansible output
- Update CI/CD — replace
ansible-playbookwithparallax playin pipelines - Enable performance features — try
--connection-pooling,--precompile,--facts-parallel - Validate compatibility — use
--validate-featuresto check for unsupported patterns