Setting up Kubernetes
This guide will help you set up a local Kubernetes cluster (via kind) with HaRP and AppAPI for ExApp development. After completing these steps, you will be able to register a k8s deploy daemon in Nextcloud and deploy a test app.
Prerequisites
Docker must be installed and running
A nextcloud-docker-dev environment running at
https://nextcloud.localThe Nextcloud container is on the
master_defaultDocker network
kubectlinstalled (install guide)kindinstalled (install guide)HaRP repository cloned (e.g.
~/nextcloud/HaRP)
Architecture overview
graph TB
OCC[Browser / OCC / API calls] -->|"Nextcloud (PHP, in Docker container)"| nginx[nginx proxy]
nginx -->|/exapps/| HaRP["HaRP (host network, port 8780)"]
HaRP -->|"k8s API calls (Deployments, Services, PVCs)"| kind["kind cluster (nc-exapps)"]
kind --> ExApp["ExApp pod (e.g. test-deploy)"]
HaRP runs on the host network (
--network=host) and communicates with:The kind k8s API server (via
https://127.0.0.1:<port>)ExApp pods via NodePort services (via the kind node IP)
Nextcloud reaches HaRP via the Docker network gateway IP
nginx proxy forwards
/exapps/requests to HaRP
0. Environment variables (optional)
If you have a custom Kubernetes environment, you can specify its configuration here (the values below are the current defaults).
# .env
HP_K8S_NAMESPACE="nextcloud-exapps"
HP_K8S_STORAGE_CLASS=""
HP_K8S_DEFAULT_STORAGE_SIZE="10Gi"
HP_K8S_BEARER_TOKEN_FILE="/var/run/secrets/kubernetes.io/serviceaccount/token"
source .env
1. Create the kind Cluster
kind create cluster --name nc-exapps
Verify:
kubectl config use-context kind-nc-exapps
kubectl cluster-info
kubectl get nodes -o wide
Note the API server URL (e.g. https://127.0.0.1:37151) and the
node InternalIP (e.g. 172.18.0.2):
# API server
kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'
# Node internal IP
kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}'
2. Create Namespace and RBAC
# Create the ExApps namespace
kubectl create namespace nextcloud-exapps
# Create a ServiceAccount for HaRP
kubectl -n nextcloud-exapps create serviceaccount harp-exapps
# Grant cluster-admin (for development; restrict in production)
kubectl create clusterrolebinding harp-exapps-admin \
--clusterrole=cluster-admin \
--serviceaccount=nextcloud-exapps:harp-exapps
Generate a bearer token (valid for 1 year):
kubectl -n nextcloud-exapps create token harp-exapps --duration=8760h
Note
The redeploy_host_k8s.sh script generates this token
automatically, so you don’t need to copy it manually.
3. Configure the nginx Proxy
The nextcloud-docker-dev nginx proxy must forward /exapps/ to HaRP.
Find the gateway IP of the master_default Docker network (this is
how containers reach the host):
docker network inspect master_default \
--format '{{range .IPAM.Config}}Gateway: {{.Gateway}}{{end}}'
Typically this is your host IP like 192.168.21.1 (may vary on your
machine).
Edit the nginx vhost file:
# Path relative to your nextcloud-docker-dev checkout:
# data/nginx/vhost.d/nextcloud.local_location
Set the content to:
location /exapps/ {
set $harp_addr <GATEWAY_IP>:8780;
proxy_pass http://$harp_addr;
# Forward the true client identity
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
Replace <GATEWAY_IP> with the gateway from above
(e.g. 192.168.21.1).
Reload nginx:
docker exec master-proxy-1 nginx -s reload
4. Build and Deploy HaRP
From the HaRP repository root:
cd path/to/HaRP
bash development/redeploy_host_k8s.sh
The script will:
Auto-detect the k8s API server URL
Generate a fresh bearer token
Connect the kind node to the
master_defaultDocker networkBuild the HaRP Docker image
Start HaRP with k8s backend enabled on host network
Wait for HaRP to become healthy:
docker ps | grep harp
# Should show "(healthy)" after ~15 seconds
Check logs if needed:
docker logs appapi-harp --tail=20
5. Register the k8s Deploy Daemon in Nextcloud
Run this inside the Nextcloud container (replace <NC_CONTAINER> with
your container ID or name, and <GATEWAY_IP> with the gateway from
Step 3):
docker exec <NC_CONTAINER> sudo -E -u www-data php occ app_api:daemon:register \
k8s_local "Kubernetes Local" "kubernetes-install" \
"http" "<GATEWAY_IP>:8780" "http://nextcloud.local" \
--harp \
--harp_shared_key "some_very_secure_password" \
--k8s \
--k8s_expose_type=nodeport \
--set-default
Verify:
docker exec <NC_CONTAINER> sudo -E -u www-data php occ app_api:daemon:list
6. Run Test Deploy
Via OCC
docker exec <NC_CONTAINER> sudo -E -u www-data php occ app_api:app:register test-deploy k8s_local \
--info-xml https://raw.githubusercontent.com/nextcloud/test-deploy/main/appinfo/info.xml \
--test-deploy-mode
Expected output:
ExApp test-deploy deployed successfully.
ExApp test-deploy successfully registered.
Via API (same as what the Admin UI uses)
# Start test deploy
curl -X POST -u admin:admin -H "OCS-APIREQUEST: true" -k \
"https://nextcloud.local/index.php/apps/app_api/daemons/k8s_local/test_deploy"
# Check status
curl -u admin:admin -H "OCS-APIREQUEST: true" -k \
"https://nextcloud.local/index.php/apps/app_api/daemons/k8s_local/test_deploy/status"
# Stop and clean up
curl -X DELETE -u admin:admin -H "OCS-APIREQUEST: true" -k \
"https://nextcloud.local/index.php/apps/app_api/daemons/k8s_local/test_deploy"
Verify k8s Resources
kubectl get deploy,svc,pvc,pods -n nextcloud-exapps -o wide
Unregister
docker exec <NC_CONTAINER> sudo -E -u www-data php occ app_api:app:unregister test-deploy
Cluster Overview
Component |
Value |
|---|---|
Type |
kind (Kubernetes in Docker) |
Cluster Name |
|
Node |
|
ExApps Namespace |
|
ServiceAccount |
|
Monitoring Commands
Cluster Status
kubectl cluster-info
kubectl get nodes -o wide
kubectl get pods -n nextcloud-exapps
kubectl get pods -n nextcloud-exapps -w # watch in real-time
Pod Inspection
kubectl describe pod <pod-name> -n nextcloud-exapps
kubectl logs <pod-name> -n nextcloud-exapps
kubectl logs -f <pod-name> -n nextcloud-exapps # follow logs
kubectl logs --previous <pod-name> -n nextcloud-exapps # after restart
Resources
kubectl get svc,deploy,pvc -n nextcloud-exapps
kubectl get all -n nextcloud-exapps
HaRP Logs
docker logs appapi-harp --tail=50
docker logs -f appapi-harp # follow
Troubleshooting
HaRP can’t reach k8s API
# Check if kind container is running
docker ps | grep kind
# Verify API server is reachable from host
curl -k https://127.0.0.1:37151/version
Nextcloud can’t reach HaRP
# From inside the Nextcloud container, test connectivity to HaRP:
docker exec <NC_CONTAINER> curl -s http://<GATEWAY_IP>:8780/
# Should return "404 Not Found" (HaRP is responding)
# If connection refused: check HaRP is running and gateway IP is correct
Heartbeat fails after successful deploy
Check HaRP logs for routing errors:
docker logs appapi-harp --tail=20
HaRP lazily resolves the k8s Service upstream on first request after a restart, so restarting HaRP does not require re-deploying ExApps. If heartbeat still fails, verify the k8s Service exists and is reachable:
kubectl get svc -n nextcloud-exapps
Pods stuck in Pending
kubectl describe pod <pod-name> -n nextcloud-exapps
# Check Events section for scheduling or image pull issues
Image pull errors
The kind cluster needs to be able to pull images. For public images
(like ghcr.io/nextcloud/test-deploy:release) this should work out of
the box.
Token expired
Regenerate by rerunning the redeploy script:
cd ~/nextcloud/HaRP
bash development/redeploy_host_k8s.sh
Clean up all ExApp resources
kubectl delete deploy,svc,pvc -n nextcloud-exapps --all
Reset everything
Remove the deploy daemon config:
docker exec <NC_CONTAINER> sudo -E -u www-data php occ app_api:daemon:unregister k8s_local
Note
Optionally, add the --rm-data option to also delete the associated PVC (Persistent Volume Claim).
Delete the kind cluster:
kind delete cluster --name nc-exapps
Remove the HaRP container:
docker rm -f appapi-harp
Then start again from Step 1.