Portworx Enterprise Deployment on StarlingX¶
Overview
Portworx is an enterprise-grade, Kubernetes-native storage and data management platform. It automates persistent storage, high availability, data protection, and disaster recovery for containers and virtual machines across multi-cloud and on-premises environments.
This guide installs Portworx on StarlingX and runs it as a persistent-storage backend. The Portworx cluster install is tested for both PX-StoreV1 (Btrfs) and PX-StoreV2 (DM-thin) datastores.
Note
PX-StoreV1 is the legacy Portworx storage backend that leverages Btrfs, a Linux copy-on-write filesystem.
PX-StoreV2 is the newer Portworx storage engine that uses Linux DM-thin for improved performance and scalability.
Portworx requires the px kernel module (px-fuse), which is not included in the
stock kernel. The KMM application, part of the StarlingX
release, is used to build and load it on every Portworx worker — storage and
application.
Both online and air-gapped installation paths are documented below. Air-gapped steps are marked with Air-gapped and can be skipped for online deployments.
Note
If upgrading StarlingX, then install Portworx after the upgrade is finalized, as Upgrade, downgrade and Backup/Restore of Portworx are not supported on StarlingX.
Prerequisites
StarlingX must be installed and fully deployed before performing this procedure.
Standard deployment: two controllers, three storage nodes (minimum requirement for Portworx), and one application worker.
Portworx storage nodes can only be workers.
At least 8 application CPU cores per storage worker. See Test Environment Configuration.
Note
StarlingX reserves the platform core(s), so size each worker with enough CPU to leave at least 8 application cores (for example 10 CPU yields 9 application cores). Verify with
system host-cpu-list <worker>.4 GB (min) to 8 GB (desired) of memory available for Portworx per storage worker.
One dedicated disk per storage worker for the Portworx data and metadata partitions (
/dev/sdcin this guide). 256 GB was used for testing.Note
The disk must be large enough to hold the metadata partition (64 GB) and the data partition (the remainder). If the disk is too small, the install fails. The application worker needs no data disk.
A dedicated cluster-host interface on every storage worker.
Note
The cluster-host interface must be separate from the management interface.
Connectivity:
Online: Active Internet connection to clone the px-fuse source and pull the Portworx Helm chart.
Air-gapped: An Internet-connected staging machine to pre-stage the build bases, px-fuse source, and images, plus a means to transfer files to the air-gapped system.
Test Environment Configuration¶
Resource |
Controller (x2) |
Storage worker (x3) |
Application worker (x1) |
|---|---|---|---|
Hostname |
|
|
|
Role |
Control plane |
Portworx storage |
Portworx compute |
CPU |
8 |
10 |
4 |
VM memory |
20 GB |
16 GB |
12 GB |
Disks |
|
|
|
Air-Gapped Preparation¶
Note
Skip the steps in this section if it is an online installation.
Procedure
Perform the following steps on an Internet-connected staging machine.
Fetch base docker images and px-fuse source.
KMM_BUILDER_TAG="stx.13.0-v1.0.0"
# Bake autoconf into the stock kmm-builder image and tag it for the local registry. cat > Dockerfile.kmm-builder-base <<EOF FROM docker.io/starlingx/kmm-builder:${KMM_BUILDER_TAG} RUN apt-get update && apt-get install -y autoconf && rm -rf /var/lib/apt/lists/* EOF docker build -f Dockerfile.kmm-builder-base \ -t registry.local:9001/starlingx/kmm-builder:${KMM_BUILDER_TAG} . # Bake kmod into debian:trixie-slim and tag it for the local registry. cat > Dockerfile.trixie-base <<'EOF' FROM debian:trixie-slim RUN apt-get update && apt-get install -y kmod && rm -rf /var/lib/apt/lists/* EOF docker build -f Dockerfile.trixie-base \ -t registry.local:9001/debian:trixie-slim . # Clone the px-fuse source and pack it for transfer. git clone https://github.com/portworx/px-fuse.git
Identify KMM application images.
# Prerequisites: # 1. Copy the chart from the controller: # scp controller-0:/usr/local/share/applications/helm/kernel-module-management-*.tgz . # 2. Extract the charts directory: # tar xzf kernel-module-management-*.tgz # 3. cd into the extracted charts directory # List KMM images referenced by the KMM application chart helm template kernel-module-management-*.tgz \ | grep -Eo '^[[:space:]]*(-[[:space:]]+)?(image|value):[[:space:]]+"?[^"[:space:]]+/[^"[:space:]]+:[^"[:space:]]+' \ | sed -E 's/^.*(image|value):[[:space:]]+"?//' \ | sort -u # Save the KMM image list, one per line IMAGES_KMM_FILE=images-kmm.txt cat > "$IMAGES_KMM_FILE" <<'EOF' gcr.io/k8s-staging-kmm/kernel-module-management-operator:v20260415-v2.6.0 gcr.io/k8s-staging-kmm/kernel-module-management-worker:v20260415-v2.6.0 gcr.io/k8s-staging-kmm/kernel-module-management-signimage:v20260415-v2.6.0 gcr.io/k8s-staging-kmm/kernel-module-management-webhook-server:v20260415-v2.6.0 gcr.io/kaniko-project/executor:236ba5690eda9170d0157aa8137ebbeb09d38685 EOF
Fetch the Portworx Helm Chart and discover container images.
PX_VERSION="3.6.0" K8S_VERSION="1.35.2" # K8s version on the controller (kubectl version) helm repo add portworx https://raw.githubusercontent.com/portworx/helm/master/stable helm repo update helm pull portworx/portworx --version 9.0.0 # Images from the portworx version manifest IMAGES_FROM_YAML_FILE=images-from-yaml.txt curl -fsSL -o versions.yaml \ "https://install.portworx.com/${PX_VERSION}/version?kbver=${K8S_VERSION}" grep -Eo '\S+/\S+:\S+' versions.yaml | sort -u > "$IMAGES_FROM_YAML_FILE" # Images from the portworx air-gapped install script IMAGES_FROM_SH_FILE=images-from-sh.txt curl -fsSL -o px-ag-install.sh "https://install.portworx.com/${PX_VERSION}/air-gapped" eval "$(grep '^IMAGES=' px-ag-install.sh)" printf '%s\n' $IMAGES | sort -u > "$IMAGES_FROM_SH_FILE" # Images from the portworx Helm chart IMAGES_FROM_HELM_CHART=images-from-helm.txt helm template portworx/portworx --version 9.0.0 \ | grep -Eo '\S+/\S+:\S+' | tr -d '"' | sort -u > "$IMAGES_FROM_HELM_CHART"
Important
The Portworx Helm chart bundles an
alpine/kubectlimage (e.g.alpine/kubectl:1.36.0). This must match the Kubernetes Server Version on the target controller, hence replace the kubectl image tag in$IMAGES_FROM_HELM_CHARTwith the correct version:# Replace the kubectl image version to match target cluster sed -i "s|alpine/kubectl:.*|alpine/kubectl:${K8S_VERSION}|" "$IMAGES_FROM_HELM_CHART" cat "$IMAGES_FROM_HELM_CHART"
Export artifacts for transfer to the target system.
KMM_BUILDER_TAG="stx.13.0-v1.0.0"
SRC_DIR="$PWD" mkdir -p px-airgap && cd px-airgap # Base images for the controller build sudo skopeo copy --override-arch amd64 --override-os linux \ "docker-daemon:registry.local:9001/starlingx/kmm-builder:${KMM_BUILDER_TAG}" \ "docker-archive:kmm-builder-base.tar:registry.local:9001/starlingx/kmm-builder:${KMM_BUILDER_TAG}" sudo skopeo copy --override-arch amd64 --override-os linux \ "docker-daemon:registry.local:9001/debian:trixie-slim" \ "docker-archive:trixie-slim.tar:registry.local:9001/debian:trixie-slim" # Portworx + KMM images, read from the three list files created in previous steps for list in "$IMAGES_FROM_SH_FILE" "$IMAGES_FROM_YAML_FILE" "$IMAGES_KMM_FILE" "$IMAGES_FROM_HELM_CHART"; do while read -r img; do [ -n "$img" ] || continue ref="$img" ref="${ref#docker.io/}"; ref="${ref#registry.k8s.io/}" file="$(echo "$ref" | tr '/:' '__').tar" skopeo copy --override-arch amd64 --override-os linux \ "docker://$img" "docker-archive:$file:registry.local:9001/$ref" || echo "FAIL $img" done < "$SRC_DIR/$list" done cd "$SRC_DIR" tar czf portworx-airgap-bundle.tar.gz \ px-airgap/ px-fuse/ versions.yaml portworx-9.0.0.tgz
Transfer
portworx-airgap-bundle.tar.gzto the air-gapped StarlingX controller.Load the artifacts into registry.local (
controller-0) and perform the following steps on the active controller (controller-0).tar xzf portworx-airgap-bundle.tar.gz USERNAME="sysinv" PASSWORD=$(keyring get sysinv services) echo "$PASSWORD" | sudo docker login registry.local:9001 -u "$USERNAME" --password-stdin # Load and push container images for img_tar in px-airgap/*.tar; do sudo docker load -i "$img_tar"; done sudo docker images --format '{{.Repository}}:{{.Tag}}' \ | grep '^registry.local:9001' \ | while read -r img; do sudo docker push "$img"; done # Push the Helm chart as an OCI artifact echo "$PASSWORD" | helm registry login registry.local:9001 -u "$USERNAME" --password-stdin helm push portworx-9.0.0.tgz oci://registry.local:9001/helm-charts # Verify the bases and chart are present system registry-image-tags starlingx/kmm-builder system registry-image-tags debian helm show chart oci://registry.local:9001/helm-charts/portworx --version 9.0.0
Build px-fuse Kernel Module¶
Procedure
Perform the following steps on the active controller (controller-0).
Install the KMM application.
# Upload the KMM application and wait until it is 'uploaded' system application-upload \ /usr/local/share/applications/helm/kernel-module-management-*.tgz watch -n 10 system application-show kernel-module-management # Local-registry credentials, base64-encoded for the docker config USERNAME="sysinv" PASSWORD=$(keyring get sysinv services) DOCKER_CREDENTIALS=$(echo -n "${USERNAME}":"${PASSWORD}" | base64) # Build a docker auth config so KMM can pull from registry.local cat >/tmp/docker-config.json <<EOF { "auths": { "https://registry.local:9001": { "auth": "$DOCKER_CREDENTIALS" } } } EOF # Encode the whole config; KMM stores it in the pull secret dconfigjson=$(cat /tmp/docker-config.json | base64 -w 0) # Helm override that wires the pull secret into the KMM app cat >~sysadmin/kmm-app-override.yaml <<EOF dockerRegistrySecretName: "kmm-registry-secret" dockerConfigJson: "$dconfigjson" EOF # Apply the override and bring the app up, then wait until 'applied' system helm-override-update kernel-module-management \ kernel-module-management kernel-module-management \ --values ~sysadmin/kmm-app-override.yaml system application-apply kernel-module-management watch -n 10 system application-show kernel-module-management
Build the px-fuse kernel module image.
Online:
KMM_BUILDER_TAG="stx.13.0-v1.0.0"
cat >~sysadmin/kmm-px-cm.yaml <<EOF apiVersion: v1 kind: ConfigMap metadata: name: kmm-px-cm namespace: kernel-module-management data: dockerfile: | FROM docker.io/starlingx/kmm-builder:${KMM_BUILDER_TAG} as builder ARG KERNEL_FULL_VERSION RUN apt-get update && apt-get install -y autoconf WORKDIR /usr/src RUN ["git", "clone", "https://github.com/portworx/px-fuse.git"] WORKDIR /usr/src/px-fuse RUN autoreconf && ./configure && make KERNELPATH=/lib/modules/\${KERNEL_FULL_VERSION}/build && mkdir /out && cp *.ko /out/ FROM debian:trixie-slim ARG KERNEL_FULL_VERSION RUN apt-get update && apt-get install -y kmod && rm -rf /var/lib/apt/lists/* COPY --from=builder /out/*.ko /opt/lib/modules/\${KERNEL_FULL_VERSION}/ RUN depmod -b /opt \${KERNEL_FULL_VERSION} EOF kubectl apply -f ~sysadmin/kmm-px-cm.yaml
Air-gapped:
KMM_BUILDER_TAG="stx.13.0-v1.0.0"
KERNEL_VERSION="6.18.15+deb13-amd64" KERNEL_VERSION_RT="6.18.15+deb13-rt-amd64" PX_FUSE_IMAGE_TAG="6.18.15_deb13-amd64" PX_FUSE_IMAGE_TAG_RT="6.18.15_deb13-rt-amd64" # Please make sure to use a KMM builder image aligned to the kernel version. # The KMM builder image supports the kernel version shipped in the same release. cat > Dockerfile.px-fuse <<EOF FROM registry.local:9001/starlingx/kmm-builder:${KMM_BUILDER_TAG} as builder ARG KERNEL_FULL_VERSION WORKDIR /usr/src/px-fuse COPY px-fuse/ . RUN autoreconf && ./configure && \\ make KERNELPATH=/lib/modules/\${KERNEL_FULL_VERSION}/build && \\ mkdir /out && cp *.ko /out/ FROM registry.local:9001/debian:trixie-slim ARG KERNEL_FULL_VERSION COPY --from=builder /out/*.ko /opt/lib/modules/\${KERNEL_FULL_VERSION}/ RUN depmod -b /opt \${KERNEL_FULL_VERSION} EOF sudo docker build -f Dockerfile.px-fuse \ --build-arg KERNEL_FULL_VERSION="$KERNEL_VERSION" \ -t "registry.local:9001/kmm/px:$PX_FUSE_IMAGE_TAG" . sudo docker build -f Dockerfile.px-fuse \ --build-arg KERNEL_FULL_VERSION="$KERNEL_VERSION_RT" \ -t "registry.local:9001/kmm/px:$PX_FUSE_IMAGE_TAG_RT" . echo "$(keyring get sysinv services)" \ | sudo docker login registry.local:9001 -u sysinv --password-stdin sudo docker push registry.local:9001/kmm/px:$PX_FUSE_IMAGE_TAG sudo docker push registry.local:9001/kmm/px:$PX_FUSE_IMAGE_TAG_RT system registry-image-tags kmm/px
Configure KMM module.
KERNEL_VERSION="6.18.15+deb13-amd64" KERNEL_VERSION_RT="6.18.15+deb13-rt-amd64" PX_FUSE_IMAGE_TAG="6.18.15_deb13-amd64" PX_FUSE_IMAGE_TAG_RT="6.18.15_deb13-rt-amd64" # Module CRD: KMM builds and loads px on nodes labeled px-node=true, # mapping each kernel flavor to a distinct image in registry.local cat >~sysadmin/kmm-px-module.yaml <<EOF apiVersion: kmm.sigs.x-k8s.io/v1beta1 kind: Module metadata: name: kmm-px namespace: kernel-module-management spec: moduleLoader: container: modprobe: moduleName: px kernelMappings: - literal: "${KERNEL_VERSION}" containerImage: "registry.local:9001/kmm/px:${PX_FUSE_IMAGE_TAG}" build: buildArgs: - name: KERNEL_FULL_VERSION value: "${KERNEL_VERSION}" baseImageRegistryTLS: insecure: false insecureSkipTLSVerify: true dockerfileConfigMap: name: kmm-px-cm registryTLS: insecure: false insecureSkipTLSVerify: true - literal: "${KERNEL_VERSION_RT}" containerImage: "registry.local:9001/kmm/px:${PX_FUSE_IMAGE_TAG_RT}" build: buildArgs: - name: KERNEL_FULL_VERSION value: "${KERNEL_VERSION_RT}" baseImageRegistryTLS: insecure: false insecureSkipTLSVerify: true dockerfileConfigMap: name: kmm-px-cm registryTLS: insecure: false insecureSkipTLSVerify: true imagePullPolicy: Always imageRepoSecret: name: "kmm-registry-secret" selector: kubernetes.io/os: linux px-node: "true" EOF # Create the Module — KMM starts the loader pods once workers are labeled kubectl apply -f ~sysadmin/kmm-px-module.yaml
Warning
Image Tag cannot contain “+” symbol, so replace “+” with “_” in the kmm/px image tag. E.g. if the kernel version is 6.18.15+deb13-amd64, then the image tag should be 6.18.15_deb13-amd64.
Label workers and verify px-fuse installation.
# Storage workers, px-node is a custom label for all workers for node in worker-0 worker-1 worker-2; do kubectl label node $node px-node="true" kubectl label node $node portworx.io/node-type=storage done # Application worker kubectl label node worker-3 px-node="true" kubectl label node worker-3 portworx.io/node-type=storageless # verify module loaded kubectl get modules.kmm.sigs.x-k8s.io -n kernel-module-management system registry-image-tags kmm/px for node in worker-0 worker-1 worker-2 worker-3; do echo "=== $node ===" ssh -t $node "sudo lsmod | grep '^px'" done
Install Portworx¶
Prerequisites
Perform the following steps on the active controller (controller-0).
Note
This procedure is validated using Portworx Enterprise 3.6.0 on StarlingX with kernel 6.18.15+deb13-amd64 (Debian 13 Trixie).
Replace the following values with those from your environment:
<portworx-disk-by-path>— Dedicated Portworx disk. E.g.pci-0000:00:1f.2-ata-3.0. Find with:ls -l /dev/disk/by-path/<storage-interface>— Worker interface for data/management traffic. E.g.enp2s2. Find with:system host-if-list <worker><kernel-version>— Worker kernel. E.g.6.18.15+deb13-amd64. Find with:uname -r
Procedure
Prepare the disks.
STORAGE_WORKERS="worker-0 worker-1 worker-2" for node in $STORAGE_WORKERS; do system host-disk-list $node done
Pick, wipe and partition the disk on all storage workers.
Warning
The disk wipe command (
system host-disk-wipe) erases all data on the selected disk.# Wipe disk for node in $STORAGE_WORKERS; do disk=$(system host-disk-list $node --nowrap \ | awk /<portworx-disk-by-path>/'{print $2}') system host-disk-wipe $node $disk --confirm done for node in $STORAGE_WORKERS; do system host-disk-list $node done # 64 GiB metadata partition for node in $STORAGE_WORKERS; do disk=$(system host-disk-list $node --nowrap \ | awk /<portworx-disk-by-path>/'{print $2}') system host-disk-partition-add $node $disk 64 done watch -n 10 "for node in $STORAGE_WORKERS; do system host-disk-partition-list $node; done" # Data partition (remainder) for node in $STORAGE_WORKERS; do disk=$(system host-disk-list $node --nowrap \ | awk /<portworx-disk-by-path>/'{print $2}') system host-disk-partition-add $node $disk 191 done watch -n 10 "for node in $STORAGE_WORKERS; do system host-disk-partition-list $node; done" # Wipe partition signatures for node in $STORAGE_WORKERS; do echo "=== $node ===" ssh -t $node "sudo wipefs -a \ /dev/disk/by-path/<portworx-disk-by-path>-part1 \ /dev/disk/by-path/<portworx-disk-by-path>-part2 \ && lsblk /dev/disk/by-path/<portworx-disk-by-path>" done
Create Helm overrides.
For PX-StoreV1 (Btrfs), confirm
btrfsis available:for node in $STORAGE_WORKERS; do echo "=== $node ===" ssh -t $node "sudo modprobe btrfs; sudo lsmod | grep btrfs || modinfo btrfs" done
cat <<'EOF' > ~sysadmin/portworx-install.yaml clusterName: px-storev1 autopilot: enabled: false dataInterface: <storage-interface> drives: "/dev/disk/by-path/<portworx-disk-by-path>-part2" envVars: "none" installCertManager: false initialStorageNodes: 3 kvdb: internal: true managementInterface: <storage-interface> systemMetadataDevice: "/dev/disk/by-path/<portworx-disk-by-path>-part1" nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: px-node operator: In values: - "true" volumes: - name: docker-root mountPath: /var/lib/docker hostPath: path: /var/lib/docker - name: exec-sock mountPath: /var/run/exec mountPropagation: Bidirectional hostPath: path: /var/run/exec EOF
For PX-StoreV2 (dm-thin), add the
clusterAnnotationsline:cat <<'EOF' > ~sysadmin/portworx-install.yaml clusterAnnotations: "portworx.io/misc-args= -T px-storev2" clusterName: px-storev2 autopilot: enabled: false dataInterface: <storage-interface> drives: "/dev/disk/by-path/<portworx-disk-by-path>-part2" envVars: "none" installCertManager: false initialStorageNodes: 3 kvdb: internal: true managementInterface: <storage-interface> systemMetadataDevice: "/dev/disk/by-path/<portworx-disk-by-path>-part1" nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: px-node operator: In values: - "true" volumes: - name: docker-root mountPath: /var/lib/docker hostPath: path: /var/lib/docker - name: exec-sock mountPath: /var/run/exec mountPropagation: Bidirectional hostPath: path: /var/run/exec EOF
Install the Helm chart.
Online:
# Add the public chart repository helm repo add portworx https://raw.githubusercontent.com/portworx/helm/master/stable helm repo update # Create the namespace kubectl create namespace portworx kubectl label namespace portworx app.starlingx.io/component=application # Install the Portworx cluster helm upgrade --install portworx portworx/portworx -n portworx -f ~sysadmin/portworx-install.yaml
Air-gapped:
# Point to local registry cat <<'EOF' >> ~sysadmin/portworx-install.yaml customRegistryURL: registry.local:9001 registrySecret: default-registry-key EOF
kubectl create namespace portworx kubectl label namespace portworx app.starlingx.io/component=application # Create an image pull secret for the local Docker registry (authentication is required by default). source /etc/platform/openrc kubectl -n portworx create secret docker-registry default-registry-key \ --docker-server=registry.local:9001 \ --docker-username=$OS_USERNAME --docker-password=$OS_PASSWORD # Version manifest — operator stays paused until this exists kubectl -n portworx create configmap px-versions --from-file=versions=versions.yaml # Install from local chart helm upgrade --install portworx ~sysadmin/portworx-9.0.0.tgz -n portworx -f ~sysadmin/portworx-install.yaml
Verify the installation.
kubectl get pods -n portworx -l name=portworx-operator kubectl get pods -n portworx -l name=portworx -o wide -w
kubectl wait -n portworx --for=condition=Ready pods -lname=portworx --timeout=600s
Validate Cluster Status¶
Perform the following steps on the active controller (controller-0).
Check cluster status.
Note
pxctlis shipped by Portworx, not by StarlingX. It is only present at/opt/pwx/bin/pxctlon the Portworx nodes themselves, so it cannot be run from a controller. Run it inside aportworxpod, as shown below;PX_POD=$(kubectl get pods -n portworx -l name=portworx -o jsonpath='{.items[0].metadata.name}') kubectl exec -it -n portworx $PX_POD -- /opt/pwx/bin/pxctl status
sysadmin@controller-0:~$ PX_POD=$(kubectl get pods -n portworx -l name=portworx -o jsonpath='{.items[0].metadata.name}') sysadmin@controller-0:~$ kubectl exec -it -n portworx $PX_POD -- /opt/pwx/bin/pxctl status Status: PX is operational Telemetry: Healthy Metering: Disabled or Unhealthy Node ID: 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d IP: 192.168.206.11 Local Storage Pool: 1 pool POOL IO_PRIORITY RAID_LEVEL USABLE USED STATUS ZONE REGION 0 HIGH raid0 173 GiB 35 MiB Online default default Local Storage Devices: 1 device Device Path Media Type Size Last-Scan 0:0 /dev/sdc2 STORAGE_MEDIUM_SSD 192 GiB 20 Apr 26 21:06 UTC total 192 GiB Cache Devices: * No cache devices Metadata Device: 1 /dev/sdc1 STORAGE_MEDIUM_SSD 64 GiB * Internal kvdb on this node is using this dedicated metadata device to store its data. Cluster Summary Cluster ID: px-storev2 Cluster UUID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee Scheduler: kubernetes Total Nodes: 3 node(s) with storage (3 online), 1 node(s) without storage (1 online) IP ID SchedulerNodeName Auth StorageNode Used Capacity Status StorageStatus Version Kernel OS 192.168.206.13 3c4d5e6f-7a8b-9c0d-1e2f-3a4b5c6d7e8f worker-2 Disabled Yes(PX-StoreV2) 35 MiB 173 GiB Online Up 3.6.0.0-a81cf43 6.18.15+deb13-amd64 Debian GNU/Linux 13 (trixie) 192.168.206.11 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d worker-0 Disabled Yes(PX-StoreV2) 35 MiB 173 GiB Online Up (This node) 3.6.0.0-a81cf43 6.18.15+deb13-amd64 Debian GNU/Linux 13 (trixie) 192.168.206.12 2b3c4d5e-6f7a-8b9c-0d1e-2f3a4b5c6d7e worker-1 Disabled Yes(PX-StoreV2) 35 MiB 173 GiB Online Up 3.6.0.0-a81cf43 6.18.15+deb13-amd64 Debian GNU/Linux 13 (trixie) 192.168.206.14 4d5e6f7a-8b9c-0d1e-2f3a-4b5c6d7e8f9a worker-3 Disabled No(PX-StoreV2) 0 B 0 B Online No Storage 3.6.0.0-a81cf43 6.18.15+deb13-amd64 Debian GNU/Linux 13 (trixie) Warnings: WARNING: Persistent journald logging is not enabled on this node. Global Storage Pool Total Used : 105 MiB Total Capacity : 519 GiB
Check StorageClasses.
kubectl get storageclasses | grep portworx
Validate with a PVC.
# Minimal PVC against the Portworx replicated StorageClass cat <<'EOF' > ~sysadmin/portworx-test-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: px-test-pvc spec: storageClassName: px-csi-replicated accessModes: - ReadWriteOnce resources: requests: storage: 5Gi EOF # Create, watch it reach Bound, then clean up kubectl apply -f ~sysadmin/portworx-test-pvc.yaml watch kubectl get pvc kubectl delete -f ~sysadmin/portworx-test-pvc.yaml kubectl get pvc
The installation is confirmed when status of
px-test-pvcshowsBoundstate.
Online vs Air-gapped¶
The only difference between the two paths is where the artifacts originate. This table summarizes the choices made at each step.
Task |
Online |
Air-gapped |
|---|---|---|
px-fuse module image |
|
bundled source, kmm-builder ( |
Helm chart source |
|
|
Portworx container images |
Pulled from public registries |
Pre-loaded into |
Extra objects |
None |
|
Internet required at deploy time |
Yes |
No |
Troubleshooting¶
For troubleshooting Portworx issues on StarlingX, refer to the following Portworx documentation listed below:
General troubleshooting
Troubleshooting overview — Landing page for all Portworx troubleshooting topics on Kubernetes.
Troubleshoot common errors — Fixes for known error messages including PVC provisioning failures and service type conflicts.
Troubleshooting tips for common problem areas — Covers node-down scenarios, KVDB quorum loss, storage-down/full states, and volume attach/detach issues.
Collect diagnostics — Generate and upload diagnostic bundles using
pxctl service diags.
Quick diagnostic commands
Use the following commands to check Portworx status on your StarlingX cluster. Each
cmd runs inside a portworx pod.
# Check Portworx cluster status
PX_POD=$(kubectl get pods -n portworx -l name=portworx -o jsonpath='{.items[0].metadata.name}')
kubectl exec -it -n portworx $PX_POD -- /opt/pwx/bin/pxctl status
# Check Portworx alerts
kubectl exec -it -n portworx $PX_POD -- /opt/pwx/bin/pxctl alerts show
# Check KVDB cluster health
kubectl exec -it -n portworx $PX_POD -- /opt/pwx/bin/pxctl service kvdb members
# Collect diagnostics on all nodes
kubectl exec -it -n portworx $PX_POD -- /opt/pwx/bin/pxctl service diags -a
# Check Portworx pods status
kubectl get pods -n portworx -l name=portworx -o wide