Kubernetes Deployment
This guide covers deploying Code Search on Kubernetes using raw manifests. For a simpler deployment, see Helm Chart.
Prerequisites
Section titled “Prerequisites”- Kubernetes cluster (1.25+)
kubectlconfigured for your cluster- PostgreSQL and Redis available (in-cluster or external)
- Persistent volume provisioner (for repository storage)
Namespace
Section titled “Namespace”Create a dedicated namespace:
apiVersion: v1kind: Namespacemetadata: name: code-searchkubectl apply -f namespace.yamlConfigMap
Section titled “ConfigMap”apiVersion: v1kind: ConfigMapmetadata: name: code-search-config namespace: code-searchdata: config.yaml: | server: host: "0.0.0.0" port: 8080
scheduler: enabled: true poll_interval: "6h" check_interval: "5m"
indexer: concurrency: 2 clone_timeout: "10m"Secrets
Section titled “Secrets”apiVersion: v1kind: Secretmetadata: name: code-search-secrets namespace: code-searchtype: OpaquestringData: database-url: "postgres://user:password@postgres:5432/codesearch?sslmode=disable" redis-url: "redis://redis:6379"PersistentVolumeClaims
Section titled “PersistentVolumeClaims”apiVersion: v1kind: PersistentVolumeClaimmetadata: name: code-search-index namespace: code-searchspec: accessModes: - ReadWriteMany # Required for multiple pods storageClassName: standard # Adjust for your cluster resources: requests: storage: 50Gi---apiVersion: v1kind: PersistentVolumeClaimmetadata: name: code-search-repos namespace: code-searchspec: accessModes: - ReadWriteMany storageClassName: standard resources: requests: storage: 100GiDeployments
Section titled “Deployments”API Server
Section titled “API Server”apiVersion: apps/v1kind: Deploymentmetadata: name: code-search-api namespace: code-searchspec: replicas: 2 selector: matchLabels: app: code-search-api template: metadata: labels: app: code-search-api spec: containers: - name: api image: ghcr.io/techquestsdev/code-search-api:latest ports: - containerPort: 8080 env: - name: CS_DATABASE_URL valueFrom: secretKeyRef: name: code-search-secrets key: database-url - name: CS_REDIS_URL valueFrom: secretKeyRef: name: code-search-secrets key: redis-url - name: CS_ZOEKT_URL value: "http://code-search-zoekt:6070" - name: CS_ZOEKT_INDEX_DIR value: "/data/index" - name: CS_ZOEKT_REPOS_DIR value: "/data/repos" volumeMounts: - name: config mountPath: /app/config.yaml subPath: config.yaml - name: index mountPath: /data/index - name: repos mountPath: /data/repos livenessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 10 periodSeconds: 30 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 10 resources: requests: memory: "512Mi" cpu: "250m" limits: memory: "2Gi" cpu: "2000m" volumes: - name: config configMap: name: code-search-config - name: index persistentVolumeClaim: claimName: code-search-index - name: repos persistentVolumeClaim: claimName: code-search-reposIndexer Worker
Section titled “Indexer Worker”apiVersion: apps/v1kind: Deploymentmetadata: name: code-search-indexer namespace: code-searchspec: replicas: 2 # Scale based on indexing needs selector: matchLabels: app: code-search-indexer template: metadata: labels: app: code-search-indexer spec: containers: - name: indexer image: ghcr.io/techquestsdev/code-search-indexer:latest env: - name: CS_DATABASE_URL valueFrom: secretKeyRef: name: code-search-secrets key: database-url - name: CS_REDIS_URL valueFrom: secretKeyRef: name: code-search-secrets key: redis-url - name: CS_ZOEKT_INDEX_DIR value: "/data/index" - name: CS_ZOEKT_REPOS_DIR value: "/data/repos" volumeMounts: - name: config mountPath: /app/config.yaml subPath: config.yaml - name: index mountPath: /data/index - name: repos mountPath: /data/repos resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "4Gi" cpu: "2000m" volumes: - name: config configMap: name: code-search-config - name: index persistentVolumeClaim: claimName: code-search-index - name: repos persistentVolumeClaim: claimName: code-search-reposZoekt Search Server
Section titled “Zoekt Search Server”apiVersion: apps/v1kind: Deploymentmetadata: name: code-search-zoekt namespace: code-searchspec: replicas: 1 # Single replica for search index consistency selector: matchLabels: app: code-search-zoekt template: metadata: labels: app: code-search-zoekt spec: containers: - name: zoekt image: ghcr.io/techquestsdev/code-search-zoekt:latest ports: - containerPort: 6070 volumeMounts: - name: index mountPath: /data/index - name: repos mountPath: /data/repos livenessProbe: httpGet: path: /health port: 6070 periodSeconds: 30 resources: requests: memory: "1Gi" cpu: "500m" limits: memory: "4Gi" cpu: "2000m" volumes: - name: index persistentVolumeClaim: claimName: code-search-index - name: repos persistentVolumeClaim: claimName: code-search-reposWeb UI
Section titled “Web UI”apiVersion: apps/v1kind: Deploymentmetadata: name: code-search-web namespace: code-searchspec: replicas: 2 selector: matchLabels: app: code-search-web template: metadata: labels: app: code-search-web spec: containers: - name: web image: ghcr.io/techquestsdev/code-search-web:latest ports: - containerPort: 3000 env: - name: API_URL value: "http://code-search-api:8080" resources: requests: memory: "256Mi" cpu: "100m" limits: memory: "512Mi" cpu: "500m"Services
Section titled “Services”apiVersion: v1kind: Servicemetadata: name: code-search-api namespace: code-searchspec: selector: app: code-search-api ports: - port: 8080 targetPort: 8080---apiVersion: v1kind: Servicemetadata: name: code-search-zoekt namespace: code-searchspec: selector: app: code-search-zoekt ports: - port: 6070 targetPort: 6070---apiVersion: v1kind: Servicemetadata: name: code-search-web namespace: code-searchspec: selector: app: code-search-web ports: - port: 3000 targetPort: 3000Ingress
Section titled “Ingress”metadata: name: code-search namespace: code-search annotations:nginx.ingress.kubernetes.io/proxy-body-size: "100m" spec: ingressClassName:nginx tls: - hosts: - code-search.example.com secretName: code-search-tlsrules: - host: code-search.example.com http: paths: - path: /api pathType:Prefix backend: service: name: code-search-api port: number: 8080 - path: /pathType: Prefix backend: service: name: code-search-web port: number: 3000Ingress metadata: name: code-search namespace: code-search annotations:traefik.ingress.kubernetes.io/router.tls: "true" spec: ingressClassName:traefik tls: - hosts: - code-search.example.com secretName: code-search-tlsrules: - host: code-search.example.com http: paths: - path: /api pathType:Prefix backend: service: name: code-search-api port: number: 8080 - path: /pathType: Prefix backend: service: name: code-search-web port: number: 3000Apply All Resources
Section titled “Apply All Resources”kubectl apply -f namespace.yamlkubectl apply -f configmap.yamlkubectl apply -f secrets.yamlkubectl apply -f pvc.yamlkubectl apply -f zoekt-deployment.yamlkubectl apply -f api-deployment.yamlkubectl apply -f indexer-deployment.yamlkubectl apply -f web-deployment.yamlkubectl apply -f services.yamlkubectl apply -f ingress.yamlOr apply all at once:
kubectl apply -f .Verify Deployment
Section titled “Verify Deployment”# Check pod statuskubectl get pods -n code-search
# Check serviceskubectl get svc -n code-search
# View logskubectl logs -f deployment/code-search-api -n code-search
# Check readinesskubectl exec -it deploy/code-search-api -n code-search -- curl http://localhost:8080/readyScaling
Section titled “Scaling”Shared Storage (Multiple Workers)
Section titled “Shared Storage (Multiple Workers)”For parallel indexing with shared storage (requires ReadWriteMany PVC):
# Scale indexers - they share the queue and storagekubectl scale deployment code-search-indexer --replicas=4 -n code-search
# Scale API serverskubectl scale deployment code-search-api --replicas=3 -n code-searchHash-Based Sharding (StatefulSet)
Section titled “Hash-Based Sharding (StatefulSet)”For extreme scale without shared storage, use a StatefulSet with hash-based sharding:
apiVersion: apps/v1kind: StatefulSetmetadata: name: code-search-indexer namespace: code-searchspec: serviceName: code-search-indexer-headless replicas: 3 # Creates indexer-0, indexer-1, indexer-2 podManagementPolicy: Parallel selector: matchLabels: app: code-search-indexer template: metadata: labels: app: code-search-indexer spec: containers: - name: indexer image: ghcr.io/techquestsdev/code-search-indexer:latest ports: - containerPort: 8081 # Federated API port name: api env: - name: CS_DATABASE_URL valueFrom: secretKeyRef: name: code-search-secrets key: database-url - name: CS_REDIS_URL valueFrom: secretKeyRef: name: code-search-secrets key: redis-url - name: CS_SHARDING_ENABLED value: "true" - name: CS_SHARDING_TOTAL_SHARDS value: "3" - name: CS_SHARDING_FEDERATED_ACCESS value: "true" - name: CS_SHARDING_INDEXER_API_PORT value: "8081" volumeMounts: - name: data mountPath: /data - name: zoekt image: ghcr.io/techquestsdev/code-search-zoekt:latest ports: - containerPort: 6070 volumeMounts: - name: data mountPath: /data volumeClaimTemplates: - metadata: name: data spec: accessModes: ["ReadWriteOnce"] # Each shard has its own volume resources: requests: storage: 50Gi---# Headless service for pod discoveryapiVersion: v1kind: Servicemetadata: name: code-search-indexer-headless namespace: code-searchspec: clusterIP: None selector: app: code-search-indexer ports: - port: 8081 name: api - port: 6070 name: zoektThen configure the API to use federated access:
# api-deployment.yaml (add these env vars)env: - name: CS_SHARDING_ENABLED value: "true" - name: CS_SHARDING_TOTAL_SHARDS value: "3" - name: CS_SHARDING_FEDERATED_ACCESS value: "true" - name: CS_SHARDING_INDEXER_SERVICE value: "code-search-indexer-headless"Next Steps
Section titled “Next Steps”- Helm Chart - Simplified Kubernetes deployment
- Configuration - Detailed configuration options
- Sharding Configuration - Horizontal scaling details