Install Red Hat Developer hub (RHDH) in a fully air-gapped Minikube environment
Fortune Ndlovu

Fortune Ndlovu @fortune-ndlovu

About: Passionate about Accessibility and Opensource

Joined:
Apr 3, 2025

Install Red Hat Developer hub (RHDH) in a fully air-gapped Minikube environment

Publish Date: Apr 3
5 0

This guide ensures that every component is self-contained, using only the resources available on your air-gapped machine. That is No external access, no file transfers, everything generated locally.

Prerequisites

Ensure the following tools are installed on the air-gapped machine:

docker --version       # Verify Docker is installed
minikube version       # Ensure Minikube is installed
kubectl version --client  # Check if kubectl is installed
helm version           # Helm is required for deployment
Enter fullscreen mode Exit fullscreen mode

Start Minikube with enough resources:

minikube start --driver=docker --cpus=4 --memory=8192 --no-vtx-check
Enter fullscreen mode Exit fullscreen mode

Important: The --no-vtx-check flag ensures Minikube starts without checking virtualization support.

1, Download and Save Container Images (Locally)

Since your machine has no external access, you need to download the images directly using Podman or Docker.

Step 1.1: Pull Required Images (Locally)

Run the following on the air-gapped machine:

podman pull registry.redhat.io/rhdh/rhdh-hub-rhel9:1.4
podman pull registry.redhat.io/rhel9/postgresql-15:latest
Enter fullscreen mode Exit fullscreen mode

Step 1.2: Save Images as .tar Files

Once the images are pulled locally, save them to .tar archives:

podman save -o rhdh-hub.tar registry.redhat.io/rhdh/rhdh-hub-rhel9:1.4
podman save -o postgresql.tar registry.redhat.io/rhel9/postgresql-15:latest
Enter fullscreen mode Exit fullscreen mode

Step 1.3: Load Images Into Minikube

Minikube does not have direct access to the host’s images, so we must load them manually.

  1. Create a directory inside Minikube for storing the images:
minikube ssh -- mkdir -p /home/docker
Enter fullscreen mode Exit fullscreen mode
  1. Copy the .tar files into Minikube’s internal storage:
minikube cp rhdh-hub.tar /home/docker/rhdh-hub.tar
minikube cp postgresql.tar /home/docker/postgresql.tar
Enter fullscreen mode Exit fullscreen mode
  1. Load the images inside Minikube:
minikube ssh -- docker load -i /home/docker/rhdh-hub.tar
minikube ssh -- docker load -i /home/docker/postgresql.tar
Enter fullscreen mode Exit fullscreen mode
  1. Verify the images are available inside Minikube:
minikube ssh -- docker images | grep rhdh-hub
minikube ssh -- docker images | grep postgresql
Enter fullscreen mode Exit fullscreen mode

If necessary, manually tag the images:

minikube ssh -- docker tag registry.redhat.io/rhdh/rhdh-hub-rhel9:1.4 rhdh-hub-rhel9:1.4
minikube ssh -- docker tag registry.redhat.io/rhel9/postgresql-15:latest postgresql-15:latest
Enter fullscreen mode Exit fullscreen mode

5. Create a Namespace

kubectl create namespace rhdh
Enter fullscreen mode Exit fullscreen mode

2, Configure Persistent Storage for PostgreSQL

Since Minikube won’t automatically create storage, manually configure PersistentVolume (PV) and PersistentVolumeClaim (PVC).

Step 2.1: Create Persistent Volume

Create a file called pv.yaml:

cat <<EOF > pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
  name: postgres-pv
  labels:
    type: local
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: "/mnt/data/postgres"
EOF
Enter fullscreen mode Exit fullscreen mode

Apply it:

kubectl apply -f pv.yaml
Enter fullscreen mode Exit fullscreen mode

Create the directory inside Minikube:

