Skip to main content

Air-Gapped Installation

Overview​

For networks without internet access, installation is performed using a single bundle. The procedure consists of three steps.

  1. Create a bundle on a machine with internet access (or download a bundle issued by KOPENS).
  2. Transfer the bundle to the air-gapped server using an authorized medium.
  3. Load and install the bundle on the air-gapped server.

The bundle includes the Docker engine installation package. Docker does not need to be pre-installed on the target server.

Bundle Contents​

The output is a single file plantpulse-platform-images-<version>.tar.gz.

PathContent
images/One tar file per image docker save
docker-rpms/Docker engine rpm for RHEL · Rocky
docker-debs/Docker engine deb for Ubuntu (select via OS_TARGET)
repo/Installation/operation scripts and the entire compose/ (including bin/env.sh)
NOTICEEmbedded third-party components and their license locations
EULA.txtTerms of use for PlantPulse

NOTICE and EULA.txt are also inside the images but are placed at the root of the bundle. This prevents the need to docker load a 10GB tar file just to see what it contains.

Step 1 — Creating the Bundle​

The recommended path is to receive a bundle issued by KOPENS.

https://product.kopens.io/plantpulse-platform/plantpulse-platform-images-<version>.tar.gz

If you must create it yourself, run the following on a machine with internet access.

cd /opt/kopens/plantpulse-platform-docker/bin
./airgap-bundle.sh
Environment VariableDefaultMeaning
INCLUDE_DATALAKE1Include data lake images
OS_TARGETrhelrhel · ubuntu · both — Which OS package to include
VERSIONCurrent month (yyyy.mm)Version embedded in the bundle filename
OUT_TGZUnder repositoryManually specify the output path
AIRGAP_KEEP3Delete old bundles, keeping only the latest N
Do not create with INCLUDE_DATALAKE=0

All five nodes (master and workers) in the cluster will boot as plantpulse-datalake images (since 2026-08-31, compose/docker-compose.worker.yml has been replaced by this image). A bundle baked with this value set to 0 will fail to establish either the master or workers. This is why the default is 1.

Do not manually write the image list

airgap-bundle.sh extracts the images to be included via docker compose config --images. Previously, the list was hard-coded in the script, but in a 2026-09-01 measurement, the stack actually required nine images, and plantpulse-platform was not among them — no service in compose/docker-compose.yml boots it. In other words, that bundle contained «one unused image» and missed «eight required images», making air-gapped installation impossible. Do not maintain duplicate lists.

Platform credentials must be configured first on the machine baking the bundle. Since the compose file declares those values as :?, the script will end in an error instead of producing an empty bundle if they are missing.

Step 2 — Transferring to the Air-Gapped Server​

Transfer .tar.gz to the target server using authorized media such as USB, internal file servers, or scp.

Step 3 — Load and Installation​

sudo -i
mkdir -p /opt/kopens/plantpulse-platform-docker
tar -xzf plantpulse-platform-images-*.tar.gz -C /opt/kopens/plantpulse-platform-docker

# 자격증명 사이드카는 폐쇄망 서버에도 있어야 한다
vi /etc/kopens/plantpulse-platform.env

cd /opt/kopens/plantpulse-platform-docker/repo/bin
./airgap-load.sh

airgap-load.sh processes the steps in order.

  1. Bundle Verification — Verify signatures and checksums using a sidecar file set (see below).
  2. Offline Docker Engine Installation — rpm -ivh or dpkg -i. Skipped if already installed.
  3. Image Load — docker load all tars within images/.
  4. Cover Inspection — Compare loaded images against compose config --images.
  5. Call install.sh — SKIP_LOGIN=1. Since images are already local, registry login is not performed.

Step 0 — Bundle Verification (Sidecar File Set since 2026-09-18)​

A set of files accompanies the bundle.

FileDescription
<번들>.tar.gz.sha256sha256sum -c input
<번들>.tar.gz.sha256.sigcosign signature for that checksum file
<번들>.tar.gz.pubCopy of the verification key

airgap-load.sh runs two steps in order — first verify the signature, then apply the checksum. This is because an unauthenticated checksum is not valid evidence.

# 1) 이 체크섬이 «우리» 것인가
cosign verify-blob --key <번들>.tar.gz.pub \
--signature <번들>.tar.gz.sha256.sig <번들>.tar.gz.sha256

# 2) 번들의 바이트가 그 체크섬과 맞는가
sha256sum -c <번들>.tar.gz.sha256
The signature target is the «checksum file», not the «bundle»

The bundle exceeds 8 GB, and cosign sign-blob loads the entire target into memory. Therefore, the signature is placed on the checksum sidecar file, and the bundle itself is verified using that checksum. The old <번들>.tar.gz.sig is no longer produced — if you have a procedure referencing that name, please correct it.

If sidecar files are missing, it proceeds «without verification»

airgap-load.sh logs the absence of sidecar files and continues installation. Bundles that only carry the old name (.tar.gz.sig) will be installed while skipping verification. Always read the Step 0 line printed on the screen.

This verification catches corruption during transmission or partial replacements. An adversary capable of replacing the entire bundle can also replace the sidecar files — therefore, for sites requiring security reviews, receive the key via a path separate from the bundle for verification.

Step 3 is the core value of this script. If there are missing images, it prints the names and stops before starting the installation. Without this, the installation would proceed for a while and then fail due to a registry pull timeout, whereas the registry is inaccessible in an air-gapped network.

The process stops immediately if images/ is empty or if any single tar fails to load — it will not start installation with missing components.

Post-installation boot verification (./up.sh, ./stack-verify-boot.sh) and operation inspection (./ops-check.sh) are the same as Docker Installation.

Air-Gapped Update​

Re-bake the bundle with the new version, transfer it using the same procedure, then unpack the new bundle and re-run bin/airgap-load.sh inside it. If an installed stack exists, it is identified as an upgrade; it is backed up first ([2.9] An installed stack was found … this is an UPGRADE), images are loaded, and the installer is run. If it fails, it automatically reverts to the previous version — support scope and backup/reversion details are in Upgrade.

  • For reversion, the previous version's images must remain on the server. If deleted, docker load the previous bundle again.
  • Regenerating only containers via ./update.sh is not an upgrade — operation scripts and compose files remain as the old version.

Common Pitfalls​

SymptomCauseAction
Bundle creation ends with «No images found»Platform credentials missing on the baking machineConfigure sidecar first and re-run
Worker does not bootBundle baked with INCLUDE_DATALAKE=0Re-bake with default (1)
Registry pull timeout during installationStack requires images not in the bundleRe-bake according to the «missing image» list printed by airgap-load.sh
Docker fails to install on Ubuntu target serverBundle contains only rpmRe-bake with OS_TARGET=ubuntu or both

Learn More​