The image contract
Your project decides everything about the base image its workspaces come from.
What the image must have
The image must provide:
- a workspace user,
$PARA_USERwith uid/gid$PARA_UID/$PARA_GID(appand1000/1000by default). para runs hooks andpara shas that user and chowns every pushed file to those ids; - bash, because para invokes hooks and shells via
su -s /bin/bash; - util-linux
su, if you runpara sh <ws> -c '<cmd>'from a terminal; - a shutdown path. Provide either a
poweroffcommand that the guest's init acts on, or an init that responds to LXC's halt signal (SIGPWRby default). Without either,para downwaits 60 seconds and fails with an error namingincus stop -f <container>.
You can also include a login shell such as zsh, dotfiles, and coding-agent tools.
Building with para image build
Build the image after editing your layers' hooks/image-build files:
para image buildThe build starts from $PARA_IMAGE_BASE, runs $PARA_IMAGE_BOOTSTRAP, then runs each layer's image-build hook in stack order. Hooks run as root in a container that supports nesting, without a tty or stdin. The finished image is published as $PARA_IMAGE_NAME.
Use $PARA_LAYER_DIR to access files from your layer during the build. See Hooks for the hook environment.
Two caveats:
- Images are per-arch, so build on the machine that runs them (arm64 on Apple Silicon).
-i/--from-currentlayers onto the existing$PARA_IMAGE_NAMEinstead of a pristine base, and skips the bootstrap. Use it for faster iteration with idempotent hooks, then run a clean build to verify the result.
The bundled base layer's hooks/image-build is the reference: Void packages, a workspace user, and the zsh extension paths bundled layers use. Docker lives in its own layer.
Checking it with para image status
$ para image status
image myapp
built 2026/07/21 14:02 UTC
base images:voidlinuxRebuild when you've edited your hooks/image-build.
para image rm deletes $PARA_IMAGE_NAME, to reclaim space or force a fully clean next build. Workspaces already up are clones and keep running.