Air-Gapped Installation
Overview
For networks without internet access, installation is performed using a single bundle. The procedure consists of three steps.
- Create a bundle on a machine with internet access (or download a bundle issued by KOPENS).
- Transfer the bundle to the air-gapped server using an authorized medium.
- 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.
| Path | Content |
|---|---|
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) |
NOTICE | Embedded third-party components and their license locations |
EULA.txt | Terms 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 Variable | Default | Meaning |
|---|---|---|
INCLUDE_DATALAKE | 1 | Include data lake images |
OS_TARGET | rhel | rhel · ubuntu · both — Which OS package to include |
VERSION | Current month (yyyy.mm) | Version embedded in the bundle filename |
OUT_TGZ | Under repository | Manually specify the output path |
AIRGAP_KEEP | 3 | Delete old bundles, keeping only the latest N |
INCLUDE_DATALAKE=0All 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.
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.
- Bundle Verification — Verify signatures and checksums using a sidecar file set (see below).
- Offline Docker Engine Installation —
rpm -ivhordpkg -i. Skipped if already installed. - Image Load —
docker loadall tars withinimages/. - Cover Inspection — Compare loaded images against
compose config --images. - 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.
| File | Description |
|---|---|
<번들>.tar.gz.sha256 | sha256sum -c input |
<번들>.tar.gz.sha256.sig | cosign signature for that checksum file |
<번들>.tar.gz.pub | Copy 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 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.
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 loadthe previous bundle again. - Regenerating only containers via
./update.shis not an upgrade — operation scripts and compose files remain as the old version.
Common Pitfalls
| Symptom | Cause | Action |
|---|---|---|
| Bundle creation ends with «No images found» | Platform credentials missing on the baking machine | Configure sidecar first and re-run |
| Worker does not boot | Bundle baked with INCLUDE_DATALAKE=0 | Re-bake with default (1) |
| Registry pull timeout during installation | Stack requires images not in the bundle | Re-bake according to the «missing image» list printed by airgap-load.sh |
| Docker fails to install on Ubuntu target server | Bundle contains only rpm | Re-bake with OS_TARGET=ubuntu or both |
Learn More
- Docker Installation — Configuration of nine containers and manual installation procedures
- One-Line Installation — Recommended path for environments with internet access
- Initial Password Change — Mandatory action immediately after installation