From 5085840a7904f2616d872d5cd16d9a05c602e04f Mon Sep 17 00:00:00 2001 From: Will Anderson Date: Thu, 23 Apr 2026 21:43:20 -0500 Subject: [PATCH] Add neuron prod DB backup and tighten actuator surface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add hourly restic backup CronJob to neuron-prod namespace - Backs up /data/neuron-prod.db to Cloudflare R2 (legion-neuron-backup bucket) - Retention: 24 hourly, 7 daily, 4 weekly — <$0.01/month at current DB size - ExternalSecret pulls restic password + R2 creds from Vault secret/r2 - Actuator exposure per environment: - prod: health,info only / show-details=never (no internals exposed) - stage: health,info,metrics,loggers / show-details=always (pre-prod visibility) - dev: full (health,info,metrics,env,beans,loggers,mappings) / show-details=always --- .../neuron-technologies/dev/configmap.yaml | 3 +- .../prod/backup-cronjob.yaml | 104 ++++++++++++++++++ .../neuron-technologies/prod/configmap.yaml | 5 +- .../prod/kustomization.yaml | 1 + .../neuron-technologies/stage/configmap.yaml | 3 +- 5 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 servers/legion/k8s/neuron-technologies/prod/backup-cronjob.yaml diff --git a/servers/legion/k8s/neuron-technologies/dev/configmap.yaml b/servers/legion/k8s/neuron-technologies/dev/configmap.yaml index 37d0505..7a0e51c 100644 --- a/servers/legion/k8s/neuron-technologies/dev/configmap.yaml +++ b/servers/legion/k8s/neuron-technologies/dev/configmap.yaml @@ -10,5 +10,6 @@ data: NEURON_DB_PATH: "/data/neuron-dev.db" NEURON_STORAGE_PATH: "/data" SERVER_TOMCAT_ACCESSLOG_ENABLED: "true" - MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,metrics" + # Dev: full exposure for debugging + MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,metrics,env,beans,loggers,mappings" MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: "always" diff --git a/servers/legion/k8s/neuron-technologies/prod/backup-cronjob.yaml b/servers/legion/k8s/neuron-technologies/prod/backup-cronjob.yaml new file mode 100644 index 0000000..d133836 --- /dev/null +++ b/servers/legion/k8s/neuron-technologies/prod/backup-cronjob.yaml @@ -0,0 +1,104 @@ +--- +# neuron-backup-credentials — restic + R2 for neuron prod DB backups +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: neuron-backup-credentials + namespace: neuron-prod +spec: + refreshInterval: 1h + secretStoreRef: + name: vault + kind: ClusterSecretStore + target: + name: neuron-backup-credentials + creationPolicy: Owner + template: + data: + RESTIC_PASSWORD: "{{ .restic_password }}" + AWS_ACCESS_KEY_ID: "{{ .r2_access_key_id }}" + AWS_SECRET_ACCESS_KEY: "{{ .r2_secret_access_key }}" + RESTIC_REPOSITORY: "s3:https://651161e0a3d321561b4c90b5bcd5f15b.r2.cloudflarestorage.com/legion-neuron-backup" + data: + - secretKey: restic_password + remoteRef: + key: secret/data/r2 + property: neuron_restic_password + - secretKey: r2_access_key_id + remoteRef: + key: secret/data/r2 + property: access_key_id + - secretKey: r2_secret_access_key + remoteRef: + key: secret/data/r2 + property: secret_access_key +--- +# Hourly neuron prod DB backup to Cloudflare R2 via restic. +# Retention: 24 hourly, 7 daily, 4 weekly. +# At current DB size (~10MB) this is <$0.01/month. +# Mounts neuron-prod-data PVC read-only — safe on single-node k3s. +apiVersion: batch/v1 +kind: CronJob +metadata: + name: neuron-db-backup + namespace: neuron-prod +spec: + schedule: "0 * * * *" + concurrencyPolicy: Forbid + failedJobsHistoryLimit: 3 + successfulJobsHistoryLimit: 3 + jobTemplate: + spec: + backoffLimit: 2 + template: + spec: + restartPolicy: OnFailure + containers: + - name: backup + image: restic/restic:latest + command: + - /bin/sh + - -c + - | + set -e + + # Init repo on first run (no-op if already initialized) + restic snapshots 2>/dev/null || restic init + + # Backup the SQLite DB file (WAL mode: exclude -shm/-wal, they're volatile) + echo "Backing up neuron prod DB..." + restic backup \ + /data/neuron-prod.db \ + --tag neuron-prod \ + --host legion \ + --exclude '*.db-shm' \ + --exclude '*.db-wal' + + # Prune: 24 hourly, 7 daily, 4 weekly + restic forget \ + --keep-hourly 24 \ + --keep-daily 7 \ + --keep-weekly 4 \ + --prune + + echo "Backup complete." + restic snapshots --latest 3 + envFrom: + - secretRef: + name: neuron-backup-credentials + volumeMounts: + - name: data + mountPath: /data + readOnly: true + resources: + requests: + memory: 128Mi + cpu: 50m + limits: + memory: 256Mi + cpu: 200m + volumes: + - name: data + persistentVolumeClaim: + claimName: neuron-prod-data + readOnly: true diff --git a/servers/legion/k8s/neuron-technologies/prod/configmap.yaml b/servers/legion/k8s/neuron-technologies/prod/configmap.yaml index fe4fbb3..f00147a 100644 --- a/servers/legion/k8s/neuron-technologies/prod/configmap.yaml +++ b/servers/legion/k8s/neuron-technologies/prod/configmap.yaml @@ -8,5 +8,6 @@ data: NEURON_DB_PATH: "/data/neuron-prod.db" NEURON_STORAGE_PATH: "/data" SERVER_TOMCAT_ACCESSLOG_ENABLED: "true" - MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,metrics" - MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: "always" + # Prod: minimal surface — never expose internals + MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info" + MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: "never" diff --git a/servers/legion/k8s/neuron-technologies/prod/kustomization.yaml b/servers/legion/k8s/neuron-technologies/prod/kustomization.yaml index 4ea8ac1..5791af3 100644 --- a/servers/legion/k8s/neuron-technologies/prod/kustomization.yaml +++ b/servers/legion/k8s/neuron-technologies/prod/kustomization.yaml @@ -8,3 +8,4 @@ resources: - rest-deployment.yaml - services.yaml - ingress.yaml + - backup-cronjob.yaml diff --git a/servers/legion/k8s/neuron-technologies/stage/configmap.yaml b/servers/legion/k8s/neuron-technologies/stage/configmap.yaml index a83d141..122d743 100644 --- a/servers/legion/k8s/neuron-technologies/stage/configmap.yaml +++ b/servers/legion/k8s/neuron-technologies/stage/configmap.yaml @@ -8,5 +8,6 @@ data: NEURON_DB_PATH: "/data/neuron-stage.db" NEURON_STORAGE_PATH: "/data" SERVER_TOMCAT_ACCESSLOG_ENABLED: "true" - MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,metrics" + # Stage: extended for pre-prod verification, not fully open + MANAGEMENT_ENDPOINTS_WEB_EXPOSURE_INCLUDE: "health,info,metrics,loggers" MANAGEMENT_ENDPOINT_HEALTH_SHOW_DETAILS: "always"