minikube ssh -- mkdir -p /mnt/data/postgres
minikube ssh -- sudo chmod 777 /mnt/data/postgres
Enter fullscreen mode Exit fullscreen mode

3, Deploy PostgreSQL

Create a file called postgres.yaml:

cat <<EOF > postgres.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: rhdh
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
---
apiVersion: v1
kind: Service
metadata:
  name: postgres
  namespace: rhdh
spec:
  ports:
    - port: 5432
  selector:
    app: postgres
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgres
  namespace: rhdh
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      securityContext:
        fsGroup: 26  # Fix permission issues
      containers:
        - name: postgres
          image: postgresql-15:latest
          imagePullPolicy: IfNotPresent
          env:
            - name: POSTGRESQL_DATABASE
              value: "rhdh"
            - name: POSTGRESQL_USER
              value: "rhdh"
            - name: POSTGRESQL_PASSWORD
              value: "rhdhpassword"
          ports:
            - containerPort: 5432
          volumeMounts:
            - name: postgres-storage
              mountPath: /var/lib/pgsql/data
      volumes:
        - name: postgres-storage
          persistentVolumeClaim:
            claimName: postgres-pvc
EOF
Enter fullscreen mode Exit fullscreen mode

Apply it:

kubectl apply -f postgres.yaml
Enter fullscreen mode Exit fullscreen mode

4, Deploy RHDH

Create a file called rhdh.yaml:

cat <<EOF > rhdh.yaml
apiVersion: v1
kind: Service
metadata:
  name: rhdh
  namespace: rhdh
spec:
  ports:
    - port: 7007
      targetPort: 7007
      nodePort: 31207
  selector:
    app: rhdh
  type: NodePort
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: rhdh
  namespace: rhdh
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rhdh
  template:
    metadata:
      labels:
        app: rhdh
    spec:
      initContainers:
        - name: wait-for-db
          image: alpine
          command: ['sh', '-c', 'until nc -z postgres.rhdh.svc.cluster.local 5432; do echo waiting for database; sleep 2; done;']
      containers:
        - name: rhdh
          image: rhdh-hub-rhel9:1.4
          imagePullPolicy: IfNotPresent
          command: ["/bin/sh", "-c"]
          args:
            - "mkdir -p /opt/app-root/src/dynamic-plugins-root && exec node packages/backend"
          env:
            - name: DATABASE_URL
              value: "postgresql://rhdh:rhdhpassword@postgres.rhdh.svc.cluster.local:5432/rhdh"
            - name: PGHOST
              value: "postgres.rhdh.svc.cluster.local"
            - name: PGPORT
              value: "5432"
            - name: PGPASSWORD
              value: "rhdhpassword"
            - name: APP_CONFIG_app_baseUrl
              value: "http://192.168.49.2:31207"
            - name: APP_CONFIG_backend_baseUrl
              value: "http://192.168.49.2:31207/api"
            - name: BACKEND_PORT
              value: "7007"
            - name: HOST
              value: "0.0.0.0"
            - name: NODE_ENV
              value: "production"
          ports:
            - containerPort: 7007
          volumeMounts:
            - name: plugins-volume
              mountPath: /opt/app-root/src/dynamic-plugins-root
      volumes:
        - name: plugins-volume
          emptyDir: {}
EOF
Enter fullscreen mode Exit fullscreen mode

Apply it:

kubectl apply -f rhdh.yaml
Enter fullscreen mode Exit fullscreen mode

5, Verify & Access RHDH

Check if the pods are running:

kubectl get pods -n rhdh
Enter fullscreen mode Exit fullscreen mode

Check logs:

kubectl logs -n rhdh -l app=rhdh
Enter fullscreen mode Exit fullscreen mode

Use port forwarding:

kubectl port-forward svc/rhdh 7007:7007 -n rhdh
Enter fullscreen mode Exit fullscreen mode

Access RHDH at:

http://localhost:7007
Enter fullscreen mode Exit fullscreen mode

Image description

Comments 0 total

    Add comment