apiVersion: apps/v1 kind: Deployment metadata: name: towerops namespace: towerops spec: replicas: 1 strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 # Allow 3rd pod during rollout to avoid downtime maxUnavailable: 0 # Keep all pods running during rollout minReadySeconds: 30 # Wait 30s after pod is ready before considering it available (endpoint propagation buffer) selector: matchLabels: app: towerops template: metadata: labels: app: towerops annotations: prometheus.io/scrape: "true" prometheus.io/port: "9568" prometheus.io/path: "/metrics" prometheus.io/job: "towerops" spec: # Use system-cluster-critical priority to ensure towerops starts after CNI is ready priorityClassName: system-cluster-critical terminationGracePeriodSeconds: 90 # Allow 90s for graceful shutdown and connection draining imagePullSecrets: - name: forgejo-registry securityContext: runAsNonRoot: true runAsUser: 65534 fsGroup: 65534 seccompProfile: type: RuntimeDefault initContainers: - name: migrate image: codeberg.org/gmcintire/towerops:main-1777403311-23304e1 imagePullPolicy: IfNotPresent command: ["/app/bin/migrate"] securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 65534 capabilities: drop: - ALL seccompProfile: type: RuntimeDefault env: - name: MIX_ENV value: "prod" - name: RELEASE_COOKIE valueFrom: secretKeyRef: name: towerops-secrets key: RELEASE_COOKIE - name: SECRET_KEY_BASE valueFrom: secretKeyRef: name: towerops-secrets key: SECRET_KEY_BASE - name: CLOAK_KEY valueFrom: secretKeyRef: name: towerops-secrets key: CLOAK_KEY - name: STRIPE_SECRET_KEY valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_SECRET_KEY optional: true - name: STRIPE_WEBHOOK_SECRET valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_WEBHOOK_SECRET optional: true - name: STRIPE_PRICE_ID valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_PRICE_ID optional: true - name: STRIPE_METER_ID valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_METER_ID optional: true - name: DEEPSEEK_API_KEY valueFrom: secretKeyRef: name: towerops-llm key: DEEPSEEK_API_KEY optional: true - name: DEEPSEEK_MODEL valueFrom: secretKeyRef: name: towerops-llm key: DEEPSEEK_MODEL optional: true envFrom: - secretRef: name: towerops-db - secretRef: name: towerops-aws resources: requests: memory: "512Mi" cpu: "100m" limits: memory: "1Gi" cpu: "500m" containers: - name: towerops image: codeberg.org/gmcintire/towerops:main-1777403311-23304e1 imagePullPolicy: IfNotPresent securityContext: allowPrivilegeEscalation: false runAsNonRoot: true runAsUser: 65534 capabilities: drop: - ALL seccompProfile: type: RuntimeDefault ports: - containerPort: 4000 name: http - containerPort: 4369 name: epmd - containerPort: 9000 name: dist - containerPort: 9568 name: metrics lifecycle: preStop: exec: command: - /bin/sh - -c - | # Stop accepting new connections by failing readiness probe # Give load balancer time to remove pod from rotation # Allow existing connections to complete sleep 20 env: - name: POD_IP valueFrom: fieldRef: fieldPath: status.podIP - name: POD_NAME valueFrom: fieldRef: fieldPath: metadata.name - name: POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: ETCD_ENABLED value: "true" - name: RELEASE_DISTRIBUTION value: "name" - name: RELEASE_NODE value: "towerops@$(POD_IP)" - name: PORT value: "4000" - name: PHX_HOST value: "towerops.net" - name: MIX_ENV value: "prod" - name: RELEASE_COOKIE valueFrom: secretKeyRef: name: towerops-secrets key: RELEASE_COOKIE - name: SECRET_KEY_BASE valueFrom: secretKeyRef: name: towerops-secrets key: SECRET_KEY_BASE - name: CLOAK_KEY valueFrom: secretKeyRef: name: towerops-secrets key: CLOAK_KEY - name: STRIPE_SECRET_KEY valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_SECRET_KEY optional: true - name: STRIPE_WEBHOOK_SECRET valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_WEBHOOK_SECRET optional: true - name: STRIPE_PRICE_ID valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_PRICE_ID optional: true - name: STRIPE_METER_ID valueFrom: secretKeyRef: name: towerops-billing key: STRIPE_METER_ID optional: true - name: DEEPSEEK_API_KEY valueFrom: secretKeyRef: name: towerops-llm key: DEEPSEEK_API_KEY optional: true - name: DEEPSEEK_MODEL valueFrom: secretKeyRef: name: towerops-llm key: DEEPSEEK_MODEL optional: true envFrom: # Redis connection configured via towerops-redis secret - secretRef: name: towerops-redis - secretRef: name: towerops-db - secretRef: name: towerops-aws resources: requests: memory: "1.5Gi" cpu: "500m" limits: memory: "3Gi" cpu: "2000m" startupProbe: httpGet: path: /health port: 4000 initialDelaySeconds: 10 periodSeconds: 5 timeoutSeconds: 3 failureThreshold: 24 # 10s + (24 * 5s) = 130s max startup time livenessProbe: httpGet: path: /health port: 4000 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 readinessProbe: httpGet: path: /health port: 4000 initialDelaySeconds: 5 periodSeconds: 5 timeoutSeconds: 2 successThreshold: 3 # Require 3 consecutive successes (15s) before marking ready failureThreshold: 2 # Allow 1 failure before marking not ready volumeMounts: - name: data mountPath: /data volumes: - name: data nfs: server: 10.0.15.103 path: /data