Skip to content

Offline Installation (Air-Gapped Servers)

This guide is for customers whose target server has no internet access and cannot reach the Python Package Index directly. The idea: build a self-contained bundle on an internet-connected machine, transfer it to the target server, and install from local files only.

When to Use This Method

Use this approach when:

  • The target server lives in an isolated network segment.
  • Outbound HTTPS to pypi.org is blocked by a firewall or proxy policy.
  • You need a reproducible, ship-once-install-anywhere artifact for a customer handover.

For internet-connected hosts, prefer the PyPI install or the Git-based install — they are simpler.

Overview

graph LR
A[Build Host with Internet] --> B[Download Wheels] --> C[Create tar.gz] --> D[Transfer to Target] --> E[pip install --no-index]

Two machines are involved:

Role Needs Internet Purpose
Build host Yes Downloads all wheels and builds the bundle
Target server No Receives the bundle and installs offline via pip

Warning

The build host should match the target's Python version and Linux platform. Wheels are Python-version- and platform-specific — a bundle built on macOS for Python 3.12 will not install on a Linux server running Python 3.11.

Build the Bundle

On the build host, clone the repository and run the helper script. It lives in tools/ and wraps pip download plus a bit of packaging:

git clone https://github.com/kuhn-ruess/cmdbsyncer
cd cmdbsyncer
./tools/build_offline_bundle.sh --include-syncer --include-enterprise \
    --python-version 3.11 \
    --platform manylinux2014_x86_64

The bundle always ships the base, extras and ansible Python dependencies plus the default Ansible playbook collection — there are no toggles for these any more. The script produces:

  • offline_bundle/ — directory with packages/, the three requirements*.txt files, the bundled ansible/ playbook tree, an install.sh and a README.txt
  • offline_bundle.tar.gz — the same folder as a single archive ready to ship

Script Options

Flag Purpose
--include-syncer Also download the cmdbsyncer package from PyPI into the bundle
--include-enterprise Also download the cmdbsyncer-enterprise package from PyPI
--syncer-version VER Pin cmdbsyncer to exactly this version (e.g. 4.1.0.dev3). Required for pre-releases.
--enterprise-version VER Same idea for cmdbsyncer-enterprise
--python-version VER Target Python version, e.g. 3.11 — must match the target server
--platform TAG Target platform tag, e.g. manylinux2014_x86_64 for typical Linux
--output-dir DIR Override output directory (default: offline_bundle)
--no-archive Skip the tar.gz step; only produce the directory

Note

When --platform is set, the script enforces --only-binary=:all: so pip ships wheels instead of source distributions. Pure-Python packages work fine; packages with C extensions must have a matching wheel on PyPI. If a wheel is missing, pip will abort with a clear error.

Bundling pre-releases

Pre-release builds (.devN, aN, bN, rcN) are not picked up by an unpinned cmdbsyncer request — pip ignores them by default. Always pass --syncer-version (and --enterprise-version if you bundle Enterprise too) for a pre-release build:

./tools/build_offline_bundle.sh \
    --include-syncer    --syncer-version 4.1.0.dev3 \
    --include-enterprise --enterprise-version 0.3.9.dev1 \
    --python-version 3.11 --platform manylinux2014_x86_64

Without the version flags the bundle ships the latest stable release. Either way the generated install.sh pins to the same exact wheel, so the customer's pip never has to make a resolution decision in the field.

Transfer the Bundle

Copy offline_bundle.tar.gz to the target server using whatever out-of-band channel is available — SCP over a jump host, a USB drive, an internal artifact repository, a support ticket attachment:

scp offline_bundle.tar.gz user@target-server:/tmp/

Install on the Target Server

On the target server, extract the archive, create a virtual environment, and run the bundled installer:

cd /opt
tar -xzf /tmp/offline_bundle.tar.gz
python3.11 -m venv venv
source venv/bin/activate
/opt/offline_bundle/install.sh

The bundled install.sh installs every requirement file from the packages/ directory and copies the bundled Ansible playbook collection to /opt/cmdbsyncer/ansible.

Customising the install

The installer respects a few environment variables — set them before running install.sh:

Variable Effect
ANSIBLE_TARGET=/path Where the playbook collection lands (default: /opt/cmdbsyncer/ansible). An existing directory is replaced.
SKIP_ANSIBLE=1 Skip the playbook copy entirely (you already manage the playbooks elsewhere)

The pip and Ansible steps run independently — a failure in one is reported but never silently swallows the other.

After install, point cmdbsyncer at the playbook directory by exporting CMDBSYNCER_ANSIBLE_DIR=<path> in the service environment, or by adding ANSIBLE_DIR=<path> to local_config.py.

Next Steps

If you built the bundle with --include-syncer, the cmdbsyncer console script is now on your PATH — the application behaves exactly like a regular PyPI install. Continue with Installation from PyPI to initialize the application (cmdbsyncer sys self_configure), point it at MongoDB, and set up a WSGI server.

If you built the bundle without --include-syncer, the Syncer source code is not part of the bundle — ship the repository separately (e.g. as a tarball or Git archive) and follow the Installation from Code guide from the ./cmdbsyncer sys self_configure step onwards, using the venv you just populated.

Upgrades

For every new release, rebuild the bundle on the internet-connected host, transfer the new archive, and re-run install.sh. pip will pick up newer wheels from packages/ and upgrade in place. After the install, re-run self_configure so new default values are seeded:

cd /opt/cmdbsyncer
source venv/bin/activate
./cmdbsyncer sys self_configure

Troubleshooting

ERROR: Could not find a version that satisfies the requirement ... The wheel for that package is missing from packages/. Verify that --python-version and --platform on the build host match the target server. Rebuild with matching values.

ERROR: ... is not a supported wheel on this platform Same root cause — the bundle was built for a different interpreter or CPU architecture. A common trap is building on macOS (macosx_* tags) for a Linux target. Always pass --platform manylinux2014_x86_64 for typical Linux servers.

Dependency conflict warnings from a previously installed cmdbsyncer An older Syncer version in the venv pins older dependency versions. Run pip uninstall cmdbsyncer first, or upgrade the Syncer itself to the matching release.

Packages with C extensions fail to build Some integrations (notably python-ldap) need system headers (libldap2-dev, libsasl2-dev, ...) if no wheel is available. Install them via the distribution package manager on the target server, or make sure the build host has matching wheels available on PyPI.