From e4649f3d76d74d3c2afcbb15c452e8d0c26f84d9 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 29 Jun 2026 09:43:37 -0700 Subject: [PATCH 01/12] housekeeping --- charts/helm/checkmate/INSTALLATION.md | 5 ++- .../deployment.yaml} | 0 .../ingress.yaml} | 0 .../service.yaml} | 0 .../templates/{ => db}/mongodb-service.yaml | 0 .../{ => db}/mongodb-statefulsets.yaml | 0 .../checkmate/templates/redis-service.yaml | 11 ------- .../templates/redis-statefulsets.yaml | 33 ------------------- .../deployment.yaml} | 0 .../ingress.yaml} | 0 .../nginx-cm.yaml} | 0 .../service.yaml} | 0 charts/helm/checkmate/values.yaml | 10 ------ docker/.gitignore | 4 --- docker/coolify/build_images.bat | 9 ++--- docker/coolify/build_images.sh | 1 - docker/coolify/redis.Dockerfile | 2 -- .../dev/docker-compose.custom-ca-example.yaml | 1 - docker/dist-arm/docker-compose.yaml | 1 - 19 files changed, 5 insertions(+), 72 deletions(-) rename charts/helm/checkmate/templates/{client-deployment.yaml => client/deployment.yaml} (100%) rename charts/helm/checkmate/templates/{client-ingress.yaml => client/ingress.yaml} (100%) rename charts/helm/checkmate/templates/{client-service.yaml => client/service.yaml} (100%) rename charts/helm/checkmate/templates/{ => db}/mongodb-service.yaml (100%) rename charts/helm/checkmate/templates/{ => db}/mongodb-statefulsets.yaml (100%) delete mode 100644 charts/helm/checkmate/templates/redis-service.yaml delete mode 100644 charts/helm/checkmate/templates/redis-statefulsets.yaml rename charts/helm/checkmate/templates/{server-deployment.yaml => server/deployment.yaml} (100%) rename charts/helm/checkmate/templates/{server-ingress.yaml => server/ingress.yaml} (100%) rename charts/helm/checkmate/templates/{server-nginx-cm.yaml => server/nginx-cm.yaml} (100%) rename charts/helm/checkmate/templates/{server-service.yaml => server/service.yaml} (100%) delete mode 100755 docker/coolify/redis.Dockerfile diff --git a/charts/helm/checkmate/INSTALLATION.md b/charts/helm/checkmate/INSTALLATION.md index 06cf9f7b9..9792a696f 100644 --- a/charts/helm/checkmate/INSTALLATION.md +++ b/charts/helm/checkmate/INSTALLATION.md @@ -23,8 +23,7 @@ Edit `values.yaml` to update: - `server.protocol` (usually http or https) - **If upgrading**: Migrate persistence settings from flat structure to nested: - Old: `persistence.mongodbSize` → New: `persistence.mongo.size` - - Old: `persistence.redisSize` → New: `persistence.redis.size` - - Add: `persistence.mongo.storageClass` and `persistence.redis.storageClass` (leave empty for default) + - Add: `persistence.mongo.storageClass` (leave empty for default) - Secrets under the `secrets` section (`JWT_SECRET`, email credentials, API keys, etc.) — replace all change_me values - **For TLS/HTTPS**: Configure ingress TLS settings (see section below) @@ -32,7 +31,7 @@ Edit `values.yaml` to update: ```bash helm install checkmate ./charts/helm/checkmate ``` -This will deploy the client, server, MongoDB, and Redis components. +This will deploy the client, server, and MongoDB components. ### 4. Verify the deployment Check pods and services: diff --git a/charts/helm/checkmate/templates/client-deployment.yaml b/charts/helm/checkmate/templates/client/deployment.yaml similarity index 100% rename from charts/helm/checkmate/templates/client-deployment.yaml rename to charts/helm/checkmate/templates/client/deployment.yaml diff --git a/charts/helm/checkmate/templates/client-ingress.yaml b/charts/helm/checkmate/templates/client/ingress.yaml similarity index 100% rename from charts/helm/checkmate/templates/client-ingress.yaml rename to charts/helm/checkmate/templates/client/ingress.yaml diff --git a/charts/helm/checkmate/templates/client-service.yaml b/charts/helm/checkmate/templates/client/service.yaml similarity index 100% rename from charts/helm/checkmate/templates/client-service.yaml rename to charts/helm/checkmate/templates/client/service.yaml diff --git a/charts/helm/checkmate/templates/mongodb-service.yaml b/charts/helm/checkmate/templates/db/mongodb-service.yaml similarity index 100% rename from charts/helm/checkmate/templates/mongodb-service.yaml rename to charts/helm/checkmate/templates/db/mongodb-service.yaml diff --git a/charts/helm/checkmate/templates/mongodb-statefulsets.yaml b/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml similarity index 100% rename from charts/helm/checkmate/templates/mongodb-statefulsets.yaml rename to charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml diff --git a/charts/helm/checkmate/templates/redis-service.yaml b/charts/helm/checkmate/templates/redis-service.yaml deleted file mode 100644 index 2720350da..000000000 --- a/charts/helm/checkmate/templates/redis-service.yaml +++ /dev/null @@ -1,11 +0,0 @@ -{{- if .Values.redis.enabled }} -apiVersion: v1 -kind: Service -metadata: - name: checkmate-redis -spec: - selector: - app: checkmate-redis - ports: - - port: {{ .Values.redis.port }} -{{- end }} diff --git a/charts/helm/checkmate/templates/redis-statefulsets.yaml b/charts/helm/checkmate/templates/redis-statefulsets.yaml deleted file mode 100644 index acf7c85a8..000000000 --- a/charts/helm/checkmate/templates/redis-statefulsets.yaml +++ /dev/null @@ -1,33 +0,0 @@ -{{- if .Values.redis.enabled }} -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: checkmate-redis -spec: - replicas: 1 - selector: - matchLabels: - app: checkmate-redis - template: - metadata: - labels: - app: checkmate-redis - spec: - containers: - - name: redis - image: {{ .Values.redis.image }} - ports: - - containerPort: {{ .Values.redis.port }} - volumeMounts: - - name: redis-data - mountPath: /data - volumeClaimTemplates: - - metadata: - name: checkmate-redis-persistent-storage - spec: - storageClassName: {{ .Values.persistence.redis.storageClass | quote }} - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: {{ .Values.persistence.redis.size | default "1Gi" | quote }} -{{- end }} \ No newline at end of file diff --git a/charts/helm/checkmate/templates/server-deployment.yaml b/charts/helm/checkmate/templates/server/deployment.yaml similarity index 100% rename from charts/helm/checkmate/templates/server-deployment.yaml rename to charts/helm/checkmate/templates/server/deployment.yaml diff --git a/charts/helm/checkmate/templates/server-ingress.yaml b/charts/helm/checkmate/templates/server/ingress.yaml similarity index 100% rename from charts/helm/checkmate/templates/server-ingress.yaml rename to charts/helm/checkmate/templates/server/ingress.yaml diff --git a/charts/helm/checkmate/templates/server-nginx-cm.yaml b/charts/helm/checkmate/templates/server/nginx-cm.yaml similarity index 100% rename from charts/helm/checkmate/templates/server-nginx-cm.yaml rename to charts/helm/checkmate/templates/server/nginx-cm.yaml diff --git a/charts/helm/checkmate/templates/server-service.yaml b/charts/helm/checkmate/templates/server/service.yaml similarity index 100% rename from charts/helm/checkmate/templates/server-service.yaml rename to charts/helm/checkmate/templates/server/service.yaml diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml index 9a586dd54..211b29b98 100644 --- a/charts/helm/checkmate/values.yaml +++ b/charts/helm/checkmate/values.yaml @@ -36,11 +36,6 @@ server: # a cert-manager issuer annotation (e.g. cert-manager.io/cluster-issuer: "letsencrypt-prod"). # The secret will be automatically created by cert-manager when using the cert-manager.io/cluster-issuer annotation -redis: - enabled: false - image: redis:7.2 - port: 6379 - mongodb: image: ghcr.io/bluewave-labs/checkmate-mongo:v3.2.0 port: 27017 @@ -55,8 +50,6 @@ secrets: # PAGESPEED_API_KEY: change_me DB_CONNECTION_STRING: mongodb://checkmate-mongodb.namespace.svc:27017/uptime_db CLIENT_HOST: change_me -# REDIS_HOST: redis -# REDIS_PORT: "6379" # DB_TYPE: MongoDB # TOKEN_TTL: 99d # REFRESH_TOKEN_TTL: 99d @@ -64,7 +57,4 @@ secrets: persistence: mongo: size: 5Gi - storageClass: "" - redis: - size: 1Gi storageClass: "" \ No newline at end of file diff --git a/docker/.gitignore b/docker/.gitignore index 76567d514..4c3f83657 100755 --- a/docker/.gitignore +++ b/docker/.gitignore @@ -2,14 +2,10 @@ !quickstart.sh !build_images.sh dev/mongo/data/* -dev/redis/data/* dist/mongo/data/* -dist/redis/data/* prod/mongo/data/* -prod/redis/data/* dist/docker-compose-test.yaml /dist-mono/mongo/data/* -/dist-mono/redis/data/* /dist-arm/mongo/data/* *.env diff --git a/docker/coolify/build_images.bat b/docker/coolify/build_images.bat index 60fc9aa4a..b940039b6 100644 --- a/docker/coolify/build_images.bat +++ b/docker/coolify/build_images.bat @@ -12,14 +12,11 @@ set dockerfiles[0]=.\docker\coolify\client.Dockerfile set services[1]=bluewaveuptime/uptime_database_mongo set dockerfiles[1]=.\docker\coolify\mongoDB.Dockerfile -set services[2]=bluewaveuptime/uptime_redis -set dockerfiles[2]=.\docker\coolify\redis.Dockerfile - -set services[3]=bluewaveuptime/uptime_server -set dockerfiles[3]=.\docker\coolify\server.Dockerfile +set services[2]=bluewaveuptime/uptime_server +set dockerfiles[2]=.\docker\coolify\server.Dockerfile REM Loop through each service and build the corresponding image -for /L %%i in (0,1,2,3) do ( +for /L %%i in (0,1,2) do ( set service=!services[%%i]! set dockerfile=!dockerfiles[%%i]! diff --git a/docker/coolify/build_images.sh b/docker/coolify/build_images.sh index 04dfe324c..1d29ae5b1 100755 --- a/docker/coolify/build_images.sh +++ b/docker/coolify/build_images.sh @@ -7,7 +7,6 @@ cd ../../ declare -A services=( ["bluewaveuptime/uptime_client"]="./docker/coolify/client.Dockerfile" ["bluewaveuptime/uptime_database_mongo"]="./docker/coolify/mongoDB.Dockerfile" - ["bluewaveuptime/uptime_redis"]="./docker/coolify/redis.Dockerfile" ["bluewaveuptime/uptime_server"]="./docker/coolify/server.Dockerfile" ) diff --git a/docker/coolify/redis.Dockerfile b/docker/coolify/redis.Dockerfile deleted file mode 100755 index af68ec61e..000000000 --- a/docker/coolify/redis.Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM redis -EXPOSE 6379 \ No newline at end of file diff --git a/docker/dev/docker-compose.custom-ca-example.yaml b/docker/dev/docker-compose.custom-ca-example.yaml index a5d7e2f43..e1f185ae9 100644 --- a/docker/dev/docker-compose.custom-ca-example.yaml +++ b/docker/dev/docker-compose.custom-ca-example.yaml @@ -19,7 +19,6 @@ services: # Mount the certs directory (read-only for security) - ./certs:/certs:ro depends_on: - - redis - mongodb # Optional: Add healthcheck to ensure the server starts properly healthcheck: diff --git a/docker/dist-arm/docker-compose.yaml b/docker/dist-arm/docker-compose.yaml index b9aba057c..49f81fe9a 100644 --- a/docker/dist-arm/docker-compose.yaml +++ b/docker/dist-arm/docker-compose.yaml @@ -9,7 +9,6 @@ services: - UPTIME_APP_API_BASE_URL=http://localhost:52345/api/v1 - UPTIME_APP_CLIENT_HOST=http://localhost - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db - - REDIS_URL=redis://redis:6379 - CLIENT_HOST=http://localhost - JWT_SECRET=my_secret depends_on: From f58825d19f6a1fe64c92c2a20b0c6d75437904f8 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 29 Jun 2026 10:03:48 -0700 Subject: [PATCH 02/12] clean up env vars --- charts/helm/checkmate/values.yaml | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml index 211b29b98..4c5a75fd1 100644 --- a/charts/helm/checkmate/values.yaml +++ b/charts/helm/checkmate/values.yaml @@ -42,17 +42,11 @@ mongodb: secrets: JWT_SECRET: change_me -# REFRESH_TOKEN_SECRET: change_me -# SYSTEM_EMAIL_ADDRESS: test@example.com -# SYSTEM_EMAIL_PASSWORD: change_me -# SYSTEM_EMAIL_HOST: smtp.example.com -# SYSTEM_EMAIL_PORT: "587" -# PAGESPEED_API_KEY: change_me DB_CONNECTION_STRING: mongodb://checkmate-mongodb.namespace.svc:27017/uptime_db CLIENT_HOST: change_me -# DB_TYPE: MongoDB -# TOKEN_TTL: 99d -# REFRESH_TOKEN_TTL: 99d + NODE_ENV: production + LOG_LEVEL: info + TOKEN_TTL: 99d persistence: mongo: From c6c41c2e3e1a09cc77a7d795eb4de365e87eeb59 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 29 Jun 2026 10:06:34 -0700 Subject: [PATCH 03/12] bare sevice --- charts/helm/checkmate/values.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml index 4c5a75fd1..0dbbae89c 100644 --- a/charts/helm/checkmate/values.yaml +++ b/charts/helm/checkmate/values.yaml @@ -42,7 +42,7 @@ mongodb: secrets: JWT_SECRET: change_me - DB_CONNECTION_STRING: mongodb://checkmate-mongodb.namespace.svc:27017/uptime_db + DB_CONNECTION_STRING: mongodb://checkmate-mongodb:27017/uptime_db CLIENT_HOST: change_me NODE_ENV: production LOG_LEVEL: info From 35c9618d599be2ec50060a7adfe4ec49b7e06217 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 29 Jun 2026 10:15:29 -0700 Subject: [PATCH 04/12] server->api --- charts/helm/checkmate/INSTALLATION.md | 20 ++++++------- .../templates/{server => api}/deployment.yaml | 24 +++++++++------- .../templates/{server => api}/ingress.yaml | 22 +++++++-------- .../helm/checkmate/templates/api/service.yaml | 9 ++++++ .../templates/client/deployment.yaml | 4 +-- .../{server => client}/nginx-cm.yaml | 6 ++-- .../helm/checkmate/templates/prechecks.yaml | 28 +++++++++---------- .../checkmate/templates/server/service.yaml | 9 ------ charts/helm/checkmate/values.yaml | 11 +++++--- 9 files changed, 70 insertions(+), 63 deletions(-) rename charts/helm/checkmate/templates/{server => api}/deployment.yaml (51%) rename charts/helm/checkmate/templates/{server => api}/ingress.yaml (61%) create mode 100644 charts/helm/checkmate/templates/api/service.yaml rename charts/helm/checkmate/templates/{server => client}/nginx-cm.yaml (84%) delete mode 100644 charts/helm/checkmate/templates/server/service.yaml diff --git a/charts/helm/checkmate/INSTALLATION.md b/charts/helm/checkmate/INSTALLATION.md index 9792a696f..ae0f36b8b 100644 --- a/charts/helm/checkmate/INSTALLATION.md +++ b/charts/helm/checkmate/INSTALLATION.md @@ -19,8 +19,8 @@ cd checkmate/charts/helm/checkmate ### 2. Customize values.yaml Edit `values.yaml` to update: -- `client.ingress.host` and `server.ingress.host` with your domain names -- `server.protocol` (usually http or https) +- `client.ingress.host` and `api.ingress.host` with your domain names +- `api.protocol` (usually http or https) - **If upgrading**: Migrate persistence settings from flat structure to nested: - Old: `persistence.mongodbSize` → New: `persistence.mongo.size` - Add: `persistence.mongo.storageClass` (leave empty for default) @@ -31,7 +31,7 @@ Edit `values.yaml` to update: ```bash helm install checkmate ./charts/helm/checkmate ``` -This will deploy the client, server, and MongoDB components. +This will deploy the client, api, and MongoDB components. ### 4. Verify the deployment Check pods and services: @@ -67,7 +67,7 @@ client: enabled: true secretName: checkmate-client-tls -server: +api: protocol: https ingress: enabled: true @@ -77,7 +77,7 @@ server: cert-manager.io/cluster-issuer: "letsencrypt-prod" tls: enabled: true - secretName: checkmate-server-tls + secretName: checkmate-api-tls ``` ### Alternative: Using --set flags @@ -87,13 +87,13 @@ You can also enable TLS during installation using Helm's `--set` flags: ```bash helm install checkmate ./charts/helm/checkmate \ --set client.protocol=https \ - --set server.protocol=https \ + --set api.protocol=https \ --set client.ingress.annotations."cert-manager\.io/cluster-issuer"="letsencrypt-prod" \ --set client.ingress.tls.enabled=true \ --set client.ingress.tls.secretName=checkmate-client-tls \ - --set server.ingress.annotations."cert-manager\.io/cluster-issuer"="letsencrypt-prod" \ - --set server.ingress.tls.enabled=true \ - --set server.ingress.tls.secretName=checkmate-server-tls + --set api.ingress.annotations."cert-manager\.io/cluster-issuer"="letsencrypt-prod" \ + --set api.ingress.tls.enabled=true \ + --set api.ingress.tls.secretName=checkmate-api-tls ``` ### Verification @@ -106,7 +106,7 @@ kubectl get certificates # Check certificate details kubectl describe certificate checkmate-client-tls -kubectl describe certificate checkmate-server-tls +kubectl describe certificate checkmate-api-tls # Verify the secrets were created kubectl get secrets | grep checkmate-tls diff --git a/charts/helm/checkmate/templates/server/deployment.yaml b/charts/helm/checkmate/templates/api/deployment.yaml similarity index 51% rename from charts/helm/checkmate/templates/server/deployment.yaml rename to charts/helm/checkmate/templates/api/deployment.yaml index 393964ccd..63bafc625 100644 --- a/charts/helm/checkmate/templates/server/deployment.yaml +++ b/charts/helm/checkmate/templates/api/deployment.yaml @@ -1,37 +1,41 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: checkmate-server + name: checkmate-api spec: - replicas: 1 + replicas: 1 # single scheduler — seeds/reconciles the jobs collection selector: matchLabels: - app: checkmate-server + app: checkmate-api template: metadata: labels: - app: checkmate-server + app: checkmate-api spec: - {{- with .Values.server.affinity }} + {{- with .Values.api.affinity }} affinity: {{- tpl ( . | toYaml) $ | nindent 8 }} {{- end }} - {{- with .Values.server.tolerations }} + {{- with .Values.api.tolerations }} tolerations: {{- tpl ( . | toYaml) $ | nindent 8 }} {{- end }} containers: - - name: server - image: {{ .Values.server.image }} + - name: api + image: {{ .Values.api.image }} ports: - - containerPort: {{ .Values.server.port }} + - containerPort: {{ .Values.api.port }} envFrom: - secretRef: name: checkmate-secrets - {{- with .Values.server.resources }} + {{- with .Values.api.resources }} resources: {{- tpl ( . | toYaml) $ | nindent 12 }} {{- end }} env: - name: CLIENT_HOST value: "{{ .Values.client.protocol }}://{{ .Values.client.ingress.host }}" + - name: QUEUE_MODE + value: "primary" + - name: QUEUE_PRIMARY_PROCESSES # false: API schedules only, worker tier processes jobs + value: {{ .Values.api.queuePrimaryProcesses | quote }} diff --git a/charts/helm/checkmate/templates/server/ingress.yaml b/charts/helm/checkmate/templates/api/ingress.yaml similarity index 61% rename from charts/helm/checkmate/templates/server/ingress.yaml rename to charts/helm/checkmate/templates/api/ingress.yaml index 34c9ac0dc..f6b9db114 100644 --- a/charts/helm/checkmate/templates/server/ingress.yaml +++ b/charts/helm/checkmate/templates/api/ingress.yaml @@ -1,11 +1,11 @@ -{{- if .Values.server.ingress.enabled }} +{{- if .Values.api.ingress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: - name: checkmate-server-ingress - {{- if .Values.server.ingress.annotations }} + name: checkmate-api-ingress + {{- if .Values.api.ingress.annotations }} annotations: - {{- range $key, $value := .Values.server.ingress.annotations }} + {{- range $key, $value := .Values.api.ingress.annotations }} {{ $key }}: {{ $value | quote }} {{- end }} {{- end }} @@ -17,22 +17,22 @@ metadata: #nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" #nginx.ingress.kubernetes.io/cors-allow-credentials: "true"*/}} spec: - ingressClassName: {{ .Values.server.ingress.className }} - {{- if .Values.server.ingress.tls.enabled }} + ingressClassName: {{ .Values.api.ingress.className }} + {{- if .Values.api.ingress.tls.enabled }} tls: - hosts: - - {{ .Values.server.ingress.host }} - secretName: {{ default (printf "%s-server-tls" .Release.Name) .Values.server.ingress.tls.secretName }} + - {{ .Values.api.ingress.host }} + secretName: {{ default (printf "%s-api-tls" .Release.Name) .Values.api.ingress.tls.secretName }} {{- end }} rules: - - host: {{ .Values.server.ingress.host }} + - host: {{ .Values.api.ingress.host }} http: paths: - path: /api/v1 pathType: Prefix backend: service: - name: checkmate-server + name: checkmate-api port: - number: {{ .Values.server.port }} + number: {{ .Values.api.port }} {{- end }} diff --git a/charts/helm/checkmate/templates/api/service.yaml b/charts/helm/checkmate/templates/api/service.yaml new file mode 100644 index 000000000..d172c72e8 --- /dev/null +++ b/charts/helm/checkmate/templates/api/service.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Service +metadata: + name: checkmate-api +spec: + selector: + app: checkmate-api + ports: + - port: {{ .Values.api.port }} diff --git a/charts/helm/checkmate/templates/client/deployment.yaml b/charts/helm/checkmate/templates/client/deployment.yaml index 381631ef5..b59d3a4a9 100644 --- a/charts/helm/checkmate/templates/client/deployment.yaml +++ b/charts/helm/checkmate/templates/client/deployment.yaml @@ -27,7 +27,7 @@ spec: - containerPort: {{ .Values.client.port }} env: - name: UPTIME_APP_API_BASE_URL - value: "{{ .Values.server.protocol }}://{{ .Values.server.ingress.host }}/api/v1" + value: "{{ .Values.api.protocol }}://{{ .Values.api.ingress.host }}/api/v1" - name: UPTIME_APP_CLIENT_HOST value: "{{ .Values.client.protocol }}://{{ .Values.client.ingress.host }}" volumeMounts: @@ -41,4 +41,4 @@ spec: volumes: - name: config-volume configMap: - name: checkmate-server-nginx-cm + name: checkmate-client-nginx-cm diff --git a/charts/helm/checkmate/templates/server/nginx-cm.yaml b/charts/helm/checkmate/templates/client/nginx-cm.yaml similarity index 84% rename from charts/helm/checkmate/templates/server/nginx-cm.yaml rename to charts/helm/checkmate/templates/client/nginx-cm.yaml index 84da55299..95d1ec936 100644 --- a/charts/helm/checkmate/templates/server/nginx-cm.yaml +++ b/charts/helm/checkmate/templates/client/nginx-cm.yaml @@ -1,7 +1,7 @@ apiVersion: v1 kind: ConfigMap metadata: - name: checkmate-server-nginx-cm + name: checkmate-client-nginx-cm data: default.conf: | server { @@ -22,7 +22,7 @@ data: } # location /api/ { - # proxy_pass http://{{ .Values.server.ingress.host }}:5000/api/; + # proxy_pass http://{{ .Values.api.ingress.host }}:5000/api/; # proxy_http_version 1.1; # proxy_set_header Host $host; # proxy_set_header X-Real-IP $remote_addr; @@ -31,7 +31,7 @@ data: # } location /api-docs/ { - proxy_pass http://{{ .Values.server.ingress.host }}:5000/api-docs/; + proxy_pass http://{{ .Values.api.ingress.host }}:5000/api-docs/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; diff --git a/charts/helm/checkmate/templates/prechecks.yaml b/charts/helm/checkmate/templates/prechecks.yaml index e2b9ec5c2..4cd337db3 100644 --- a/charts/helm/checkmate/templates/prechecks.yaml +++ b/charts/helm/checkmate/templates/prechecks.yaml @@ -2,13 +2,13 @@ {{- fail "client.ingress.host must be overridden and not set to 'change_me'" }} {{- end }} -{{- if eq .Values.server.ingress.host "change_me" }} - {{- fail "server.ingress.host must be overridden and not set to 'change_me'" }} +{{- if eq .Values.api.ingress.host "change_me" }} + {{- fail "api.ingress.host must be overridden and not set to 'change_me'" }} {{- end }} -{{- $serverProtocol := .Values.server.protocol }} -{{- if not (or (eq $serverProtocol "http") (eq $serverProtocol "https")) }} - {{- fail "server.protocol must be either 'http' or 'https'" }} +{{- $apiProtocol := .Values.api.protocol }} +{{- if not (or (eq $apiProtocol "http") (eq $apiProtocol "https")) }} + {{- fail "api.protocol must be either 'http' or 'https'" }} {{- end }} {{- $clientProtocol := .Values.client.protocol }} @@ -21,28 +21,28 @@ {{- fail "client.ingress.tls.enabled is true but client.protocol is not 'https'. Set client.protocol: https to avoid mixed content." }} {{- end }} -{{- if and .Values.server.ingress.tls.enabled (ne $serverProtocol "https") }} - {{- fail "server.ingress.tls.enabled is true but server.protocol is not 'https'. Set server.protocol: https to ensure correct API base URL." }} +{{- if and .Values.api.ingress.tls.enabled (ne $apiProtocol "https") }} + {{- fail "api.ingress.tls.enabled is true but api.protocol is not 'https'. Set api.protocol: https to ensure correct API base URL." }} {{- end }} {{/* If client runs on https, API must also be https to avoid mixed content */}} -{{- if and (eq $clientProtocol "https") (ne $serverProtocol "https") }} - {{- fail "client.protocol is 'https' but server.protocol is not. Set server.protocol: https to prevent browser mixed-content issues." }} +{{- if and (eq $clientProtocol "https") (ne $apiProtocol "https") }} + {{- fail "client.protocol is 'https' but api.protocol is not. Set api.protocol: https to prevent browser mixed-content issues." }} {{- end }} {{/* Fail early if TLS enabled without cert-manager annotations (cluster-issuer or issuer) */}} {{- $cAnn := .Values.client.ingress.annotations | default dict }} -{{- $sAnn := .Values.server.ingress.annotations | default dict }} +{{- $aAnn := .Values.api.ingress.annotations | default dict }} {{- $clientHasIssuer := or (hasKey $cAnn "cert-manager.io/cluster-issuer") (hasKey $cAnn "cert-manager.io/issuer") }} -{{- $serverHasIssuer := or (hasKey $sAnn "cert-manager.io/cluster-issuer") (hasKey $sAnn "cert-manager.io/issuer") }} +{{- $apiHasIssuer := or (hasKey $aAnn "cert-manager.io/cluster-issuer") (hasKey $aAnn "cert-manager.io/issuer") }} {{- if and .Values.client.ingress.tls.enabled (not $clientHasIssuer) }} {{- fail "client.ingress.tls.enabled is true but no cert-manager issuer annotation found. Add 'cert-manager.io/cluster-issuer' or 'cert-manager.io/issuer'." }} {{- end }} -{{- if and .Values.server.ingress.tls.enabled (not $serverHasIssuer) }} - {{- fail "server.ingress.tls.enabled is true but no cert-manager issuer annotation found. Add 'cert-manager.io/cluster-issuer' or 'cert-manager.io/issuer'." }} +{{- if and .Values.api.ingress.tls.enabled (not $apiHasIssuer) }} + {{- fail "api.ingress.tls.enabled is true but no cert-manager issuer annotation found. Add 'cert-manager.io/cluster-issuer' or 'cert-manager.io/issuer'." }} {{- end }} -{{/* Secret name can be omitted; we default to -client|server-tls in templates */}} +{{/* Secret name can be omitted; we default to -client|api-tls in templates */}} diff --git a/charts/helm/checkmate/templates/server/service.yaml b/charts/helm/checkmate/templates/server/service.yaml deleted file mode 100644 index d8a7fbe86..000000000 --- a/charts/helm/checkmate/templates/server/service.yaml +++ /dev/null @@ -1,9 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: checkmate-server -spec: - selector: - app: checkmate-server - ports: - - port: {{ .Values.server.port }} diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml index 0dbbae89c..900159b49 100644 --- a/charts/helm/checkmate/values.yaml +++ b/charts/helm/checkmate/values.yaml @@ -17,10 +17,13 @@ client: # a cert-manager issuer annotation (e.g. cert-manager.io/cluster-issuer: "letsencrypt-prod"). # The secret will be automatically created by cert-manager when using the cert-manager.io/cluster-issuer annotation -server: +api: image: ghcr.io/bluewave-labs/checkmate-backend:v3.2.0 port: 52345 protocol: http + # false: API only schedules, the worker tier processes jobs (requires workers deployed). + # true: API also processes jobs (all-in-one; no separate worker needed). + queuePrimaryProcesses: false ingress: enabled: true host: change_me @@ -31,8 +34,8 @@ server: # cert-manager.io/cluster-issuer: "letsencrypt-prod" tls: enabled: false - # secretName: {{ .Release.Name }}-server-tls # Optional; defaults to -server-tls if omitted - # Note: when enabling TLS, also set server.protocol: https and add + # secretName: {{ .Release.Name }}-api-tls # Optional; defaults to -api-tls if omitted + # Note: when enabling TLS, also set api.protocol: https and add # a cert-manager issuer annotation (e.g. cert-manager.io/cluster-issuer: "letsencrypt-prod"). # The secret will be automatically created by cert-manager when using the cert-manager.io/cluster-issuer annotation @@ -51,4 +54,4 @@ secrets: persistence: mongo: size: 5Gi - storageClass: "" \ No newline at end of file + storageClass: "" From a39792cf0b0a908676e8e4895333b9daa6e146dc Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 29 Jun 2026 14:14:42 -0700 Subject: [PATCH 05/12] add readiness and liveness checks to API --- .../checkmate/templates/api/deployment.yaml | 22 ++++++++++++++++++- charts/helm/checkmate/values.yaml | 12 ++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/charts/helm/checkmate/templates/api/deployment.yaml b/charts/helm/checkmate/templates/api/deployment.yaml index 63bafc625..ec2e05d99 100644 --- a/charts/helm/checkmate/templates/api/deployment.yaml +++ b/charts/helm/checkmate/templates/api/deployment.yaml @@ -24,10 +24,30 @@ spec: - name: api image: {{ .Values.api.image }} ports: - - containerPort: {{ .Values.api.port }} + - name: http + containerPort: {{ .Values.api.port }} envFrom: - secretRef: name: checkmate-secrets + # Probes hit the Express /api/v1/health endpoint — proves the HTTP server is responsive. + # NOTE: this does NOT detect a wedged job loop when queuePrimaryProcesses=true (the API is + # also a worker). Worker-aware health for that mode is a follow-up — see worker-health-checks.md §7.5. + livenessProbe: + httpGet: + path: /api/v1/health + port: http + initialDelaySeconds: {{ .Values.api.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.api.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.api.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.api.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: /api/v1/health + port: http + initialDelaySeconds: {{ .Values.api.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.api.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.api.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.api.probes.readiness.failureThreshold }} {{- with .Values.api.resources }} resources: {{- tpl ( . | toYaml) $ | nindent 12 }} diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml index 900159b49..42c98f73c 100644 --- a/charts/helm/checkmate/values.yaml +++ b/charts/helm/checkmate/values.yaml @@ -24,6 +24,18 @@ api: # false: API only schedules, the worker tier processes jobs (requires workers deployed). # true: API also processes jobs (all-in-one; no separate worker needed). queuePrimaryProcesses: false + # Liveness/readiness probes hit GET /api/v1/health on the api port (proves the HTTP server is up). + probes: + liveness: + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 ingress: enabled: true host: change_me From 754b0af3a3e610fa95691e96907bf018a0695660 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 30 Jun 2026 09:08:01 -0700 Subject: [PATCH 06/12] update helm charts --- charts/helm/checkmate/INSTALLATION.md | 65 +++++++++++++++++++ .../checkmate/templates/api/deployment.yaml | 7 +- .../templates/client/deployment.yaml | 1 + .../templates/db/mongodb-statefulsets.yaml | 5 +- .../helm/checkmate/templates/prechecks.yaml | 5 ++ .../templates/worker/deployment.yaml | 65 +++++++++++++++++++ .../templates/worker/scaledobject.yaml | 30 +++++++++ charts/helm/checkmate/values.yaml | 41 +++++++++++- 8 files changed, 213 insertions(+), 6 deletions(-) create mode 100644 charts/helm/checkmate/templates/worker/deployment.yaml create mode 100644 charts/helm/checkmate/templates/worker/scaledobject.yaml diff --git a/charts/helm/checkmate/INSTALLATION.md b/charts/helm/checkmate/INSTALLATION.md index ae0f36b8b..c0528b66b 100644 --- a/charts/helm/checkmate/INSTALLATION.md +++ b/charts/helm/checkmate/INSTALLATION.md @@ -42,6 +42,71 @@ kubectl get svc Once all pods are `Running` and `Ready`, you can access Checkmate via the configured ingress hosts. +## Scaling: the worker tier & autoscaling + +By default the chart runs a single all-in-one API pod that both schedules **and** processes monitoring +jobs (`worker.enabled: false`). For larger deployments you can split processing onto a dedicated, +horizontally-scalable worker tier. + +### Topology + +| `worker.enabled` | API (`checkmate-api`) | Worker (`checkmate-worker`) | +| --- | --- | --- | +| `false` (default) | schedules **and** processes jobs (`QUEUE_PRIMARY_PROCESSES=true`) | not deployed | +| `true` | schedules + serves the API only (`QUEUE_PRIMARY_PROCESSES=false`, derived) | processes all jobs | + +`QUEUE_PRIMARY_PROCESSES` is **derived** from `worker.enabled` — you never set it directly, so the API +and worker tier can never both process (or both ignore) the queue. + +### Enabling the worker tier + +```yaml +worker: + enabled: true + replicas: 2 # used only when autoscaling.enabled = false +``` + +The worker takes **no inbound traffic** (no Service): the kubelet probes pod IPs directly on +`HEALTH_PORT` (`/livez`, `/readyz`), and graceful shutdown drains in-flight jobs before exit. + +> **Invariant:** `worker.terminationGracePeriodSeconds` (default `30`) **must stay greater than the +> server drain timeout (25s)**. Lower it and Kubernetes SIGKILLs workers mid-drain, losing in-flight +> checks. + +> **Caveat:** with the worker tier enabled the API no longer processes jobs, so if **all** workers are +> down nothing processes. The `autoscaling.minReplicaCount: 1` floor plus liveness restarts mitigate +> this; full failover to the API would need leader election (not supported). + +### Autoscaling with KEDA + +Backlog-driven autoscaling is **off by default** and requires the +[KEDA operator](https://keda.sh/) installed in-cluster. Enable it with: + +```yaml +worker: + enabled: true + autoscaling: + enabled: true + minReplicaCount: 1 # processing floor — never 0, or checks silently stall + maxReplicaCount: 10 + backlogPerReplica: 50 # target due-job backlog per worker + mongo: + dbName: uptime_db # MUST match the database in DB_CONNECTION_STRING +``` + +A KEDA `ScaledObject` queries MongoDB directly for the due-check backlog and scales the worker +Deployment between `minReplicaCount` and `maxReplicaCount`. When autoscaling is on, the Deployment +omits `replicas` so it doesn't fight the HPA KEDA creates. Make sure `autoscaling.mongo.dbName` +matches the database name in your `DB_CONNECTION_STRING`, or the scaler counts the wrong DB (always 0 +backlog → never scales up). + +#### Fallback: Prometheus-adapter HPA (not shipped) + +If your KEDA/Mongo versions reject the `$$NOW`/`$toLong` query, or you already run Prometheus, scrape +the worker's `/metrics` (`checkmate_worker_due_backlog`), expose it via prometheus-adapter, and target +it with a standard `HorizontalPodAutoscaler`. The manifest is left to the operator; the app computes +`now` in code on this path, so the query coercion is unnecessary. + ## Enabling TLS/HTTPS with cert-manager If you have [cert-manager](https://cert-manager.io/) installed in your cluster, you can enable automatic TLS certificate provisioning using Let's Encrypt or other certificate issuers. diff --git a/charts/helm/checkmate/templates/api/deployment.yaml b/charts/helm/checkmate/templates/api/deployment.yaml index ec2e05d99..8433ecd58 100644 --- a/charts/helm/checkmate/templates/api/deployment.yaml +++ b/charts/helm/checkmate/templates/api/deployment.yaml @@ -23,6 +23,7 @@ spec: containers: - name: api image: {{ .Values.api.image }} + imagePullPolicy: {{ .Values.api.imagePullPolicy }} ports: - name: http containerPort: {{ .Values.api.port }} @@ -57,5 +58,7 @@ spec: value: "{{ .Values.client.protocol }}://{{ .Values.client.ingress.host }}" - name: QUEUE_MODE value: "primary" - - name: QUEUE_PRIMARY_PROCESSES # false: API schedules only, worker tier processes jobs - value: {{ .Values.api.queuePrimaryProcesses | quote }} + # Derived: when a worker tier is deployed the API only schedules (false); otherwise it is + # all-in-one and processes jobs itself (true). Keeps the two from ever competing for jobs. + - name: QUEUE_PRIMARY_PROCESSES + value: {{ not .Values.worker.enabled | quote }} diff --git a/charts/helm/checkmate/templates/client/deployment.yaml b/charts/helm/checkmate/templates/client/deployment.yaml index b59d3a4a9..2919dbd4d 100644 --- a/charts/helm/checkmate/templates/client/deployment.yaml +++ b/charts/helm/checkmate/templates/client/deployment.yaml @@ -23,6 +23,7 @@ spec: containers: - name: client image: {{ .Values.client.image }} + imagePullPolicy: {{ .Values.client.imagePullPolicy }} ports: - containerPort: {{ .Values.client.port }} env: diff --git a/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml b/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml index 8149b77e2..d4e048ee7 100644 --- a/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml +++ b/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml @@ -23,6 +23,7 @@ spec: containers: - name: mongodb image: {{ .Values.mongodb.image }} + imagePullPolicy: {{ .Values.mongodb.imagePullPolicy }} ports: - containerPort: {{ .Values.mongodb.port }} command: ["mongod", "--quiet", "--bind_ip_all"] @@ -37,7 +38,9 @@ spec: - metadata: name: checkmate-mongo-persistent-storage spec: - storageClassName: {{ .Values.persistence.mongo.storageClass | quote }} + {{- with .Values.persistence.mongo.storageClass }} + storageClassName: {{ . | quote }} {{/* omitted when empty so the cluster's default StorageClass applies */}} + {{- end }} accessModes: ["ReadWriteOnce"] resources: requests: diff --git a/charts/helm/checkmate/templates/prechecks.yaml b/charts/helm/checkmate/templates/prechecks.yaml index 4cd337db3..b8ff2ef11 100644 --- a/charts/helm/checkmate/templates/prechecks.yaml +++ b/charts/helm/checkmate/templates/prechecks.yaml @@ -45,4 +45,9 @@ {{- fail "api.ingress.tls.enabled is true but no cert-manager issuer annotation found. Add 'cert-manager.io/cluster-issuer' or 'cert-manager.io/issuer'." }} {{- end }} +{{/* Worker grace period must exceed the server drain timeout (25s) or workers are SIGKILLed mid-drain */}} +{{- if and .Values.worker.enabled (le (int .Values.worker.terminationGracePeriodSeconds) 25) }} + {{- fail "worker.terminationGracePeriodSeconds must be greater than 25 (the server drain timeout) or workers are killed mid-drain" }} +{{- end }} + {{/* Secret name can be omitted; we default to -client|api-tls in templates */}} diff --git a/charts/helm/checkmate/templates/worker/deployment.yaml b/charts/helm/checkmate/templates/worker/deployment.yaml new file mode 100644 index 000000000..a4a76b541 --- /dev/null +++ b/charts/helm/checkmate/templates/worker/deployment.yaml @@ -0,0 +1,65 @@ +{{- if .Values.worker.enabled }} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: checkmate-worker +spec: + {{- if not .Values.worker.autoscaling.enabled }} + replicas: {{ .Values.worker.replicas }} # omitted when autoscaling owns the replica count + {{- end }} + selector: + matchLabels: + app: checkmate-worker + template: + metadata: + labels: + app: checkmate-worker + spec: + terminationGracePeriodSeconds: {{ .Values.worker.terminationGracePeriodSeconds }} + {{- with .Values.worker.affinity }} + affinity: + {{- tpl ( . | toYaml) $ | nindent 8 }} + {{- end }} + {{- with .Values.worker.tolerations }} + tolerations: + {{- tpl ( . | toYaml) $ | nindent 8 }} + {{- end }} + containers: + - name: worker + image: {{ .Values.worker.image | default .Values.api.image }} + imagePullPolicy: {{ .Values.worker.imagePullPolicy | default .Values.api.imagePullPolicy }} + ports: + - name: health + containerPort: {{ .Values.worker.healthPort }} + envFrom: + - secretRef: + name: checkmate-secrets # DB_CONNECTION_STRING (+ JWT_SECRET/CLIENT_HOST for validateEnv) + env: + # Secret default CLIENT_HOST is the non-URL "change_me"; override like the API so validateEnv passes. + - name: CLIENT_HOST + value: "{{ .Values.client.protocol }}://{{ .Values.client.ingress.host }}" + - name: QUEUE_MODE + value: "worker" + - name: HEALTH_PORT + value: {{ .Values.worker.healthPort | quote }} + livenessProbe: + httpGet: + path: /livez + port: health + initialDelaySeconds: {{ .Values.worker.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.worker.probes.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.worker.probes.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.worker.probes.liveness.failureThreshold }} + readinessProbe: + httpGet: + path: /readyz + port: health + initialDelaySeconds: {{ .Values.worker.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.worker.probes.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.worker.probes.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.worker.probes.readiness.failureThreshold }} + {{- with .Values.worker.resources }} + resources: + {{- tpl ( . | toYaml) $ | nindent 12 }} + {{- end }} +{{- end }} diff --git a/charts/helm/checkmate/templates/worker/scaledobject.yaml b/charts/helm/checkmate/templates/worker/scaledobject.yaml new file mode 100644 index 000000000..09fc9a8a3 --- /dev/null +++ b/charts/helm/checkmate/templates/worker/scaledobject.yaml @@ -0,0 +1,30 @@ +{{- if and .Values.worker.enabled .Values.worker.autoscaling.enabled }} +apiVersion: keda.sh/v1alpha1 +kind: ScaledObject +metadata: + name: checkmate-worker +spec: + scaleTargetRef: + name: checkmate-worker + minReplicaCount: {{ .Values.worker.autoscaling.minReplicaCount }} # floor: never 0 — a cold gap stops checks + maxReplicaCount: {{ .Values.worker.autoscaling.maxReplicaCount }} + cooldownPeriod: {{ .Values.worker.autoscaling.cooldownPeriod }} + advanced: + horizontalPodAutoscalerConfig: + behavior: + scaleDown: + stabilizationWindowSeconds: {{ .Values.worker.autoscaling.stabilizationWindowSeconds }} + triggers: + - type: mongodb + metadata: + dbName: {{ .Values.worker.autoscaling.mongo.dbName | quote }} + collection: {{ .Values.worker.autoscaling.mongo.collection | quote }} + # The Phase 2 countDueBacklog predicate, time-relativized with $$NOW (epoch-ms via $toLong): + query: >- + { "type": "check", "isActive": true, + "$expr": { "$lte": ["$nextScheduledAt", { "$toLong": "$$NOW" }] }, + "$or": [ { "lockedUntil": null }, + { "$expr": { "$lt": ["$lockedUntil", { "$toLong": "$$NOW" }] } } ] } + queryValue: {{ .Values.worker.autoscaling.backlogPerReplica | quote }} + connectionStringFromEnv: DB_CONNECTION_STRING # resolved from the worker's checkmate-secrets +{{- end }} diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml index 42c98f73c..7f8fbc65d 100644 --- a/charts/helm/checkmate/values.yaml +++ b/charts/helm/checkmate/values.yaml @@ -1,5 +1,6 @@ client: image: ghcr.io/bluewave-labs/checkmate-client:v3.2.0 + imagePullPolicy: IfNotPresent # IfNotPresent so kind-loaded :latest images are used (not pulled) port: 80 protocol: http ingress: @@ -19,11 +20,9 @@ client: api: image: ghcr.io/bluewave-labs/checkmate-backend:v3.2.0 + imagePullPolicy: IfNotPresent # IfNotPresent so kind-loaded :latest images are used (not pulled) port: 52345 protocol: http - # false: API only schedules, the worker tier processes jobs (requires workers deployed). - # true: API also processes jobs (all-in-one; no separate worker needed). - queuePrimaryProcesses: false # Liveness/readiness probes hit GET /api/v1/health on the api port (proves the HTTP server is up). probes: liveness: @@ -53,8 +52,44 @@ api: mongodb: image: ghcr.io/bluewave-labs/checkmate-mongo:v3.2.0 + imagePullPolicy: IfNotPresent # IfNotPresent so kind-loaded :latest images are used (not pulled) port: 27017 +worker: + # Off by default; existing installs are unchanged. When true, a dedicated worker processes all + # jobs and the API stops processing (QUEUE_PRIMARY_PROCESSES is derived as false). When false, the + # API is all-in-one and processes jobs itself. + enabled: false + image: "" # defaults to api.image when empty, same as API image + imagePullPolicy: "" # defaults to api.imagePullPolicy when empty + replicas: 1 # used only when autoscaling.enabled = false + healthPort: 52346 # HEALTH_PORT; serves /livez /readyz /metrics + terminationGracePeriodSeconds: 30 # MUST stay > the server drain timeout (25s) or workers are killed mid-drain + resources: {} + affinity: {} + tolerations: {} + probes: + liveness: # /livez — failureThreshold>=3 so one slow tick never restarts mid-job + initialDelaySeconds: 10 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + readiness: # /readyz — flips to 503 on drain + initialDelaySeconds: 5 + periodSeconds: 10 + timeoutSeconds: 3 + failureThreshold: 3 + autoscaling: + enabled: false # requires the KEDA operator installed in-cluster + minReplicaCount: 1 # processing floor — never 0 (a cold gap silently stops checks) + maxReplicaCount: 10 + backlogPerReplica: 50 # KEDA queryValue: target due-backlog per worker + cooldownPeriod: 300 # seconds before scaling an idle target down + stabilizationWindowSeconds: 300 # damp the interval-boundary herd + mongo: + dbName: uptime_db # must match DB_CONNECTION_STRING's database + collection: jobs + secrets: JWT_SECRET: change_me DB_CONNECTION_STRING: mongodb://checkmate-mongodb:27017/uptime_db From c0e33eb4faae150624a325d11403f9712374838e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 30 Jun 2026 09:08:20 -0700 Subject: [PATCH 07/12] skip general rate limiter on dev mode --- server/src/api/middleware/rateLimiter.ts | 16 +++++++++------- server/src/app.ts | 3 ++- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/server/src/api/middleware/rateLimiter.ts b/server/src/api/middleware/rateLimiter.ts index 99655149d..839f9377d 100644 --- a/server/src/api/middleware/rateLimiter.ts +++ b/server/src/api/middleware/rateLimiter.ts @@ -1,12 +1,14 @@ import rateLimit from "express-rate-limit"; -export const generalApiLimiter = rateLimit({ - windowMs: 60 * 1000, - limit: 600, - standardHeaders: true, - legacyHeaders: false, - ipv6Subnet: 64, -}); +export const generalApiLimiter = (devMode: boolean) => + rateLimit({ + windowMs: 60 * 1000, + limit: 600, + standardHeaders: true, + legacyHeaders: false, + ipv6Subnet: 64, + skip: () => devMode, + }); export const authApiLimiter = rateLimit({ windowMs: 60 * 1000, diff --git a/server/src/app.ts b/server/src/app.ts index 94a122137..7cb4b9510 100644 --- a/server/src/app.ts +++ b/server/src/app.ts @@ -38,7 +38,8 @@ export const createApp = ({ }; const publicStatusPageCorsOrigin = createStatusPageCorsOrigin(allowedOrigin, services.statusPagesRepository); - app.use(generalApiLimiter); + const devMode = envSettings.nodeEnv === "development"; + app.use(generalApiLimiter(devMode)); app.use((req, res, next) => { const corsOptions = isPublicStatusPageApiPath(req.method, req.path) From 8f657dcd36cc8c8f89b5e493307ff9cf125b4038 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 30 Jun 2026 10:51:21 -0700 Subject: [PATCH 08/12] fix mongo namespace --- charts/helm/checkmate/templates/secrets.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/charts/helm/checkmate/templates/secrets.yaml b/charts/helm/checkmate/templates/secrets.yaml index af104e7d1..9c99395f4 100644 --- a/charts/helm/checkmate/templates/secrets.yaml +++ b/charts/helm/checkmate/templates/secrets.yaml @@ -4,6 +4,14 @@ {{- fail "secrets.JWT_SECRET must be overridden and cannot be 'change_me'" }} {{- end }} +{{- /* + The bundled Mongo service ("checkmate-mongodb") lives in .Release.Namespace. App pods reach it + by bare name, but the KEDA operator runs in its own namespace and a bare name is NXDOMAIN there, + so worker autoscaling fails to query the backlog. Qualify the in-cluster host to its FQDN here so + the one connection string resolves from every namespace. External/already-qualified hosts are not + matched (the char after the host name is "." for an FQDN, which the pattern excludes). +*/}} +{{- $mongoFqdn := printf "checkmate-mongodb.%s.svc.cluster.local" .Release.Namespace }} apiVersion: v1 kind: Secret metadata: @@ -11,5 +19,9 @@ metadata: type: Opaque stringData: {{- range $key, $value := $secrets }} +{{- if eq $key "DB_CONNECTION_STRING" }} + {{ $key }}: {{ regexReplaceAll "(//|@)checkmate-mongodb([:/,]|$)" $value (printf "${1}%s${2}" $mongoFqdn) | quote }} +{{- else }} {{ $key }}: {{ $value | quote }} +{{- end }} {{- end }} \ No newline at end of file From 0ec77a5ab19e8106ee6598b44af6ee5f4f5231a2 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 30 Jun 2026 16:58:52 -0700 Subject: [PATCH 09/12] add alias for backward compat --- charts/helm/checkmate/INSTALLATION.md | 45 +++++++++++++++++++ charts/helm/checkmate/templates/_helpers.tpl | 16 +++++++ .../checkmate/templates/api/deployment.yaml | 29 ++++++------ .../helm/checkmate/templates/api/ingress.yaml | 19 ++++---- .../helm/checkmate/templates/api/service.yaml | 3 +- .../templates/client/deployment.yaml | 3 +- .../checkmate/templates/client/nginx-cm.yaml | 7 +-- .../helm/checkmate/templates/prechecks.yaml | 13 +++--- .../templates/worker/deployment.yaml | 5 ++- 9 files changed, 104 insertions(+), 36 deletions(-) create mode 100644 charts/helm/checkmate/templates/_helpers.tpl diff --git a/charts/helm/checkmate/INSTALLATION.md b/charts/helm/checkmate/INSTALLATION.md index c0528b66b..7177da86a 100644 --- a/charts/helm/checkmate/INSTALLATION.md +++ b/charts/helm/checkmate/INSTALLATION.md @@ -42,6 +42,51 @@ kubectl get svc Once all pods are `Running` and `Ready`, you can access Checkmate via the configured ingress hosts. +## Upgrading from a previous release + +This release renames the `server.*` value block to `api.*` and drops the (unused) bundled Redis. +Read this before running `helm upgrade` on an existing install. + +### `server.*` → `api.*` (values rename) + +The value block formerly named `server` is now `api` (`server.image` → `api.image`, +`server.ingress.host` → `api.ingress.host`, `server.protocol` → `api.protocol`, `server.resources`, +`server.affinity`, `server.tolerations`, `server.ingress.*`, etc.). + +**Your existing `server.*` overrides keep working** — the chart merges any legacy `server.*` values on +top of the `api.*` defaults (legacy wins where set), so an unchanged values file upgrades cleanly. The +shim is transitional; migrate your overrides to `api.*` at your convenience and drop the `server:` +block. If you set **both** `server.*` and `api.*` for the same field, the legacy `server.*` value wins — +don't mix them. + +### Resource renames cause a replace, not a rename + +The Kubernetes objects are renamed alongside the values: + +| Old object | New object | +| --- | --- | +| Deployment/Service `checkmate-server` | `checkmate-api` | +| Ingress `checkmate-server-ingress` | `checkmate-api-ingress` | +| ConfigMap `checkmate-server-nginx-cm` | `checkmate-client-nginx-cm` | + +Helm deletes the old objects and creates the new ones on upgrade. Expect a brief API restart and a +**new ClusterIP** for the API Service — update anything outside the chart that referenced the +`checkmate-server` Service by name. MongoDB data (its PVC) is untouched. + +### Redis removed + +The bundled Redis StatefulSet/Service and the `redis.*` / `persistence.redis.*` values are gone — the +scheduler runs in-process and never used them. Default installs (`redis.enabled: false`) are +unaffected. If you had explicitly set `redis.enabled: true`, **the Redis StatefulSet and its PVC are +deleted on upgrade** — back up anything you stored there first. Leftover `redis:` / `persistence.redis:` +keys in your values file are harmless (ignored). + +### New secret defaults + +`NODE_ENV: production`, `LOG_LEVEL: info`, and `TOKEN_TTL: 99d` are now part of the default `secrets` +block and merge into your install unless you override them. Set them explicitly if you relied on +different values (e.g. a non-production `NODE_ENV`). + ## Scaling: the worker tier & autoscaling By default the chart runs a single all-in-one API pod that both schedules **and** processes monitoring diff --git a/charts/helm/checkmate/templates/_helpers.tpl b/charts/helm/checkmate/templates/_helpers.tpl new file mode 100644 index 000000000..a65766ab8 --- /dev/null +++ b/charts/helm/checkmate/templates/_helpers.tpl @@ -0,0 +1,16 @@ +{{- /* +Back-compat shim: the `server.*` value block was renamed to `api.*`. Existing installs that +still pass a legacy `server:` block keep working — this returns the effective API values with any +legacy `server.*` overrides merged on top of the `api.*` defaults (legacy wins where set). + +New installs have no `server:` key, so this is a no-op deepCopy of `.Values.api`. Consume it as: + {{- $api := include "checkmate.api" . | fromYaml -}} +then reference `$api.*` instead of `.Values.api.*`. +*/}} +{{- define "checkmate.api" -}} +{{- $api := deepCopy .Values.api -}} +{{- with .Values.server -}} +{{- $api = mergeOverwrite $api (deepCopy .) -}} +{{- end -}} +{{- $api | toYaml -}} +{{- end -}} diff --git a/charts/helm/checkmate/templates/api/deployment.yaml b/charts/helm/checkmate/templates/api/deployment.yaml index 8433ecd58..542c6d0af 100644 --- a/charts/helm/checkmate/templates/api/deployment.yaml +++ b/charts/helm/checkmate/templates/api/deployment.yaml @@ -1,3 +1,4 @@ +{{- $api := include "checkmate.api" . | fromYaml -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -12,21 +13,21 @@ spec: labels: app: checkmate-api spec: - {{- with .Values.api.affinity }} + {{- with $api.affinity }} affinity: {{- tpl ( . | toYaml) $ | nindent 8 }} {{- end }} - {{- with .Values.api.tolerations }} + {{- with $api.tolerations }} tolerations: {{- tpl ( . | toYaml) $ | nindent 8 }} {{- end }} containers: - name: api - image: {{ .Values.api.image }} - imagePullPolicy: {{ .Values.api.imagePullPolicy }} + image: {{ $api.image }} + imagePullPolicy: {{ $api.imagePullPolicy }} ports: - name: http - containerPort: {{ .Values.api.port }} + containerPort: {{ $api.port }} envFrom: - secretRef: name: checkmate-secrets @@ -37,19 +38,19 @@ spec: httpGet: path: /api/v1/health port: http - initialDelaySeconds: {{ .Values.api.probes.liveness.initialDelaySeconds }} - periodSeconds: {{ .Values.api.probes.liveness.periodSeconds }} - timeoutSeconds: {{ .Values.api.probes.liveness.timeoutSeconds }} - failureThreshold: {{ .Values.api.probes.liveness.failureThreshold }} + initialDelaySeconds: {{ $api.probes.liveness.initialDelaySeconds }} + periodSeconds: {{ $api.probes.liveness.periodSeconds }} + timeoutSeconds: {{ $api.probes.liveness.timeoutSeconds }} + failureThreshold: {{ $api.probes.liveness.failureThreshold }} readinessProbe: httpGet: path: /api/v1/health port: http - initialDelaySeconds: {{ .Values.api.probes.readiness.initialDelaySeconds }} - periodSeconds: {{ .Values.api.probes.readiness.periodSeconds }} - timeoutSeconds: {{ .Values.api.probes.readiness.timeoutSeconds }} - failureThreshold: {{ .Values.api.probes.readiness.failureThreshold }} - {{- with .Values.api.resources }} + initialDelaySeconds: {{ $api.probes.readiness.initialDelaySeconds }} + periodSeconds: {{ $api.probes.readiness.periodSeconds }} + timeoutSeconds: {{ $api.probes.readiness.timeoutSeconds }} + failureThreshold: {{ $api.probes.readiness.failureThreshold }} + {{- with $api.resources }} resources: {{- tpl ( . | toYaml) $ | nindent 12 }} {{- end }} diff --git a/charts/helm/checkmate/templates/api/ingress.yaml b/charts/helm/checkmate/templates/api/ingress.yaml index f6b9db114..ae60889f2 100644 --- a/charts/helm/checkmate/templates/api/ingress.yaml +++ b/charts/helm/checkmate/templates/api/ingress.yaml @@ -1,11 +1,12 @@ -{{- if .Values.api.ingress.enabled }} +{{- $api := include "checkmate.api" . | fromYaml -}} +{{- if $api.ingress.enabled }} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: checkmate-api-ingress - {{- if .Values.api.ingress.annotations }} + {{- if $api.ingress.annotations }} annotations: - {{- range $key, $value := .Values.api.ingress.annotations }} + {{- range $key, $value := $api.ingress.annotations }} {{ $key }}: {{ $value | quote }} {{- end }} {{- end }} @@ -17,15 +18,15 @@ metadata: #nginx.ingress.kubernetes.io/cors-allow-headers: "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization" #nginx.ingress.kubernetes.io/cors-allow-credentials: "true"*/}} spec: - ingressClassName: {{ .Values.api.ingress.className }} - {{- if .Values.api.ingress.tls.enabled }} + ingressClassName: {{ $api.ingress.className }} + {{- if $api.ingress.tls.enabled }} tls: - hosts: - - {{ .Values.api.ingress.host }} - secretName: {{ default (printf "%s-api-tls" .Release.Name) .Values.api.ingress.tls.secretName }} + - {{ $api.ingress.host }} + secretName: {{ default (printf "%s-api-tls" .Release.Name) $api.ingress.tls.secretName }} {{- end }} rules: - - host: {{ .Values.api.ingress.host }} + - host: {{ $api.ingress.host }} http: paths: - path: /api/v1 @@ -34,5 +35,5 @@ spec: service: name: checkmate-api port: - number: {{ .Values.api.port }} + number: {{ $api.port }} {{- end }} diff --git a/charts/helm/checkmate/templates/api/service.yaml b/charts/helm/checkmate/templates/api/service.yaml index d172c72e8..dfea901c4 100644 --- a/charts/helm/checkmate/templates/api/service.yaml +++ b/charts/helm/checkmate/templates/api/service.yaml @@ -1,3 +1,4 @@ +{{- $api := include "checkmate.api" . | fromYaml -}} apiVersion: v1 kind: Service metadata: @@ -6,4 +7,4 @@ spec: selector: app: checkmate-api ports: - - port: {{ .Values.api.port }} + - port: {{ $api.port }} diff --git a/charts/helm/checkmate/templates/client/deployment.yaml b/charts/helm/checkmate/templates/client/deployment.yaml index 2919dbd4d..006ccfaa1 100644 --- a/charts/helm/checkmate/templates/client/deployment.yaml +++ b/charts/helm/checkmate/templates/client/deployment.yaml @@ -1,3 +1,4 @@ +{{- $api := include "checkmate.api" . | fromYaml -}} apiVersion: apps/v1 kind: Deployment metadata: @@ -28,7 +29,7 @@ spec: - containerPort: {{ .Values.client.port }} env: - name: UPTIME_APP_API_BASE_URL - value: "{{ .Values.api.protocol }}://{{ .Values.api.ingress.host }}/api/v1" + value: "{{ $api.protocol }}://{{ $api.ingress.host }}/api/v1" - name: UPTIME_APP_CLIENT_HOST value: "{{ .Values.client.protocol }}://{{ .Values.client.ingress.host }}" volumeMounts: diff --git a/charts/helm/checkmate/templates/client/nginx-cm.yaml b/charts/helm/checkmate/templates/client/nginx-cm.yaml index 95d1ec936..0d23aac0a 100644 --- a/charts/helm/checkmate/templates/client/nginx-cm.yaml +++ b/charts/helm/checkmate/templates/client/nginx-cm.yaml @@ -1,5 +1,6 @@ +{{- $api := include "checkmate.api" . | fromYaml -}} apiVersion: v1 -kind: ConfigMap +kind: ConfigMap metadata: name: checkmate-client-nginx-cm data: @@ -22,7 +23,7 @@ data: } # location /api/ { - # proxy_pass http://{{ .Values.api.ingress.host }}:5000/api/; + # proxy_pass http://{{ $api.ingress.host }}:5000/api/; # proxy_http_version 1.1; # proxy_set_header Host $host; # proxy_set_header X-Real-IP $remote_addr; @@ -31,7 +32,7 @@ data: # } location /api-docs/ { - proxy_pass http://{{ .Values.api.ingress.host }}:5000/api-docs/; + proxy_pass http://{{ $api.ingress.host }}:5000/api-docs/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; diff --git a/charts/helm/checkmate/templates/prechecks.yaml b/charts/helm/checkmate/templates/prechecks.yaml index b8ff2ef11..cc9f6b9a1 100644 --- a/charts/helm/checkmate/templates/prechecks.yaml +++ b/charts/helm/checkmate/templates/prechecks.yaml @@ -1,12 +1,13 @@ +{{- $api := include "checkmate.api" . | fromYaml -}} {{- if eq .Values.client.ingress.host "change_me" }} {{- fail "client.ingress.host must be overridden and not set to 'change_me'" }} {{- end }} -{{- if eq .Values.api.ingress.host "change_me" }} - {{- fail "api.ingress.host must be overridden and not set to 'change_me'" }} +{{- if eq $api.ingress.host "change_me" }} + {{- fail "api.ingress.host (or legacy server.ingress.host) must be overridden and not set to 'change_me'" }} {{- end }} -{{- $apiProtocol := .Values.api.protocol }} +{{- $apiProtocol := $api.protocol }} {{- if not (or (eq $apiProtocol "http") (eq $apiProtocol "https")) }} {{- fail "api.protocol must be either 'http' or 'https'" }} {{- end }} @@ -21,7 +22,7 @@ {{- fail "client.ingress.tls.enabled is true but client.protocol is not 'https'. Set client.protocol: https to avoid mixed content." }} {{- end }} -{{- if and .Values.api.ingress.tls.enabled (ne $apiProtocol "https") }} +{{- if and $api.ingress.tls.enabled (ne $apiProtocol "https") }} {{- fail "api.ingress.tls.enabled is true but api.protocol is not 'https'. Set api.protocol: https to ensure correct API base URL." }} {{- end }} @@ -32,7 +33,7 @@ {{/* Fail early if TLS enabled without cert-manager annotations (cluster-issuer or issuer) */}} {{- $cAnn := .Values.client.ingress.annotations | default dict }} -{{- $aAnn := .Values.api.ingress.annotations | default dict }} +{{- $aAnn := $api.ingress.annotations | default dict }} {{- $clientHasIssuer := or (hasKey $cAnn "cert-manager.io/cluster-issuer") (hasKey $cAnn "cert-manager.io/issuer") }} {{- $apiHasIssuer := or (hasKey $aAnn "cert-manager.io/cluster-issuer") (hasKey $aAnn "cert-manager.io/issuer") }} @@ -41,7 +42,7 @@ {{- fail "client.ingress.tls.enabled is true but no cert-manager issuer annotation found. Add 'cert-manager.io/cluster-issuer' or 'cert-manager.io/issuer'." }} {{- end }} -{{- if and .Values.api.ingress.tls.enabled (not $apiHasIssuer) }} +{{- if and $api.ingress.tls.enabled (not $apiHasIssuer) }} {{- fail "api.ingress.tls.enabled is true but no cert-manager issuer annotation found. Add 'cert-manager.io/cluster-issuer' or 'cert-manager.io/issuer'." }} {{- end }} diff --git a/charts/helm/checkmate/templates/worker/deployment.yaml b/charts/helm/checkmate/templates/worker/deployment.yaml index a4a76b541..1252ee8ea 100644 --- a/charts/helm/checkmate/templates/worker/deployment.yaml +++ b/charts/helm/checkmate/templates/worker/deployment.yaml @@ -1,3 +1,4 @@ +{{- $api := include "checkmate.api" . | fromYaml -}} {{- if .Values.worker.enabled }} apiVersion: apps/v1 kind: Deployment @@ -26,8 +27,8 @@ spec: {{- end }} containers: - name: worker - image: {{ .Values.worker.image | default .Values.api.image }} - imagePullPolicy: {{ .Values.worker.imagePullPolicy | default .Values.api.imagePullPolicy }} + image: {{ .Values.worker.image | default $api.image }} + imagePullPolicy: {{ .Values.worker.imagePullPolicy | default $api.imagePullPolicy }} ports: - name: health containerPort: {{ .Values.worker.healthPort }} From e3d63a1cb4e5144847979d2bf810e446580813f3 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 2 Jul 2026 09:11:26 -0700 Subject: [PATCH 10/12] bump version --- charts/helm/checkmate/Chart.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/helm/checkmate/Chart.yaml b/charts/helm/checkmate/Chart.yaml index 97f320e68..b3908cffb 100644 --- a/charts/helm/checkmate/Chart.yaml +++ b/charts/helm/checkmate/Chart.yaml @@ -2,5 +2,5 @@ apiVersion: v2 name: checkmate-chart description: A Helm chart for Checkmate App type: application -version: 0.1.0 -appVersion: "2.3" +version: 0.2.0 +appVersion: "3.2.0" From ff536f40a2e5cb9971bcac252c3397776c39fb9d Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 2 Jul 2026 09:44:37 -0700 Subject: [PATCH 11/12] use version tag --- charts/helm/checkmate/INSTALLATION.md | 21 +++++++++++++++++++ charts/helm/checkmate/templates/_helpers.tpl | 19 +++++++++++++++++ .../checkmate/templates/api/deployment.yaml | 2 +- .../templates/client/deployment.yaml | 2 +- .../templates/db/mongodb-statefulsets.yaml | 2 +- .../templates/worker/deployment.yaml | 2 +- charts/helm/checkmate/values.yaml | 10 ++++++--- 7 files changed, 51 insertions(+), 7 deletions(-) diff --git a/charts/helm/checkmate/INSTALLATION.md b/charts/helm/checkmate/INSTALLATION.md index 7177da86a..6bc1f3ecd 100644 --- a/charts/helm/checkmate/INSTALLATION.md +++ b/charts/helm/checkmate/INSTALLATION.md @@ -87,6 +87,27 @@ keys in your values file are harmless (ignored). block and merge into your install unless you override them. Set them explicitly if you relied on different values (e.g. a non-production `NODE_ENV`). +### Image tags follow the chart's `appVersion` + +Each tier's `image` value is now the repository **without a tag**; the tag defaults to the chart's +`appVersion`, so the client, api, worker, and mongodb images all move together when you upgrade the +chart. You can still pin per tier: + +```yaml +api: + image: ghcr.io/bluewave-labs/checkmate-backend # repo only + tag: "" # empty → Chart.appVersion; set e.g. "v3.1.0" to pin, or "latest" for a dev tag +``` + +- `.tag` overrides the version for that tier. The `worker` tier inherits `api.tag` (and + `api.image`) unless you set `worker.tag` / `worker.image`. +- If you put a **full `repo:tag`** in `image` (e.g. a legacy `server.image: …:v3.2.0` override), it is + used verbatim and the `tag` field is ignored — so existing pinned values keep working unchanged. + +**Prefer a pinned tag over `latest`.** With the default `imagePullPolicy: IfNotPresent`, `latest` is +cached per node and never refreshed, and `helm rollback` can't move a floating tag — pin a version (or +let it default to `appVersion`) for reproducible, rollback-able deploys. + ## Scaling: the worker tier & autoscaling By default the chart runs a single all-in-one API pod that both schedules **and** processes monitoring diff --git a/charts/helm/checkmate/templates/_helpers.tpl b/charts/helm/checkmate/templates/_helpers.tpl index a65766ab8..69a1b259b 100644 --- a/charts/helm/checkmate/templates/_helpers.tpl +++ b/charts/helm/checkmate/templates/_helpers.tpl @@ -14,3 +14,22 @@ then reference `$api.*` instead of `.Values.api.*`. {{- end -}} {{- $api | toYaml -}} {{- end -}} + +{{- /* +Resolve a full image reference from a repo + optional tag. `appVersion` is the single source of +truth for tags: bumping Chart.appVersion moves every tier at once. Call as: + {{ include "checkmate.image" (dict "image" $repo "tag" $tag "root" $) }} + +If `image` already carries a tag (a ":" in its final path segment) it is used verbatim — this keeps +legacy `server.image: repo:tag` overrides working and lets anyone pin a full ref (e.g. tag "latest"). +A ":" only in the registry segment (a registry port like "reg:5000/x") is ignored, so a tag is still +appended. Otherwise the tag is `tag` if set, else the chart's appVersion. +*/}} +{{- define "checkmate.image" -}} +{{- $lastSegment := .image | splitList "/" | last -}} +{{- if contains ":" $lastSegment -}} +{{- .image -}} +{{- else -}} +{{- printf "%s:%s" .image (.tag | default .root.Chart.AppVersion) -}} +{{- end -}} +{{- end -}} diff --git a/charts/helm/checkmate/templates/api/deployment.yaml b/charts/helm/checkmate/templates/api/deployment.yaml index 542c6d0af..6469d3bdd 100644 --- a/charts/helm/checkmate/templates/api/deployment.yaml +++ b/charts/helm/checkmate/templates/api/deployment.yaml @@ -23,7 +23,7 @@ spec: {{- end }} containers: - name: api - image: {{ $api.image }} + image: {{ include "checkmate.image" (dict "image" $api.image "tag" $api.tag "root" $) }} imagePullPolicy: {{ $api.imagePullPolicy }} ports: - name: http diff --git a/charts/helm/checkmate/templates/client/deployment.yaml b/charts/helm/checkmate/templates/client/deployment.yaml index 006ccfaa1..a98e6a648 100644 --- a/charts/helm/checkmate/templates/client/deployment.yaml +++ b/charts/helm/checkmate/templates/client/deployment.yaml @@ -23,7 +23,7 @@ spec: {{- end }} containers: - name: client - image: {{ .Values.client.image }} + image: {{ include "checkmate.image" (dict "image" .Values.client.image "tag" .Values.client.tag "root" $) }} imagePullPolicy: {{ .Values.client.imagePullPolicy }} ports: - containerPort: {{ .Values.client.port }} diff --git a/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml b/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml index d4e048ee7..16c228c2b 100644 --- a/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml +++ b/charts/helm/checkmate/templates/db/mongodb-statefulsets.yaml @@ -22,7 +22,7 @@ spec: {{- end }} containers: - name: mongodb - image: {{ .Values.mongodb.image }} + image: {{ include "checkmate.image" (dict "image" .Values.mongodb.image "tag" .Values.mongodb.tag "root" $) }} imagePullPolicy: {{ .Values.mongodb.imagePullPolicy }} ports: - containerPort: {{ .Values.mongodb.port }} diff --git a/charts/helm/checkmate/templates/worker/deployment.yaml b/charts/helm/checkmate/templates/worker/deployment.yaml index 1252ee8ea..0b0849455 100644 --- a/charts/helm/checkmate/templates/worker/deployment.yaml +++ b/charts/helm/checkmate/templates/worker/deployment.yaml @@ -27,7 +27,7 @@ spec: {{- end }} containers: - name: worker - image: {{ .Values.worker.image | default $api.image }} + image: {{ include "checkmate.image" (dict "image" (.Values.worker.image | default $api.image) "tag" (.Values.worker.tag | default $api.tag) "root" $) }} imagePullPolicy: {{ .Values.worker.imagePullPolicy | default $api.imagePullPolicy }} ports: - name: health diff --git a/charts/helm/checkmate/values.yaml b/charts/helm/checkmate/values.yaml index 7f8fbc65d..b2126fe20 100644 --- a/charts/helm/checkmate/values.yaml +++ b/charts/helm/checkmate/values.yaml @@ -1,5 +1,6 @@ client: - image: ghcr.io/bluewave-labs/checkmate-client:v3.2.0 + image: ghcr.io/bluewave-labs/checkmate-client + tag: "" # defaults to Chart.appVersion; set a full repo:tag in `image` to pin (e.g. a :latest dev tag) imagePullPolicy: IfNotPresent # IfNotPresent so kind-loaded :latest images are used (not pulled) port: 80 protocol: http @@ -19,7 +20,8 @@ client: # The secret will be automatically created by cert-manager when using the cert-manager.io/cluster-issuer annotation api: - image: ghcr.io/bluewave-labs/checkmate-backend:v3.2.0 + image: ghcr.io/bluewave-labs/checkmate-backend + tag: "" # defaults to Chart.appVersion; set a full repo:tag in `image` to pin (e.g. a :latest dev tag) imagePullPolicy: IfNotPresent # IfNotPresent so kind-loaded :latest images are used (not pulled) port: 52345 protocol: http @@ -51,7 +53,8 @@ api: # The secret will be automatically created by cert-manager when using the cert-manager.io/cluster-issuer annotation mongodb: - image: ghcr.io/bluewave-labs/checkmate-mongo:v3.2.0 + image: ghcr.io/bluewave-labs/checkmate-mongo + tag: "" # defaults to Chart.appVersion; set a full repo:tag in `image` to pin imagePullPolicy: IfNotPresent # IfNotPresent so kind-loaded :latest images are used (not pulled) port: 27017 @@ -61,6 +64,7 @@ worker: # API is all-in-one and processes jobs itself. enabled: false image: "" # defaults to api.image when empty, same as API image + tag: "" # defaults to api.tag (-> Chart.appVersion) when empty imagePullPolicy: "" # defaults to api.imagePullPolicy when empty replicas: 1 # used only when autoscaling.enabled = false healthPort: 52346 # HEALTH_PORT; serves /livez /readyz /metrics From 8671685cf97ebaace8cb61add9dbbc54d31bdd65 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 2 Jul 2026 10:07:42 -0700 Subject: [PATCH 12/12] bump version --- charts/helm/checkmate/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/helm/checkmate/Chart.yaml b/charts/helm/checkmate/Chart.yaml index b3908cffb..c9433f690 100644 --- a/charts/helm/checkmate/Chart.yaml +++ b/charts/helm/checkmate/Chart.yaml @@ -3,4 +3,4 @@ name: checkmate-chart description: A Helm chart for Checkmate App type: application version: 0.2.0 -appVersion: "3.2.0" +appVersion: "v3.8.1" # "v" prefix: published image tags are v-prefixed (ghcr.io/.../checkmate-*:v3.8.1)