# Deploying to k3s **When to use:** Deploying rdev-api or claudebox pods to the k3s cluster. ## Prerequisites - Access to orchard9 k3s cluster - kubectl installed - kubeconfig file at `~/.kube/orchard9-k3sf.yaml` ## Quick Deploy ```bash # CRITICAL: Always set kubeconfig first export KUBECONFIG=~/.kube/orchard9-k3sf.yaml # Deploy all resources kubectl apply -k deployments/k8s/base # Verify deployment kubectl get pods -n rdev kubectl get svc -n rdev ``` ## What Gets Deployed The Kustomize base deploys: | Resource | Name | Purpose | |----------|------|---------| | Namespace | `rdev` | Isolation | | Deployment | `rdev-api` | API server | | StatefulSet | `claudebox` | Claude Code pods | | Service | `rdev-api` | Internal service | | ServiceAccount | `rdev-api` | RBAC identity | | Role/RoleBinding | `rdev-api` | Namespace permissions | | ClusterRole/Binding | `rdev-api` | Cluster-wide permissions | | PVC | `workspace-pvc` | Shared workspace volume | | Secret | `claude-credentials` | Claude auth (manual) | ## Deployment Steps ### 1. Set Kubeconfig ```bash export KUBECONFIG=~/.kube/orchard9-k3sf.yaml # Verify connection kubectl cluster-info ``` ### 2. Create Secrets (First Time Only) ```bash # Claude credentials secret kubectl create secret generic claude-credentials \ -n rdev \ --from-file=credentials.json=/path/to/credentials.json # Database credentials (if using external postgres) kubectl create secret generic rdev-db \ -n rdev \ --from-literal=host=postgres.example.com \ --from-literal=password=secret ``` ### 3. Apply Manifests ```bash # Full deployment kubectl apply -k deployments/k8s/base # Or apply individually kubectl apply -f deployments/k8s/base/namespace.yaml kubectl apply -f deployments/k8s/base/rdev-api.yaml kubectl apply -f deployments/k8s/base/claudebox.yaml ``` ### 4. Verify ```bash # Check pods kubectl get pods -n rdev # Expected: rdev-api-xxx Running, claudebox-0 Running # Check logs kubectl logs -n rdev deployment/rdev-api # Test API kubectl port-forward -n rdev svc/rdev-api 8080:8080 curl http://localhost:8080/health ``` ## Updating Deployments ### Rolling Update ```bash # Update image tag in manifests, then: kubectl apply -k deployments/k8s/base # Or force rollout kubectl rollout restart deployment/rdev-api -n rdev ``` ### Watch Rollout ```bash kubectl rollout status deployment/rdev-api -n rdev ``` ## Manifest Structure ``` deployments/k8s/ └── base/ ├── kustomization.yaml # Kustomize config ├── namespace.yaml # rdev namespace ├── rdev-api.yaml # API deployment + RBAC ├── claudebox.yaml # StatefulSet for Claude pods └── pvc-workspace.yaml # Shared workspace PVC ``` ## RBAC Permissions rdev-api requires: - **Namespace-scoped:** Pod exec, ConfigMap read - **Cluster-scoped:** Deployments, Services, Ingresses (for project deployment feature) ### Woodpecker CI Deployer RBAC The `woodpecker-deployer-rbac.yaml` manifest grants Woodpecker CI permission to deploy projects. Without this, deploy steps fail with permission errors. **Why it's needed:** Woodpecker pipeline steps run as the `default` ServiceAccount in the `threesix` namespace, but need to `kubectl set image` on deployments in the `projects` namespace. **Permissions granted:** - `get`, `list`, `patch` on `deployments` (apps API group) This follows least-privilege principles - only the minimum permissions needed for `kubectl set image` to work. ## Troubleshooting ### Pod not starting ```bash kubectl describe pod -n rdev kubectl logs -n rdev --previous ``` ### Permission denied Check RBAC: ```bash kubectl auth can-i exec pods -n rdev --as=system:serviceaccount:rdev:rdev-api ``` ### Image pull error Verify registry access: ```bash kubectl get events -n rdev --sort-by='.lastTimestamp' ``` ### Database connection failed Check secret and network policy: ```bash kubectl get secret rdev-db -n rdev -o yaml kubectl exec -n rdev deployment/rdev-api -- env | grep DB_ ``` ## Automated CI/CD via Woodpecker rdev now uses Woodpecker CI for automated builds and deploys: ``` git push → Gitea → Woodpecker → kaniko → registry.threesix.ai → kubectl deploy ``` ### How It Works 1. Push to `git.threesix.ai/jordan/rdev` triggers Woodpecker 2. Woodpecker runs `.woodpecker.yml`: - Tests with `go test ./...` - Builds images via `woodpeckerci/plugin-kaniko` - Pushes to `registry.threesix.ai/rdev/{api,worker,claudebox}` - Deploys via `kubectl set image` ### Manual Deploy (if needed) ```bash export KUBECONFIG=~/.kube/orchard9-k3sf.yaml kubectl apply -f deployments/k8s/base/rdev-api.yaml kubectl rollout restart -n rdev deployment/rdev-api ``` ### Image Registry Images are stored in Zot at `registry.threesix.ai/rdev/`: - `rdev/api:latest` - API server - `rdev/worker:latest` - Worker pool - `rdev/claudebox:latest` - Claude Code container ## Constraints - **ON-PREM k3s** - Not GKE/EKS, always use local kubeconfig - **Kustomize only** - No ArgoCD or Helm - **Woodpecker CI** - Automated builds on push to main ## Related - [Releasing](./releasing.md) - Build and publish new versions - [Database Guide](./database.md) - [Kubernetes Adapter](../services/kubernetes.md) - [External Health Diagnostics](./external-health-diagnostics.md) - Debug registry/CI/git issues