Skip to main content

Bundle deep-dive

This is the package reference behind Prepare Docker Deployment. The runbook owns the command sequence; this page explains what the package bundle is and how to reason about it.

The bundle has two deployment-owned pieces:

  • .reploy/requirements.txt: root Arbiter packages selected for this deployment.
  • .reploy/bundle/: the complete wheelhouse used by the container at startup.

The goal is reproducible container creation. Reploy resolves package versions and prepares wheels during staging before the runtime container needs them, so Docker can create or recreate the runtime container without reaching PyPI.

Package roots

.reploy/requirements.txt records the root packages for the deployment. Normal package roots are exact pins:

.reploy/requirements.txt
arbiter-server==<version>
arbiter-imap==<version>
arbiter-smtp==<version>

Unpinned names and version ranges are rejected for runtime deployment state. Use the bundle commands in the prepare runbook to select plugins or upgrade versions.

By default, reploy stage arbiter-server seeds the bundle with the Arbiter server package root. When the installation bundle is prepared, Reploy resolves the package root to the exact version selected for the deployment.

bundle add and bundle remove update .reploy/requirements.txt. Adding arbiter-suite selects all plugins in the suite meta package. When the suite must be represented as concrete runtime roots, Arbiter expands it into arbiter-server plus the selected plugin packages.

Custom service plugins do not need to be known to Reploy's Arbiter blueprint. They must be Python packages that expose an Arbiter service entry point:

pyproject.toml
[project.entry-points."arbiter.services"]
my_plugin = "my_package:plugin"

For a plugin available from a package index, add an exact package pin:

reploy bundle add my-arbiter-plugin==1.0.0

After adding custom package roots, the next staging or app command prepares the installation bundle automatically. Published package pins may still need package-index access during preparation, and any missing transitive dependency wheels still have to be resolved during preparation.

The package named arbiter on PyPI is unrelated to this project. Use arbiter-suite or concrete packages such as arbiter-server, arbiter-smtp, and arbiter-imap.

Wheelhouse

Reploy automatically validates .reploy/requirements.txt, resolves the full runtime install set with the configured runtime image, and writes all required wheels to .reploy/bundle/ when the bundle is missing or out of date. Each preparation expands the wheelhouse fully and prunes stale wheels that are no longer part of the resolved install set.

When the staging directory is in an Arbiter repository checkout, normal preparation first rebuilds local Arbiter wheels from that checkout. Use bundle build --pypi-only only when the selected Arbiter package names should come from the package index instead of the checkout.

The generated Compose file mounts .reploy/bundle at /bundle. During container startup, the install command uses /bundle, so startup does not depend on package-index access.

Staging and app commands prepare the wheelhouse automatically when it is missing or out of date. Use bundle build only when you want to force that preparation early. Use bundle check to validate an already-prepared wheelhouse without downloading packages or building wheels. Use bundle list all after preparation to show every wheelhouse package, marked as root or transitive.

The default Docker env keeps the wheelhouse inside the deployment directory: REPLOY_BUNDLE_DIR=./.reploy/bundle. Keep runtime paths relative to the deployment directory; Linux install rejects absolute host paths for runtime files.

Server-owned runtime state is kept under the deployment directory by default: ARBITER_SERVER_DATA_DIR=./data/server. Docker mounts that host directory at /data/server, and the generated Compose command passes it to Arbiter as arbiter.storage.server_data_dir=/data/server. Self-signed TLS material lives there.

Plugin writable state is separate: ARBITER_PLUGIN_DATA_DIR=./data/plugins. Docker mounts that host directory at /data/plugins, and the generated Compose command passes it to Arbiter as arbiter.storage.plugin_data_dir=/data/plugins.

Upgrades

bundle upgrade upgrades root package requirements and marks the installation bundle for refresh. It reports changed root packages first, followed by changed transitive packages after the refreshed bundle is prepared.

For an installed Linux systemd deployment, prepare and test the staging directory first, then run install again.

Common upgrade targets are: no argument for all selected packages, a release line such as 0.9, a package such as arbiter-smtp, or an exact pin such as arbiter-smtp==0.9.4. Prerelease pins such as 0.9.0.dev1 are useful for validating a release line before the final package release.

Skip upgrade when you want to keep the versions already recorded in .reploy/requirements.txt.

Explicit package roots

Most deployments should use the bundle menu. To seed exact package roots during staging, pass repeated --requirement values with the Arbiter server app reference:

reploy stage arbiter-server \
--requirement arbiter-server==<version> \
--requirement arbiter-smtp==<version>

The same mechanism can express a curated suite plus an override:

reploy stage arbiter-server \
--requirement arbiter-suite==<version> \
--requirement arbiter-smtp==<version>

Arbiter expands that selection to concrete package pins so pip does not see conflicting meta-package dependencies:

.reploy/requirements.txt
arbiter-server==<version>
arbiter-smtp==<version>
arbiter-imap==<version>

For explicit local artifact bundles, .reploy/requirements.txt can also name container wheel paths directly:

.reploy/requirements.txt
/bundle/arbiter_server-<version>-py3-none-any.whl
/bundle/arbiter_smtp-<version>-py3-none-any.whl
Local checkout testing

Arbiter's blueprint declares local source mappings for the packages in this repository. When you stage from the local blueprint file, Reploy uses those local_sources entries to build the selected Arbiter packages from the checkout during bundle preparation:

reploy stage file:server/src/arbiter_server/reploy/arbiter.blueprint.yaml

Use package pins for third-party plugins. Persistent /source/... roots are not installable, and doctor --preinstall rejects them so the installed deployment never depends on a host checkout mount.

Maintainer package-index refresh

--pypi-only is intended for maintainers preparing package-index bundles. It resolves selected Arbiter package names from the package index instead of the checked-out repository packages.

For bundle build --pypi-only, the helper also rejects local path roots such as /source/app/..., ignores the existing deployment wheelhouse, rewrites .reploy/requirements.txt to the resolved exact versions, and builds a fresh wheelhouse from package-index results.

For bundle upgrade --pypi-only, the helper skips the local repo wheel refresh step and resolves upgrade targets from the package index.

Use this only when the selected Arbiter packages are expected to be available from the package index.