Prerequisites
What the controller and the target hosts need before you run anything
Parallax’s headline claim is that target hosts need no Python. That is true, and it is worth being precise about what it costs: the work Ansible does with a Python interpreter on the target, Parallax does with a Go toolchain on the controller. Read this page before installing.
Controller requirements
A Go toolchain is required at runtime — not just to build
This is the single most surprising requirement, so it comes first.
Parallax modules are Go programs. They are not shipped inside the parallax binary
as precompiled payloads; they are compiled on the controller, on demand, for the
architecture of each target host, and the resulting static binary is copied over
and executed. pkg/module/compiler/compiler.go shells out to go build to do this.
The compile is roughly:
CGO_ENABLED=0 GOOS=<target-os> GOARCH=<target-arch> \
go build -tags netgo,osusergo -ldflags "-s -w -extldflags '-static'" -o <module> .netgo and osusergo plus CGO_ENABLED=0 are what make the module a fully static
binary with no libc, DNS-resolver or NSS dependency on the target — that is the
mechanism behind “no Python, no runtime dependencies on the target”.
So on the controller you need:
| Requirement | Notes |
|---|---|
| Go 1.25 or newer | Must be on PATH as go. Match the version in go.mod. |
| Network access to the Go module proxy | go mod tidy runs when generating a module. See Offline operation. |
Writable GOCACHE | See Warm your build cache. |
| A writable temp directory | Each compile gets its own os.MkdirTemp scratch directory. |
If go is not on PATH, the first task that needs a module fails with:
parallax requires a Go toolchain on the controller to compile modules,
but 'go' was not found on PATH: exec: "go": executable file not found in $PATHThat check is a preflight in the compiler, so the failure is reported up front
rather than surfacing as an opaque exec error somewhere inside module execution.
Warm your build cache
Compilation happens per module, per target GOOS/GOARCH. A cold GOCACHE means
the standard library gets compiled for that target before your module does, which
turns a sub-second step into tens of seconds. A warm cache turns it back into
milliseconds. This is the difference between Parallax feeling instant and feeling
broken, so it is worth arranging deliberately:
Do not wipe
GOCACHEbetween runs. In CI, cache theGOCACHEdirectory (go env GOCACHE, usually~/.cache/go-build) along with the module cache.In a container image, pre-warm the cache at build time for the architectures you actually target:
dockerfileRUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -tags netgo,osusergo std && \ CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -tags netgo,osusergo stdParallax also caches the compiled module binaries themselves (
--cache, on by default, in~/.parallax/module-cache). That is a separate layer above the Go build cache, and both matter.
Matching module SDK source
Generated modules import the SDK source embedded in the controller binary. This
keeps release builds paired with the SDK they were built against, including when
the controller was compiled with -trimpath and is running outside a checkout.
For development or managed installations, PARALLAX_MODULE_SDK_PATH can select
an existing SDK directory. The resolver also checks the original source path
when available and /usr/local/share/parallax/module-sdk or
/opt/parallax/module-sdk, then extracts the embedded matching source as a
fallback. Overrides are the operator’s responsibility and should match the
controller version.
The embedded source does not include the Go toolchain or every dependency’s module cache. The first build may still download dependencies. The project Dockerfile includes Go and SDK source for controller use.
Offline operation
GenerateCode runs go mod tidy in the generated module directory unless
PARALLAX_OFFLINE is set. Setting it also swaps go fmt for a direct gofmt
invocation, avoiding the toolchain’s module resolution entirely:
export PARALLAX_OFFLINE=1Use this only when the SDK path resolves locally and the module’s imports are all
standard library or already in the local module cache — PARALLAX_OFFLINE skips
dependency resolution, it does not vendor anything for you.
Reference setup
acceptance/docker/ubuntu.dockerfile is the known-good configuration and is worth
copying:
# Go toolchain from the official distribution, on PATH
ENV GOLANG_VERSION 1.25.12
ENV PATH /usr/local/go/bin:${PATH}
RUN wget -q https://go.dev/dl/go${GOLANG_VERSION}.linux-amd64.tar.gz -O go.tgz \
&& tar -C /usr/local -xzf go.tgz \
&& rm go.tgz
# Module SDK source, at the path the resolver already knows about,
# and named explicitly so it does not depend on the resolver's fallbacks
COPY module-sdk /usr/local/share/parallax/module-sdk
ENV PARALLAX_MODULE_SDK_PATH=/usr/local/share/parallax/module-sdkNote that it does both: it installs to a known system path and sets the environment variable. That belt-and-braces approach is the recommendation.
Target host requirements
Targets need no Python, no agent and no Parallax installation. They do need:
| Requirement | Why |
|---|---|
| An SSH server, reachable from the controller | Default transport. local, docker and winrm connections are also available. |
| A shell and a writable temp directory | Compiled module binaries are copied in, executed and removed. |
| An architecture the controller can cross-compile for | Any Go-supported GOOS/GOARCH pair. |
sudo / su / doas | Only if you use become. |
Module-specific target requirements
Most modules are self-contained static binaries and shell out to nothing. The exceptions are worth knowing about:
| Module | Needs on the target | Notes |
|---|---|---|
unarchive | tar for .tar, .tar.gz, .tar.bz2, .tar.xz; gunzip for a bare .gz | Zip archives are handled natively via archive/zip and need nothing. |
apt, yum, dnf, pip, … | The corresponding package manager | Expected. |
service, systemd | The corresponding init system | Expected. |
Note:
unarchive’s use of externaltarandgunzipreflects current behaviour. Native extraction is in progress; when it lands, this row drops to “nothing” and this note should be removed.
Quick check
On the controller:
go version # must print 1.25 or newer
go env GOCACHE # must be writable, and ideally already populated
echo "${PARALLAX_MODULE_SDK_PATH:-<unset>}"
ls /usr/local/share/parallax/module-sdk 2>/dev/null || echo "not installed system-wide"If PARALLAX_MODULE_SDK_PATH is unset and no system path exists and you are
running a downloaded release binary, fix that before going further — see
The -trimpath trap.
On a target, from the controller:
parallax play -i inventory.ini --check ping.ymlThe first run is the slow one; it populates both the Go build cache and the module binary cache.
Next steps
- Installation — getting the
parallaxbinary - Quick Start — running your first playbook