Skip to content
RepitlessPublic

About

Easy airgap k8s production bundle and management tools

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

40 Commits

Folders and files

Repository files navigation

Packube

Packube v0.1.1 builds self-contained Kubernetes packages online and installs them in air-gapped environments.

The package is the unit of ownership and transfer. A target selects package versions, while a bundle is only target.yaml plus a pool of unchanged package archives. There is no package lock, bundle manifest, or application digest chain.

The complete design, file contracts, command flows, validation policy, and code architecture are documented in docs/system-design-v0.1.1.md.

Core behavior

  • A package is a self-contained .tar.gz and can be inspected, planned, and installed independently.
  • target build remains the main online workflow. It reuses existing package archives, builds missing packages, and creates a bundle directory.
  • Platforms are configured once in packube.yaml and apply to every package in the workspace.
  • package scan scans every package by default. An optional package name limits it to one source. It adds missing image declarations without removing manual declarations.
  • Package and target builds never scan for images; they package only references already declared in spec.images.
  • Runtime packages have no Packube content digests. Operators may extract, edit, and recreate them with standard tar commands.
  • Archive path safety, schema validity, dependencies, image platforms, cluster access, and deployment failures remain hard errors.
  • K3s and K9s download checksums remain strict because they protect upstream executable distribution, not editable application content.

Build Packube

Packube requires Go 1.26.5 or newer to build this repository.

make build
./build/packube version

Package authoring additionally uses Helm when a package contains Helm deploy units and Skopeo when it contains images.

Image packaging resolves normal fully qualified references from their registry and falls back to the local Docker daemon when the registry source is unavailable. This allows locally built Compose images to be packaged without a separate export script.

Quick start

Create a workspace and package source:

packube init shop --platform linux/amd64 --platform linux/arm64
cd shop
packube package new api --version 1.4.0

Edit packages/api/package.yaml and its referenced files, then discover and build images:

packube package scan
packube package scan api --check
packube package build api

Define a target in targets/production.yaml, inspect its exact resolution, and build the bundle directory:

packube target inspect production
packube target build production
packube target build production --rebuild api
packube target build production --rebuild-all

Run package scan after changing manifests, charts, or values. Both package build and target build use only the image references already declared in spec.images.

The generated layout is intentionally simple:

dist/
├── packages/
│   ├── api-1.4.0.tar.gz
│   └── database-16.2.0.tar.gz
└── bundles/
    └── production/
        ├── target.yaml
        └── packages/
            ├── api-1.4.0.tar.gz
            └── database-16.2.0.tar.gz

Transfer the directory directly or create an outer tar with the system tool:

tar -czf production.tar.gz -C dist/bundles/production .

Inspect, plan, and install offline:

packube bundle inspect production.tar.gz
packube bundle plan production.tar.gz --context airgap
packube bundle install production.tar.gz --context airgap

bundle plan performs cluster and tool preflight checks without importing images or changing Kubernetes resources. bundle install repeats those checks, installs the complete dependency closure in dependency-first order, and stops at the first failure without rollback.

After another K3s node joins, import the same selected application images into that node's local containerd store without applying manifests or running Helm:

sudo packube bundle install production.tar.gz --import-only

Run this command locally on every new node. It chooses the archives matching that host's OS and architecture and does not require Kubernetes API access.

Workspace formats

packube.yaml owns the complete platform set:

apiVersion: packube/v1alpha1
kind: Project
metadata:
  name: shop
spec:
  platforms:
    - os: linux
      architecture: amd64
    - os: linux
      architecture: arm64

A package source owns dependencies, declared images, deploy units, and readiness rules:

apiVersion: packube/v1alpha1
kind: PackageSource
metadata:
  name: api
  version: 1.4.0
