rdev/.claude/guides/ops/deploying.md
jordan 96c9389c97
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
docs: update build/deploy docs for Woodpecker CI
- deploying.md: Add Woodpecker CI section, update constraints
- releasing.md: Add automated releases via Woodpecker, Zot registry
- RELEASE_CHECKLIST.md: Update build/deploy commands
- CLAUDE.md: Update quick reference for automated deploys

Images now at registry.threesix.ai/rdev/* instead of ghcr.io

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-05 23:54:00 -07:00

5.3 KiB

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

# 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

export KUBECONFIG=~/.kube/orchard9-k3sf.yaml

# Verify connection
kubectl cluster-info

2. Create Secrets (First Time Only)

# 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

# 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

# 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

# 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

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

kubectl describe pod -n rdev <pod-name>
kubectl logs -n rdev <pod-name> --previous

Permission denied

Check RBAC:

kubectl auth can-i exec pods -n rdev --as=system:serviceaccount:rdev:rdev-api

Image pull error

Verify registry access:

kubectl get events -n rdev --sort-by='.lastTimestamp'

Database connection failed

Check secret and network policy:

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)

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