this repo has no description
1# BSPDS Production Kubernetes Deployment
2
3> **Warning**: These instructions are untested and theoretical, written from the top of Lewis' head. They may contain errors or omissions. This warning will be removed once the guide has been verified.
4
5This guide covers deploying BSPDS on a production multi-node Kubernetes cluster with high availability, auto-scaling, and proper secrets management.
6
7## Architecture Overview
8
9```
10 ┌─────────────────────────────────────────────────┐
11 │ Kubernetes Cluster │
12 │ │
13 Internet ──────►│ Ingress Controller (nginx/traefik) │
14 │ │ │
15 │ ▼ │
16 │ ┌─────────────┐ │
17 │ │ Service │◄── HPA (2-10 replicas) │
18 │ └──────┬──────┘ │
19 │ │ │
20 │ ┌────┴────┐ │
21 │ ▼ ▼ │
22 │ ┌─────┐ ┌─────┐ │
23 │ │BSPDS│ │BSPDS│ ... (pods) │
24 │ └──┬──┘ └──┬──┘ │
25 │ │ │ │
26 │ ▼ ▼ │
27 │ ┌──────────────────────────────────────┐ │
28 │ │ PostgreSQL │ MinIO │ Valkey │ │
29 │ │ (HA/Operator)│ (StatefulSet) │ (Sentinel) │
30 │ └──────────────────────────────────────┘ │
31 └─────────────────────────────────────────────────┘
32```
33
34## Prerequisites
35
36- Kubernetes cluster (1.30+) with at least 3 nodes (1.34 is current stable)
37- `kubectl` configured to access your cluster
38- `helm` 3.x installed
39- Storage class that supports `ReadWriteOnce` (for databases)
40- Ingress controller installed (nginx-ingress or traefik)
41- cert-manager installed for TLS certificates
42
43### Quick Prerequisites Setup
44
45If you need to install prerequisites:
46
47```bash
48# Install nginx-ingress (chart v4.14.1 - December 2025)
49helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
50helm repo update
51helm install ingress-nginx ingress-nginx/ingress-nginx \
52 --namespace ingress-nginx --create-namespace \
53 --version 4.14.1
54
55# Install cert-manager (v1.19.2 - December 2025)
56helm repo add jetstack https://charts.jetstack.io
57helm repo update
58helm install cert-manager jetstack/cert-manager \
59 --namespace cert-manager --create-namespace \
60 --version v1.19.2 \
61 --set installCRDs=true
62```
63
64---
65
66## 1. Create Namespace
67
68```bash
69kubectl create namespace bspds
70kubectl config set-context --current --namespace=bspds
71```
72
73## 2. Create Secrets
74
75Generate secure passwords and secrets:
76
77```bash
78# Generate secrets
79DB_PASSWORD=$(openssl rand -base64 32)
80MINIO_PASSWORD=$(openssl rand -base64 32)
81JWT_SECRET=$(openssl rand -base64 48)
82DPOP_SECRET=$(openssl rand -base64 48)
83MASTER_KEY=$(openssl rand -base64 48)
84
85# Create Kubernetes secrets
86kubectl create secret generic bspds-db-credentials \
87 --from-literal=username=bspds \
88 --from-literal=password="$DB_PASSWORD"
89
90kubectl create secret generic bspds-minio-credentials \
91 --from-literal=root-user=minioadmin \
92 --from-literal=root-password="$MINIO_PASSWORD"
93
94kubectl create secret generic bspds-secrets \
95 --from-literal=jwt-secret="$JWT_SECRET" \
96 --from-literal=dpop-secret="$DPOP_SECRET" \
97 --from-literal=master-key="$MASTER_KEY"
98
99# Save secrets locally (KEEP SECURE!)
100echo "DB_PASSWORD=$DB_PASSWORD" > secrets.txt
101echo "MINIO_PASSWORD=$MINIO_PASSWORD" >> secrets.txt
102echo "JWT_SECRET=$JWT_SECRET" >> secrets.txt
103echo "DPOP_SECRET=$DPOP_SECRET" >> secrets.txt
104echo "MASTER_KEY=$MASTER_KEY" >> secrets.txt
105chmod 600 secrets.txt
106```
107
108## 3. Deploy PostgreSQL
109
110### Option A: CloudNativePG Operator (Recommended for HA)
111
112```bash
113# Install CloudNativePG operator (v1.28.0 - December 2025)
114kubectl apply --server-side -f \
115 https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.28/releases/cnpg-1.28.0.yaml
116
117# Wait for operator
118kubectl wait --for=condition=available --timeout=120s \
119 deployment/cnpg-controller-manager -n cnpg-system
120```
121
122```bash
123cat <<EOF | kubectl apply -f -
124apiVersion: postgresql.cnpg.io/v1
125kind: Cluster
126metadata:
127 name: bspds-db
128 namespace: bspds
129spec:
130 instances: 3
131
132 postgresql:
133 parameters:
134 max_connections: "200"
135 shared_buffers: "256MB"
136
137 bootstrap:
138 initdb:
139 database: pds
140 owner: bspds
141 secret:
142 name: bspds-db-credentials
143
144 storage:
145 size: 20Gi
146 storageClass: standard # adjust for your cluster
147
148 resources:
149 requests:
150 memory: "512Mi"
151 cpu: "250m"
152 limits:
153 memory: "1Gi"
154 cpu: "1000m"
155
156 affinity:
157 podAntiAffinityType: required
158EOF
159```
160
161### Option B: Simple StatefulSet (Single Instance)
162
163```bash
164cat <<EOF | kubectl apply -f -
165apiVersion: v1
166kind: PersistentVolumeClaim
167metadata:
168 name: bspds-db-pvc
169 namespace: bspds
170spec:
171 accessModes:
172 - ReadWriteOnce
173 resources:
174 requests:
175 storage: 20Gi
176---
177apiVersion: apps/v1
178kind: StatefulSet
179metadata:
180 name: bspds-db
181 namespace: bspds
182spec:
183 serviceName: bspds-db
184 replicas: 1
185 selector:
186 matchLabels:
187 app: bspds-db
188 template:
189 metadata:
190 labels:
191 app: bspds-db
192 spec:
193 containers:
194 - name: postgres
195 image: postgres:18-alpine
196 ports:
197 - containerPort: 5432
198 env:
199 - name: POSTGRES_DB
200 value: pds
201 - name: POSTGRES_USER
202 valueFrom:
203 secretKeyRef:
204 name: bspds-db-credentials
205 key: username
206 - name: POSTGRES_PASSWORD
207 valueFrom:
208 secretKeyRef:
209 name: bspds-db-credentials
210 key: password
211 - name: PGDATA
212 value: /var/lib/postgresql/data/pgdata
213 volumeMounts:
214 - name: data
215 mountPath: /var/lib/postgresql/data
216 resources:
217 requests:
218 memory: "256Mi"
219 cpu: "100m"
220 limits:
221 memory: "1Gi"
222 cpu: "500m"
223 livenessProbe:
224 exec:
225 command: ["pg_isready", "-U", "bspds", "-d", "pds"]
226 initialDelaySeconds: 30
227 periodSeconds: 10
228 readinessProbe:
229 exec:
230 command: ["pg_isready", "-U", "bspds", "-d", "pds"]
231 initialDelaySeconds: 5
232 periodSeconds: 5
233 volumes:
234 - name: data
235 persistentVolumeClaim:
236 claimName: bspds-db-pvc
237---
238apiVersion: v1
239kind: Service
240metadata:
241 name: bspds-db-rw
242 namespace: bspds
243spec:
244 selector:
245 app: bspds-db
246 ports:
247 - port: 5432
248 targetPort: 5432
249EOF
250```
251
252## 4. Deploy MinIO
253
254```bash
255cat <<EOF | kubectl apply -f -
256apiVersion: v1
257kind: PersistentVolumeClaim
258metadata:
259 name: bspds-minio-pvc
260 namespace: bspds
261spec:
262 accessModes:
263 - ReadWriteOnce
264 resources:
265 requests:
266 storage: 50Gi
267---
268apiVersion: apps/v1
269kind: StatefulSet
270metadata:
271 name: bspds-minio
272 namespace: bspds
273spec:
274 serviceName: bspds-minio
275 replicas: 1
276 selector:
277 matchLabels:
278 app: bspds-minio
279 template:
280 metadata:
281 labels:
282 app: bspds-minio
283 spec:
284 containers:
285 - name: minio
286 image: minio/minio:RELEASE.2025-10-15T17-29-55Z
287 args:
288 - server
289 - /data
290 - --console-address
291 - ":9001"
292 ports:
293 - containerPort: 9000
294 name: api
295 - containerPort: 9001
296 name: console
297 env:
298 - name: MINIO_ROOT_USER
299 valueFrom:
300 secretKeyRef:
301 name: bspds-minio-credentials
302 key: root-user
303 - name: MINIO_ROOT_PASSWORD
304 valueFrom:
305 secretKeyRef:
306 name: bspds-minio-credentials
307 key: root-password
308 volumeMounts:
309 - name: data
310 mountPath: /data
311 resources:
312 requests:
313 memory: "256Mi"
314 cpu: "100m"
315 limits:
316 memory: "512Mi"
317 cpu: "500m"
318 livenessProbe:
319 httpGet:
320 path: /minio/health/live
321 port: 9000
322 initialDelaySeconds: 30
323 periodSeconds: 10
324 readinessProbe:
325 httpGet:
326 path: /minio/health/ready
327 port: 9000
328 initialDelaySeconds: 10
329 periodSeconds: 5
330 volumes:
331 - name: data
332 persistentVolumeClaim:
333 claimName: bspds-minio-pvc
334---
335apiVersion: v1
336kind: Service
337metadata:
338 name: bspds-minio
339 namespace: bspds
340spec:
341 selector:
342 app: bspds-minio
343 ports:
344 - port: 9000
345 targetPort: 9000
346 name: api
347 - port: 9001
348 targetPort: 9001
349 name: console
350EOF
351```
352
353### Initialize MinIO Bucket
354
355```bash
356kubectl run minio-init --rm -it --restart=Never \
357 --image=minio/mc:RELEASE.2025-07-16T15-35-03Z \
358 --env="MINIO_ROOT_USER=minioadmin" \
359 --env="MINIO_ROOT_PASSWORD=$(kubectl get secret bspds-minio-credentials -o jsonpath='{.data.root-password}' | base64 -d)" \
360 --command -- sh -c "
361 mc alias set local http://bspds-minio:9000 \$MINIO_ROOT_USER \$MINIO_ROOT_PASSWORD &&
362 mc mb --ignore-existing local/pds-blobs
363 "
364```
365
366## 5. Deploy Valkey
367
368```bash
369cat <<EOF | kubectl apply -f -
370apiVersion: v1
371kind: PersistentVolumeClaim
372metadata:
373 name: bspds-valkey-pvc
374 namespace: bspds
375spec:
376 accessModes:
377 - ReadWriteOnce
378 resources:
379 requests:
380 storage: 5Gi
381---
382apiVersion: apps/v1
383kind: StatefulSet
384metadata:
385 name: bspds-valkey
386 namespace: bspds
387spec:
388 serviceName: bspds-valkey
389 replicas: 1
390 selector:
391 matchLabels:
392 app: bspds-valkey
393 template:
394 metadata:
395 labels:
396 app: bspds-valkey
397 spec:
398 containers:
399 - name: valkey
400 image: valkey/valkey:9-alpine
401 args:
402 - valkey-server
403 - --appendonly
404 - "yes"
405 - --maxmemory
406 - "256mb"
407 - --maxmemory-policy
408 - allkeys-lru
409 ports:
410 - containerPort: 6379
411 volumeMounts:
412 - name: data
413 mountPath: /data
414 resources:
415 requests:
416 memory: "128Mi"
417 cpu: "50m"
418 limits:
419 memory: "300Mi"
420 cpu: "200m"
421 livenessProbe:
422 exec:
423 command: ["valkey-cli", "ping"]
424 initialDelaySeconds: 10
425 periodSeconds: 5
426 readinessProbe:
427 exec:
428 command: ["valkey-cli", "ping"]
429 initialDelaySeconds: 5
430 periodSeconds: 3
431 volumes:
432 - name: data
433 persistentVolumeClaim:
434 claimName: bspds-valkey-pvc
435---
436apiVersion: v1
437kind: Service
438metadata:
439 name: bspds-valkey
440 namespace: bspds
441spec:
442 selector:
443 app: bspds-valkey
444 ports:
445 - port: 6379
446 targetPort: 6379
447EOF
448```
449
450## 6. Build and Push BSPDS Image
451
452```bash
453# Build image
454cd /path/to/bspds
455docker build -t your-registry.com/bspds:latest .
456docker push your-registry.com/bspds:latest
457```
458
459If using a private registry, create an image pull secret:
460
461```bash
462kubectl create secret docker-registry regcred \
463 --docker-server=your-registry.com \
464 --docker-username=your-username \
465 --docker-password=your-password \
466 --docker-email=your-email
467```
468
469## 7. Run Database Migrations
470
471BSPDS runs migrations automatically on startup. However, if you want to run migrations separately (recommended for zero-downtime deployments), you can use a Job:
472
473```bash
474cat <<'EOF' | kubectl apply -f -
475apiVersion: batch/v1
476kind: Job
477metadata:
478 name: bspds-migrate
479 namespace: bspds
480spec:
481 ttlSecondsAfterFinished: 300
482 template:
483 spec:
484 restartPolicy: Never
485 containers:
486 - name: migrate
487 image: your-registry.com/bspds:latest
488 command: ["/usr/local/bin/bspds"]
489 args: ["--migrate-only"] # Add this flag to your app, or remove this Job
490 env:
491 - name: DB_PASSWORD
492 valueFrom:
493 secretKeyRef:
494 name: bspds-db-credentials
495 key: password
496 - name: DATABASE_URL
497 value: "postgres://bspds:$(DB_PASSWORD)@bspds-db-rw:5432/pds"
498EOF
499
500kubectl wait --for=condition=complete --timeout=120s job/bspds-migrate
501```
502
503> **Note**: If your BSPDS image doesn't have a `--migrate-only` flag, you can skip this step. The app will run migrations on first startup. Alternatively, build a separate migration image with `sqlx-cli` installed.
504
505## 8. Deploy BSPDS Application
506
507```bash
508cat <<EOF | kubectl apply -f -
509apiVersion: v1
510kind: ConfigMap
511metadata:
512 name: bspds-config
513 namespace: bspds
514data:
515 PDS_HOSTNAME: "pds.example.com"
516 SERVER_HOST: "0.0.0.0"
517 SERVER_PORT: "3000"
518 S3_ENDPOINT: "http://bspds-minio:9000"
519 AWS_REGION: "us-east-1"
520 S3_BUCKET: "pds-blobs"
521 VALKEY_URL: "redis://bspds-valkey:6379"
522 APPVIEW_URL: "https://api.bsky.app"
523 CRAWLERS: "https://bsky.network"
524 FRONTEND_DIR: "/app/frontend/dist"
525---
526apiVersion: apps/v1
527kind: Deployment
528metadata:
529 name: bspds
530 namespace: bspds
531spec:
532 replicas: 2
533 selector:
534 matchLabels:
535 app: bspds
536 template:
537 metadata:
538 labels:
539 app: bspds
540 spec:
541 imagePullSecrets:
542 - name: regcred # Remove if using public registry
543 affinity:
544 podAntiAffinity:
545 preferredDuringSchedulingIgnoredDuringExecution:
546 - weight: 100
547 podAffinityTerm:
548 labelSelector:
549 matchLabels:
550 app: bspds
551 topologyKey: kubernetes.io/hostname
552 containers:
553 - name: bspds
554 image: your-registry.com/bspds:latest
555 ports:
556 - containerPort: 3000
557 name: http
558 envFrom:
559 - configMapRef:
560 name: bspds-config
561 env:
562 - name: DB_PASSWORD
563 valueFrom:
564 secretKeyRef:
565 name: bspds-db-credentials
566 key: password
567 - name: DATABASE_URL
568 value: "postgres://bspds:$(DB_PASSWORD)@bspds-db-rw:5432/pds"
569 - name: AWS_ACCESS_KEY_ID
570 valueFrom:
571 secretKeyRef:
572 name: bspds-minio-credentials
573 key: root-user
574 - name: AWS_SECRET_ACCESS_KEY
575 valueFrom:
576 secretKeyRef:
577 name: bspds-minio-credentials
578 key: root-password
579 - name: JWT_SECRET
580 valueFrom:
581 secretKeyRef:
582 name: bspds-secrets
583 key: jwt-secret
584 - name: DPOP_SECRET
585 valueFrom:
586 secretKeyRef:
587 name: bspds-secrets
588 key: dpop-secret
589 - name: MASTER_KEY
590 valueFrom:
591 secretKeyRef:
592 name: bspds-secrets
593 key: master-key
594 resources:
595 requests:
596 memory: "256Mi"
597 cpu: "100m"
598 limits:
599 memory: "1Gi"
600 cpu: "1000m"
601 livenessProbe:
602 httpGet:
603 path: /xrpc/_health
604 port: 3000
605 initialDelaySeconds: 30
606 periodSeconds: 10
607 failureThreshold: 3
608 readinessProbe:
609 httpGet:
610 path: /xrpc/_health
611 port: 3000
612 initialDelaySeconds: 5
613 periodSeconds: 5
614 failureThreshold: 3
615 securityContext:
616 runAsNonRoot: true
617 runAsUser: 1000
618 allowPrivilegeEscalation: false
619---
620apiVersion: v1
621kind: Service
622metadata:
623 name: bspds
624 namespace: bspds
625spec:
626 selector:
627 app: bspds
628 ports:
629 - port: 80
630 targetPort: 3000
631 name: http
632EOF
633```
634
635## 9. Configure Horizontal Pod Autoscaler
636
637```bash
638cat <<EOF | kubectl apply -f -
639apiVersion: autoscaling/v2
640kind: HorizontalPodAutoscaler
641metadata:
642 name: bspds
643 namespace: bspds
644spec:
645 scaleTargetRef:
646 apiVersion: apps/v1
647 kind: Deployment
648 name: bspds
649 minReplicas: 2
650 maxReplicas: 10
651 metrics:
652 - type: Resource
653 resource:
654 name: cpu
655 target:
656 type: Utilization
657 averageUtilization: 70
658 - type: Resource
659 resource:
660 name: memory
661 target:
662 type: Utilization
663 averageUtilization: 80
664 behavior:
665 scaleDown:
666 stabilizationWindowSeconds: 300
667 policies:
668 - type: Pods
669 value: 1
670 periodSeconds: 60
671 scaleUp:
672 stabilizationWindowSeconds: 0
673 policies:
674 - type: Percent
675 value: 100
676 periodSeconds: 15
677 - type: Pods
678 value: 4
679 periodSeconds: 15
680 selectPolicy: Max
681EOF
682```
683
684## 10. Configure Pod Disruption Budget
685
686```bash
687cat <<EOF | kubectl apply -f -
688apiVersion: policy/v1
689kind: PodDisruptionBudget
690metadata:
691 name: bspds
692 namespace: bspds
693spec:
694 minAvailable: 1
695 selector:
696 matchLabels:
697 app: bspds
698EOF
699```
700
701## 11. Configure TLS with cert-manager
702
703```bash
704cat <<EOF | kubectl apply -f -
705apiVersion: cert-manager.io/v1
706kind: ClusterIssuer
707metadata:
708 name: letsencrypt-prod
709spec:
710 acme:
711 server: https://acme-v02.api.letsencrypt.org/directory
712 email: your-email@example.com
713 privateKeySecretRef:
714 name: letsencrypt-prod
715 solvers:
716 - http01:
717 ingress:
718 class: nginx
719EOF
720```
721
722## 12. Configure Ingress
723
724```bash
725cat <<EOF | kubectl apply -f -
726apiVersion: networking.k8s.io/v1
727kind: Ingress
728metadata:
729 name: bspds
730 namespace: bspds
731 annotations:
732 cert-manager.io/cluster-issuer: letsencrypt-prod
733 nginx.ingress.kubernetes.io/proxy-read-timeout: "86400"
734 nginx.ingress.kubernetes.io/proxy-send-timeout: "86400"
735 nginx.ingress.kubernetes.io/proxy-body-size: "100m"
736 nginx.ingress.kubernetes.io/proxy-buffering: "off"
737 nginx.ingress.kubernetes.io/websocket-services: "bspds"
738spec:
739 ingressClassName: nginx
740 tls:
741 - hosts:
742 - pds.example.com
743 secretName: bspds-tls
744 rules:
745 - host: pds.example.com
746 http:
747 paths:
748 - path: /
749 pathType: Prefix
750 backend:
751 service:
752 name: bspds
753 port:
754 number: 80
755EOF
756```
757
758## 13. Configure Network Policies (Optional but Recommended)
759
760```bash
761cat <<EOF | kubectl apply -f -
762apiVersion: networking.k8s.io/v1
763kind: NetworkPolicy
764metadata:
765 name: bspds-network-policy
766 namespace: bspds
767spec:
768 podSelector:
769 matchLabels:
770 app: bspds
771 policyTypes:
772 - Ingress
773 - Egress
774 ingress:
775 - from:
776 - namespaceSelector:
777 matchLabels:
778 kubernetes.io/metadata.name: ingress-nginx
779 ports:
780 - protocol: TCP
781 port: 3000
782 egress:
783 - to:
784 - podSelector:
785 matchLabels:
786 app: bspds-db
787 ports:
788 - protocol: TCP
789 port: 5432
790 - to:
791 - podSelector:
792 matchLabels:
793 app: bspds-minio
794 ports:
795 - protocol: TCP
796 port: 9000
797 - to:
798 - podSelector:
799 matchLabels:
800 app: bspds-valkey
801 ports:
802 - protocol: TCP
803 port: 6379
804 - to: # Allow DNS
805 - namespaceSelector: {}
806 podSelector:
807 matchLabels:
808 k8s-app: kube-dns
809 ports:
810 - protocol: UDP
811 port: 53
812 - to: # Allow external HTTPS (for federation)
813 - ipBlock:
814 cidr: 0.0.0.0/0
815 ports:
816 - protocol: TCP
817 port: 443
818EOF
819```
820
821## 14. Deploy Prometheus Monitoring (Optional)
822
823```bash
824cat <<EOF | kubectl apply -f -
825apiVersion: monitoring.coreos.com/v1
826kind: ServiceMonitor
827metadata:
828 name: bspds
829 namespace: bspds
830 labels:
831 release: prometheus
832spec:
833 selector:
834 matchLabels:
835 app: bspds
836 endpoints:
837 - port: http
838 path: /metrics
839 interval: 30s
840EOF
841```
842
843---
844
845## Verification
846
847```bash
848# Check all pods are running
849kubectl get pods -n bspds
850
851# Check services
852kubectl get svc -n bspds
853
854# Check ingress
855kubectl get ingress -n bspds
856
857# Check certificate
858kubectl get certificate -n bspds
859
860# Test health endpoint
861curl -s https://pds.example.com/xrpc/_health | jq
862
863# Test DID endpoint
864curl -s https://pds.example.com/.well-known/atproto-did
865```
866
867---
868
869## Maintenance
870
871### View Logs
872
873```bash
874# All BSPDS pods
875kubectl logs -l app=bspds -n bspds -f
876
877# Specific pod
878kubectl logs -f deployment/bspds -n bspds
879```
880
881### Scale Manually
882
883```bash
884kubectl scale deployment bspds --replicas=5 -n bspds
885```
886
887### Update BSPDS
888
889```bash
890# Build and push new image
891docker build -t your-registry.com/bspds:v1.2.3 .
892docker push your-registry.com/bspds:v1.2.3
893
894# Update deployment
895kubectl set image deployment/bspds bspds=your-registry.com/bspds:v1.2.3 -n bspds
896
897# Watch rollout
898kubectl rollout status deployment/bspds -n bspds
899```
900
901### Backup Database
902
903```bash
904# For CloudNativePG
905kubectl cnpg backup bspds-db -n bspds
906
907# For StatefulSet
908kubectl exec -it bspds-db-0 -n bspds -- pg_dump -U bspds pds > backup-$(date +%Y%m%d).sql
909```
910
911### Run Migrations
912
913If you have a migration Job defined, you can re-run it:
914
915```bash
916# Delete old job first (if exists)
917kubectl delete job bspds-migrate -n bspds --ignore-not-found
918
919# Re-apply the migration job from step 7
920# Or simply restart the deployment - BSPDS runs migrations on startup
921kubectl rollout restart deployment/bspds -n bspds
922```
923
924---
925
926## Troubleshooting
927
928### Pod Won't Start
929
930```bash
931kubectl describe pod -l app=bspds -n bspds
932kubectl logs -l app=bspds -n bspds --previous
933```
934
935### Database Connection Issues
936
937```bash
938# Test connectivity from a debug pod
939kubectl run debug --rm -it --restart=Never --image=postgres:18-alpine -- \
940 psql "postgres://bspds:PASSWORD@bspds-db-rw:5432/pds" -c "SELECT 1"
941```
942
943### Certificate Issues
944
945```bash
946kubectl describe certificate bspds-tls -n bspds
947kubectl describe certificaterequest -n bspds
948kubectl logs -l app.kubernetes.io/name=cert-manager -n cert-manager
949```
950
951### View Resource Usage
952
953```bash
954kubectl top pods -n bspds
955kubectl top nodes
956```