spec:
  dependencies:
    - name: database
      version: ">=16.0.0 <17.0.0"
  images:
    - registry.example.com/shop/api:1.4.0
  deploy:
    - manifests:
        - manifests/*.yaml
    - helm:
        chart:
          name: api
          repository: https://charts.example.com
          version: 1.4.0
        release: api
        namespace: api
        values:
          - values/production.yaml
  readiness:
    - resource:
        apiVersion: apps/v1
        kind: Deployment
        namespace: api
        name: api
      condition:
        type: Available
        status: "True"

A target selects root package constraints. Transitive dependencies come from the selected runtime package or package source:

apiVersion: packube/v1alpha1
kind: Target
metadata:
  name: production
spec:
  packages:
    - name: api
      version: ">=1.4.0 <2.0.0"

Packube selects the highest compatible version in the current source/artifact pool. It selects one version per name and rejects missing packages, conflicts, cycles, and duplicate identities. Because v0.1.1 writes no lock file, use exact constraints when resolution must not change as new packages are added.

JSON schemas and complete examples are in schemas.

Standalone offline package

Package commands require a package tar, not an extracted directory:

packube package inspect api-1.4.0.tar.gz
packube package plan api-1.4.0.tar.gz --pool ./packages
packube package install api-1.4.0.tar.gz --pool ./packages

The default dependency policy finds each direct dependency package in the sibling directory or --pool, selects its highest compatible version, and checks its declared readiness against the live cluster without reinstalling it. Dependencies therefore need meaningful readiness rules.

Use --recursive to resolve and install the complete dependency closure, or --ignore-dependencies to install only the requested package with warnings. These flags are mutually exclusive.

Offline customization

Use standard tar commands; Packube has no pack, unpack, or patch command:

mkdir api-1.4.0
tar -xzf api-1.4.0.tar.gz -C api-1.4.0
# Edit manifests, values, or package.yaml.
tar -czf api-1.4.0-custom.tar.gz -C api-1.4.0 .
packube package inspect api-1.4.0-custom.tar.gz

Retarring does not require checksum regeneration. Do not edit an embedded OCI archive by hand; rebuild or replace that archive instead. If a customized package keeps the same name and version, replace the original archive in the pool because two files with the same internal identity are rejected.

Incremental bundle updates

There is no full-bundle rebuild requirement. Copy only the changed package and, when necessary, the changed target:

cp api-1.4.1.tar.gz production/packages/
cp targets/production.yaml production/target.yaml
packube bundle plan production

A package pool may contain multiple versions. Unused versions produce warnings.

There is no bundle uninstall. Packube does not record enough ownership state to safely remove shared namespaces, CRDs, Helm releases, YAML objects, or images. Use Helm, kubectl, or a cluster reset for explicit teardown and refresh flows.

Offline cluster tools

# Online
packube k3s prepare
packube self-copy

# Offline first control-plane node
sudo packube k3s init k3s-cluster-<version>-<arch>.tar.gz \
  --vip 192.168.1.100

# Offline additional control-plane node
sudo packube k3s join k3s-cluster-<version>-<arch>.tar.gz \
  --vip 192.168.1.100 \
  --server-ip 192.168.1.10

packube monitor k9s --readonly
packube cluster doctor

K3s installation detects the default interface and host IPv4 address unless they are supplied explicitly. The prepared K3s bundle includes K9s and a shared cluster token; treat it as private material.

Command structure

packube
├── init
├── package
│   ├── new
│   ├── scan
│   ├── build
│   ├── inspect
│   ├── plan
│   └── install
├── target
│   ├── inspect
│   └── build
├── bundle
│   ├── inspect
│   ├── plan
│   └── install
├── cluster
│   └── doctor
├── k3s
│   ├── prepare
│   ├── init
│   └── join
├── monitor
│   └── k9s
├── self-copy
└── version

Package and bundle installation currently supports exactly one schedulable node when the installation contains images, because installation imports into the local image store. Packages without images may be installed on a multi-node cluster. Use bundle install --import-only locally after each additional node joins; that mode only imports images and never redeploys workloads.

About

Easy airgap k8s production bundle and management tools

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages