PerfLoad

Installation Guide

Detailed setup instructions for running PerfLoad locally with Docker, and deploying it to a Kubernetes cluster.

Docker

The fastest way to run PerfLoad. A single pre-built image, one port, one volume.

Prerequisites

1. Run the container

docker run -d --name perfload \
  -p 3000:3000 \
  -v perfload-runs:/app/runs \
  perfload/perfload-runner:latest

To pin a specific release instead, replace latest with a version tag, e.g. 1.6.0.

2. Verify it's up

curl http://localhost:3000/health

Expected response: {"status":"ok"}

InterfaceURL
Dashboardhttp://localhost:3000/
Workbenchhttp://localhost:3000/load-tester.html
k6 live dashboardhttp://localhost:3000/runs/<id>/dashboard/live/

3. Update to a new version

docker pull perfload/perfload-runner:latest
docker stop perfload && docker rm perfload
docker run -d --name perfload \
  -p 3000:3000 \
  -v perfload-runs:/app/runs \
  perfload/perfload-runner:latest

Run history in the perfload-runs volume survives this — it's independent of the container.

4. Managing the container

# Stop, keep the volume
docker stop perfload

# Start again
docker start perfload

# View logs
docker logs perfload

# Remove the container (volume persists)
docker rm perfload

# Only if you want run history gone too
docker volume rm perfload-runs

Kubernetes

Deployed via kustomize from cd-deploy-configs/apps/perfload-runner/. A docker run gives the container the whole host's CPU, memory, and a writable layer that survives until removed — none of that is true by default in a k8s pod, which shares the node and starts from a clean, ephemeral filesystem on every reschedule. The manifests and checklist below cover what has to be made explicit to get equivalent behavior in the cluster.

Prerequisites

Manifests

cd-deploy-configs/apps/perfload-runner/ holds the following five files. If the directory doesn't exist yet for a first-time deploy, create them.

deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: perfload-runner
spec:
  replicas: 1
  selector:
    matchLabels:
      app: perfload-runner
  template:
    metadata:
      labels:
        app: perfload-runner
    spec:
      containers:
        - name: perfload-runner
          image: perfload/perfload-runner:latest
          ports:
            - containerPort: 3000
          resources:
            requests:
              cpu: 500m
              memory: 768Mi
            limits:
              cpu: "1"
              memory: 1Gi
          livenessProbe:
            httpGet:
              path: /health
              port: 3000
          readinessProbe:
            httpGet:
              path: /health
              port: 3000
          envFrom:
            - configMapRef:
                name: app-configmap
          volumeMounts:
            - name: runs
              mountPath: /app/runs
            - name: dshm
              mountPath: /dev/shm
      volumes:
        - name: runs
          persistentVolumeClaim:
            claimName: perfload-runner-runs
        - name: dshm
          emptyDir:
            medium: Memory
            sizeLimit: 512Mi
pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: perfload-runner-runs
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
service.yaml

Exposes the pod inside the cluster. How it's reached from outside (Ingress, LoadBalancer, etc.) is cluster-specific and not covered here.

apiVersion: v1
kind: Service
metadata:
  name: perfload-runner
spec:
  selector:
    app: perfload-runner
  ports:
    - port: 3000
      targetPort: 3000
configmap.yaml

The deployment's envFrom requires this to exist even if empty, since a missing ConfigMap reference blocks the pod from starting. Every key below has a safe in-code default (see "Config differences" below) — only add one if you're deliberately overriding it.

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-configmap
data: {}
# To override a default, replace `data: {}` above with e.g.:
# data:
#   PORT: "3000"
#   K6_DASHBOARD_PORT_START: "5665"
#   K6_DASHBOARD_PORT_COUNT: "20"
kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
  - deployment.yaml
  - pvc.yaml
  - service.yaml
  - configmap.yaml

Deploy

kubectl apply -k cd-deploy-configs/apps/perfload-runner/

Verify

kubectl rollout status deployment/perfload-runner
kubectl get pods -l app=perfload-runner

# Confirm it actually serves traffic, not just that the pod is Ready
kubectl port-forward svc/perfload-runner 3000:3000
curl http://localhost:3000/health

With the port-forward still open, also run one real test end-to-end (via the Workbench at http://localhost:3000/load-tester.html, or POST /runs directly) — see "Health checks" below for why a passing probe alone isn't enough to call the rollout good.

After deploying: differences from Docker to check

A k8s pod shares the node with other pods and starts from a clean, ephemeral filesystem every time it's rescheduled, unlike docker run above. Work through this checklist once the manifests are applied.

1. Resource requests/limits

2. Ephemeral vs. persistent storage

3. /dev/shm / shared memory

4. Health checks

5. Config differences (local docker run vs. cluster)