--- apiVersion: apps/v1 kind: Deployment metadata: name: bookwyrm-web namespace: bookwyrm-application labels: app: bookwyrm component: web spec: replicas: 2 selector: matchLabels: app: bookwyrm component: web template: metadata: labels: app: bookwyrm component: web spec: securityContext: runAsNonRoot: true runAsUser: 1000 runAsGroup: 1000 fsGroup: 1000 seccompProfile: type: RuntimeDefault # Init containers handle initialization tasks once initContainers: - name: wait-for-database image: /library/bookwyrm-web:latest command: ["/bin/bash", "-c"] args: - | echo "Waiting for database..." max_attempts=30 attempt=1 while [ $attempt -le $max_attempts ]; do if python manage.py check --database default >/dev/null 2>&1; then echo "Database is ready!" exit 0 fi echo "Database not ready (attempt $attempt/$max_attempts), waiting..." sleep 2 attempt=$((attempt + 1)) done echo "Database failed to become ready after $max_attempts attempts" exit 1 envFrom: - configMapRef: name: bookwyrm-config - secretRef: name: bookwyrm-secrets securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] readOnlyRootFilesystem: false runAsNonRoot: true runAsUser: 1000 - name: run-migrations image: /library/bookwyrm-web:latest command: ["/bin/bash", "-c"] args: - | echo "Running database migrations..." python manage.py migrate --noinput echo "Initializing database if needed..." python manage.py initdb || echo "Database already initialized" envFrom: - configMapRef: name: bookwyrm-config - secretRef: name: bookwyrm-secrets volumeMounts: - name: app-storage mountPath: /app/images subPath: images - name: app-storage mountPath: /app/static subPath: static securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] readOnlyRootFilesystem: false runAsNonRoot: true runAsUser: 1000 containers: - name: bookwyrm-web image: /library/bookwyrm-web:latest imagePullPolicy: Always ports: - containerPort: 80 name: http protocol: TCP env: - name: CONTAINER_TYPE value: "web" - name: DJANGO_SETTINGS_MODULE value: "bookwyrm.settings" - name: FORCE_COLLECTSTATIC value: "true" - name: FORCE_COMPILE_THEMES value: "true" - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace envFrom: - configMapRef: name: bookwyrm-config - secretRef: name: bookwyrm-secrets resources: requests: cpu: 500m # Reduced from 1000m - similar to Pixelfed memory: 1Gi # Reduced from 2Gi - sufficient for Django startup limits: cpu: 2000m # Keep same limit for bursts memory: 4Gi # Keep same limit for safety volumeMounts: - name: app-storage mountPath: /app/images subPath: images - name: app-storage mountPath: /app/static subPath: static - name: app-storage mountPath: /app/exports subPath: exports - name: backups-storage mountPath: /backups - name: cache-storage mountPath: /tmp livenessProbe: httpGet: path: /health/ port: http initialDelaySeconds: 60 periodSeconds: 30 timeoutSeconds: 10 failureThreshold: 3 readinessProbe: httpGet: path: /health/ port: http initialDelaySeconds: 30 periodSeconds: 10 timeoutSeconds: 5 failureThreshold: 3 securityContext: allowPrivilegeEscalation: false capabilities: drop: - ALL readOnlyRootFilesystem: false runAsNonRoot: true runAsUser: 1000 volumes: - name: app-storage persistentVolumeClaim: claimName: bookwyrm-app-storage - name: cache-storage persistentVolumeClaim: claimName: bookwyrm-cache-storage - name: backups-storage persistentVolumeClaim: claimName: bookwyrm-backups nodeSelector: kubernetes.io/arch: arm64 tolerations: - effect: NoSchedule key: node-role.kubernetes.io/control-plane operator: Exists --- apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: bookwyrm-web-hpa namespace: bookwyrm-application spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: bookwyrm-web minReplicas: 2 maxReplicas: 6 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 70 - type: Resource resource: name: memory target: type: Utilization averageUtilization: 80 behavior: scaleDown: stabilizationWindowSeconds: 300 policies: - type: Percent value: 50 periodSeconds: 60 scaleUp: stabilizationWindowSeconds: 60 policies: - type: Percent value: 100 periodSeconds: 60