Wave 1 indri→ringtail migration: paperless, teslamate, mealie (#363)
Migrate paperless, teslamate, and mealie off the OOM-saturated minikube-indri node onto ringtail k3s, shedding ~1.1 GiB of resident load. Second chain in the indri-k8s decommission after immich. **Containers ported to Nix (default.nix), build-verified on ringtail:** - paperless → wraps nixpkgs paperless-ngx 2.20.15 (pinned unstable); runs as web/worker/beat/consumer - mealie → wraps nixpkgs mealie 3.16.0 (forward 4-minor bump, breaking-change reviewed); single gunicorn, SQLite - teslamate → from-scratch beamPackages mixRelease (not in nixpkgs); erlang_27+elixir_1_18, npm assets, ex_cldr locales pre-fetched **Data:** cold downtime-tolerant cutover. paperless+teslamate postgres dump/restore from quiesced source into a new ringtail blumeops-pg CNPG cluster; mealie SQLite PVC copied. Source DBs untouched until verified (rollback = repoint). **Also:** ringtail blumeops-pg cluster + ExternalSecrets scaffold; fixes pre-existing shower version-check drift. Runbook: docs/how-to/ringtail/migrate-wave1-ringtail.md. Deploy-from-branch + cutover happens before merge; container images rebuilt from main after merge. Reviewed-on: #363
This commit is contained in:
parent
40bd929820
commit
fcac8e5a72
45 changed files with 1422 additions and 445 deletions
26
argocd/apps/mealie-ringtail.yaml
Normal file
26
argocd/apps/mealie-ringtail.yaml
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# Mealie on ringtail k3s.
|
||||
#
|
||||
# Wave-1 indri-k8s decommission. Staging deployment; the minikube `mealie`
|
||||
# app stays in parallel until cutover (copy SQLite PVC, drop the minikube
|
||||
# tailscale ingress, flip Caddy). See [[migrate-wave1-ringtail]].
|
||||
#
|
||||
# Prerequisites:
|
||||
# - external-secrets-ringtail (onepassword-blumeops ClusterSecretStore)
|
||||
# - mealie-data PVC contents copied from minikube at cutover
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: mealie-ringtail
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
|
||||
targetRevision: main
|
||||
path: argocd/manifests/mealie-ringtail
|
||||
destination:
|
||||
server: https://ringtail.tail8d86e.ts.net:6443
|
||||
namespace: mealie
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
28
argocd/apps/paperless-ringtail.yaml
Normal file
28
argocd/apps/paperless-ringtail.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Paperless-ngx on ringtail k3s.
|
||||
#
|
||||
# Wave-1 indri-k8s decommission. Staging deployment; the minikube
|
||||
# `paperless` app stays in parallel until cutover (drop the minikube
|
||||
# tailscale ingress to free the name, then flip Caddy). See
|
||||
# [[migrate-wave1-ringtail]].
|
||||
#
|
||||
# Prerequisites:
|
||||
# - databases-ringtail blumeops-pg (paperless database + role)
|
||||
# - external-secrets-ringtail (onepassword-blumeops ClusterSecretStore)
|
||||
# - sifaka NFS rule granting ringtail access to /volume1/paperless
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: paperless-ringtail
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
|
||||
targetRevision: main
|
||||
path: argocd/manifests/paperless-ringtail
|
||||
destination:
|
||||
server: https://ringtail.tail8d86e.ts.net:6443
|
||||
namespace: paperless
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
28
argocd/apps/teslamate-ringtail.yaml
Normal file
28
argocd/apps/teslamate-ringtail.yaml
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# TeslaMate on ringtail k3s.
|
||||
#
|
||||
# Wave-1 indri-k8s decommission. Staging deployment; the minikube
|
||||
# `teslamate` app stays in parallel until cutover (migrate the teslamate
|
||||
# database, drop the minikube tailscale ingress, flip Caddy). See
|
||||
# [[migrate-wave1-ringtail]].
|
||||
#
|
||||
# Prerequisites:
|
||||
# - databases-ringtail blumeops-pg (teslamate database + role; cube +
|
||||
# earthdistance extensions created by superuser at cutover)
|
||||
# - external-secrets-ringtail (onepassword-blumeops ClusterSecretStore)
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: teslamate-ringtail
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
|
||||
targetRevision: main
|
||||
path: argocd/manifests/teslamate-ringtail
|
||||
destination:
|
||||
server: https://ringtail.tail8d86e.ts.net:6443
|
||||
namespace: teslamate
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
97
argocd/manifests/databases-ringtail/blumeops-pg.yaml
Normal file
97
argocd/manifests/databases-ringtail/blumeops-pg.yaml
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
# PostgreSQL Cluster for blumeops services on ringtail k3s.
|
||||
#
|
||||
# Wave-1 indri-k8s decommission target (see [[migrate-wave1-ringtail]]).
|
||||
# Holds the paperless and teslamate databases migrated off the minikube
|
||||
# blumeops-pg via cold pg_dump/pg_restore at cutover. miniflux + authentik
|
||||
# stay where they are for now (later waves), so this cluster only carries
|
||||
# the wave-1 roles.
|
||||
#
|
||||
# Apps reach this in-cluster at blumeops-pg-rw.databases.svc.cluster.local
|
||||
# — the same name they used on minikube, so teslamate's DATABASE_HOST is
|
||||
# unchanged.
|
||||
#
|
||||
# Database creation is deferred to cutover, mirroring the minikube cluster
|
||||
# (where only the bootstrap database is declared and the rest were created
|
||||
# out-of-band):
|
||||
# - paperless: the bootstrap database below (restored into at cutover).
|
||||
# - teslamate: created at its cutover by the eblume superuser, because the
|
||||
# dump's `earthdistance` extension is untrusted and CREATE EXTENSION
|
||||
# needs superuser. (cube + earthdistance ownership then transferred to
|
||||
# the teslamate role so it can ALTER EXTENSION UPDATE.)
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: blumeops-pg
|
||||
namespace: databases
|
||||
spec:
|
||||
instances: 1
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:18.3
|
||||
|
||||
storage:
|
||||
size: 10Gi
|
||||
storageClass: local-path
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: paperless
|
||||
owner: paperless
|
||||
|
||||
managed:
|
||||
roles:
|
||||
# eblume superuser for admin + privileged restore steps (extensions)
|
||||
- name: eblume
|
||||
login: true
|
||||
superuser: true
|
||||
createdb: true
|
||||
createrole: true
|
||||
connectionLimit: -1
|
||||
ensure: present
|
||||
inherit: true
|
||||
passwordSecret:
|
||||
name: blumeops-pg-eblume
|
||||
# borgmatic read-only user for backups
|
||||
- name: borgmatic
|
||||
login: true
|
||||
connectionLimit: -1
|
||||
ensure: present
|
||||
inherit: true
|
||||
inRoles:
|
||||
- pg_read_all_data
|
||||
passwordSecret:
|
||||
name: blumeops-pg-borgmatic
|
||||
# paperless user (also the bootstrap database owner above; the
|
||||
# managed role sets its password from the 1Password-backed secret)
|
||||
- name: paperless
|
||||
login: true
|
||||
connectionLimit: -1
|
||||
ensure: present
|
||||
inherit: true
|
||||
passwordSecret:
|
||||
name: blumeops-pg-paperless
|
||||
# teslamate user. Extension ownership (cube, earthdistance) is
|
||||
# transferred to this role at cutover so it can ALTER EXTENSION UPDATE.
|
||||
- name: teslamate
|
||||
login: true
|
||||
connectionLimit: -1
|
||||
ensure: present
|
||||
inherit: true
|
||||
passwordSecret:
|
||||
name: blumeops-pg-teslamate
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "500m"
|
||||
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "50"
|
||||
shared_buffers: "128MB"
|
||||
password_encryption: "scram-sha-256"
|
||||
pg_hba:
|
||||
# Password auth from anywhere; network security is via Tailscale.
|
||||
- host all all 0.0.0.0/0 scram-sha-256
|
||||
- host all all ::/0 scram-sha-256
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# ExternalSecret for borgmatic backup user password
|
||||
#
|
||||
# Replaces the manual op inject workflow from secret-borgmatic.yaml.tpl
|
||||
#
|
||||
# 1Password item: "borgmatic" in blumeops vault
|
||||
# Field: "db-password"
|
||||
#
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: blumeops-pg-borgmatic
|
||||
namespace: databases
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: blumeops-pg-borgmatic
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
type: kubernetes.io/basic-auth
|
||||
data:
|
||||
username: borgmatic
|
||||
password: "{{ .password }}"
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: borgmatic
|
||||
property: db-password
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# ExternalSecret for eblume superuser password
|
||||
#
|
||||
# Replaces the manual op inject workflow from secret-eblume.yaml.tpl
|
||||
#
|
||||
# 1Password item: "postgres" in blumeops vault
|
||||
# Field: "password"
|
||||
#
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: blumeops-pg-eblume
|
||||
namespace: databases
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: blumeops-pg-eblume
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
type: kubernetes.io/basic-auth
|
||||
data:
|
||||
username: eblume
|
||||
password: "{{ .password }}"
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: postgres
|
||||
property: password
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
# ExternalSecret for Paperless database user password
|
||||
#
|
||||
# 1Password item: "Paperless (blumeops)" in blumeops vault
|
||||
# Field: "postgresql-password"
|
||||
#
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: blumeops-pg-paperless
|
||||
namespace: databases
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: blumeops-pg-paperless
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
type: kubernetes.io/basic-auth
|
||||
data:
|
||||
username: paperless
|
||||
password: "{{ .password }}"
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: Paperless (blumeops)
|
||||
property: postgresql-password
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
# ExternalSecret for TeslaMate database user password
|
||||
#
|
||||
# Replaces the manual op inject workflow from secret-teslamate.yaml.tpl
|
||||
#
|
||||
# 1Password item: "TeslaMate" in blumeops vault
|
||||
# Field: "db_password"
|
||||
#
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: blumeops-pg-teslamate
|
||||
namespace: databases
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: blumeops-pg-teslamate
|
||||
creationPolicy: Owner
|
||||
template:
|
||||
type: kubernetes.io/basic-auth
|
||||
data:
|
||||
username: teslamate
|
||||
password: "{{ .password }}"
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: TeslaMate
|
||||
property: db_password
|
||||
|
|
@ -7,3 +7,9 @@ resources:
|
|||
- immich-pg.yaml
|
||||
- external-secret-immich-borgmatic.yaml
|
||||
- service-immich-pg-tailscale.yaml
|
||||
# wave-1 indri-k8s decommission: blumeops-pg (paperless + teslamate)
|
||||
- blumeops-pg.yaml
|
||||
- external-secret-eblume.yaml
|
||||
- external-secret-borgmatic.yaml
|
||||
- external-secret-paperless.yaml
|
||||
- external-secret-teslamate.yaml
|
||||
|
|
|
|||
102
argocd/manifests/mealie-ringtail/deployment.yaml
Normal file
102
argocd/manifests/mealie-ringtail/deployment.yaml
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# Mealie on ringtail k3s — Nix image.
|
||||
#
|
||||
# Single gunicorn process (the Nix image's default `mealie-run` entrypoint
|
||||
# runs init_db then gunicorn), serving the prebuilt frontend. DB is SQLite
|
||||
# on the mealie-data PVC; its contents are copied from the minikube PVC at
|
||||
# cutover. See [[migrate-wave1-ringtail]].
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mealie
|
||||
namespace: mealie
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mealie
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mealie
|
||||
spec:
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: mealie
|
||||
image: registry.ops.eblu.me/blumeops/mealie:kustomized
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
env:
|
||||
- name: BASE_URL
|
||||
value: "https://meals.ops.eblu.me"
|
||||
- name: ALLOW_SIGNUP
|
||||
value: "false"
|
||||
- name: TZ
|
||||
value: "America/Los_Angeles"
|
||||
- name: MAX_WORKERS
|
||||
value: "1"
|
||||
- name: WEB_CONCURRENCY
|
||||
value: "1"
|
||||
# OIDC — Authentik (public client, PKCE)
|
||||
- name: OIDC_AUTH_ENABLED
|
||||
value: "true"
|
||||
- name: OIDC_CONFIGURATION_URL
|
||||
value: "https://authentik.ops.eblu.me/application/o/mealie/.well-known/openid-configuration"
|
||||
- name: OIDC_CLIENT_ID
|
||||
value: "mealie"
|
||||
- name: OIDC_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mealie-secrets
|
||||
key: oidc-client-secret
|
||||
- name: OIDC_AUTO_REDIRECT
|
||||
value: "false"
|
||||
- name: OIDC_PROVIDER_NAME
|
||||
value: "Authentik"
|
||||
- name: OIDC_ADMIN_GROUP
|
||||
value: "admins"
|
||||
- name: OIDC_SIGNUP_ENABLED
|
||||
value: "true"
|
||||
- name: OIDC_USER_CLAIM
|
||||
value: "email"
|
||||
# OpenAI — recipe parsing, image OCR, ingredient extraction
|
||||
- name: OPENAI_API_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: mealie-secrets
|
||||
key: openai-api-key
|
||||
- name: OPENAI_MODEL
|
||||
value: "gpt-4o"
|
||||
- name: OPENAI_REQUEST_TIMEOUT
|
||||
value: "120"
|
||||
- name: OPENAI_WORKERS
|
||||
value: "1"
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /app/data
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "1000Mi"
|
||||
cpu: "500m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /api/app/about
|
||||
port: 9000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /api/app/about
|
||||
port: 9000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: mealie-data
|
||||
23
argocd/manifests/mealie-ringtail/external-secret.yaml
Normal file
23
argocd/manifests/mealie-ringtail/external-secret.yaml
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: mealie-secrets
|
||||
namespace: mealie
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: mealie-secrets
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: oidc-client-secret
|
||||
remoteRef:
|
||||
key: "Authentik (blumeops)"
|
||||
property: mealie-client-secret
|
||||
- secretKey: openai-api-key
|
||||
remoteRef:
|
||||
key: "openai (blumeops)"
|
||||
property: credential
|
||||
15
argocd/manifests/mealie-ringtail/kustomization.yaml
Normal file
15
argocd/manifests/mealie-ringtail/kustomization.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: mealie
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- pvc.yaml
|
||||
- ingress-tailscale.yaml
|
||||
- external-secret.yaml
|
||||
|
||||
images:
|
||||
- name: registry.ops.eblu.me/blumeops/mealie
|
||||
newTag: v3.16.0-1d4cbbf-nix
|
||||
14
argocd/manifests/mealie-ringtail/pvc.yaml
Normal file
14
argocd/manifests/mealie-ringtail/pvc.yaml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# SQLite data volume for Mealie on ringtail. Contents copied from the
|
||||
# minikube mealie-data PVC at cutover (recipes, meal plans, uploaded media).
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: mealie-data
|
||||
namespace: mealie
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
13
argocd/manifests/mealie-ringtail/service.yaml
Normal file
13
argocd/manifests/mealie-ringtail/service.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mealie
|
||||
namespace: mealie
|
||||
spec:
|
||||
selector:
|
||||
app: mealie
|
||||
ports:
|
||||
- name: http
|
||||
port: 9000
|
||||
targetPort: 9000
|
||||
protocol: TCP
|
||||
|
|
@ -4,7 +4,9 @@ metadata:
|
|||
name: mealie
|
||||
namespace: mealie
|
||||
spec:
|
||||
replicas: 1
|
||||
# Migrated to ringtail (mealie-ringtail). Scaled to 0; SQLite PVC retained
|
||||
# for rollback until the decommission PR. See [[migrate-wave1-ringtail]].
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mealie
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ resources:
|
|||
- deployment.yaml
|
||||
- service.yaml
|
||||
- pvc.yaml
|
||||
- ingress-tailscale.yaml
|
||||
# ingress removed: name 'meals' handed off to mealie-ringtail at cutover
|
||||
- external-secret.yaml
|
||||
|
||||
images:
|
||||
|
|
|
|||
201
argocd/manifests/paperless-ringtail/deployment.yaml
Normal file
201
argocd/manifests/paperless-ringtail/deployment.yaml
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
# Paperless-ngx on ringtail k3s — Nix image, multi-process.
|
||||
#
|
||||
# The upstream s6 image ran web + worker + scheduler + consumer (and DB
|
||||
# migrations) in one container. The Nix image (containers/paperless/
|
||||
# default.nix) ships the binaries but no supervisor, so we run those as
|
||||
# four containers in one pod, sharing the local data/consume dirs
|
||||
# (emptyDir) and the NFS media volume; redis is colocated so
|
||||
# PAPERLESS_REDIS=localhost works for all. A migrate initContainer runs
|
||||
# DB migrations once before the app containers start.
|
||||
#
|
||||
# DB points in-cluster at the ringtail blumeops-pg (was pg.ops.eblu.me on
|
||||
# indri). PAPERLESS_{DATA_DIR,MEDIA_ROOT,CONSUMPTION_DIR} are set
|
||||
# explicitly because the Nix package does not default to the upstream
|
||||
# /usr/src/paperless paths.
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: paperless
|
||||
namespace: paperless
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: paperless
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: paperless
|
||||
spec:
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
initContainers:
|
||||
# redis as a native sidecar (restartPolicy: Always): starts before
|
||||
# the migrate init and stays running for the app containers, so all
|
||||
# of them reach PAPERLESS_REDIS=localhost:6379.
|
||||
- name: redis
|
||||
image: docker.io/library/redis:kustomized
|
||||
restartPolicy: Always
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
volumeMounts:
|
||||
- name: redis-data
|
||||
mountPath: /data
|
||||
resources:
|
||||
requests:
|
||||
memory: "32Mi"
|
||||
cpu: "10m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
- name: migrate
|
||||
image: registry.ops.eblu.me/blumeops/paperless:kustomized
|
||||
command: ["paperless-ngx", "migrate", "--no-input"]
|
||||
env: &paperless-env
|
||||
- name: PAPERLESS_URL
|
||||
value: "https://paperless.ops.eblu.me"
|
||||
- name: PAPERLESS_REDIS
|
||||
value: "redis://localhost:6379"
|
||||
- name: PAPERLESS_DBHOST
|
||||
value: "blumeops-pg-rw.databases.svc.cluster.local"
|
||||
- name: PAPERLESS_DBPORT
|
||||
value: "5432"
|
||||
- name: PAPERLESS_DBNAME
|
||||
value: "paperless"
|
||||
- name: PAPERLESS_DBUSER
|
||||
value: "paperless"
|
||||
- name: PAPERLESS_DBPASS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-secrets
|
||||
key: db-password
|
||||
# Explicit port to override the k8s-injected PAPERLESS_PORT
|
||||
# (service named 'paperless' would set PAPERLESS_PORT=tcp://...)
|
||||
- name: PAPERLESS_PORT
|
||||
value: "8000"
|
||||
- name: PAPERLESS_DATA_DIR
|
||||
value: "/usr/src/paperless/data"
|
||||
- name: PAPERLESS_MEDIA_ROOT
|
||||
value: "/usr/src/paperless/media"
|
||||
- name: PAPERLESS_CONSUMPTION_DIR
|
||||
value: "/usr/src/paperless/consume"
|
||||
- name: PAPERLESS_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-secrets
|
||||
key: secret-key
|
||||
- name: PAPERLESS_TIME_ZONE
|
||||
value: "America/Los_Angeles"
|
||||
- name: PAPERLESS_OCR_LANGUAGE
|
||||
value: "eng"
|
||||
- name: PAPERLESS_TASK_WORKERS
|
||||
value: "1"
|
||||
- name: PAPERLESS_ADMIN_USER
|
||||
value: "eblume"
|
||||
- name: PAPERLESS_ADMIN_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-secrets
|
||||
key: admin-password
|
||||
- name: PAPERLESS_ADMIN_MAIL
|
||||
value: "blume.erich@gmail.com"
|
||||
- name: PAPERLESS_APPS
|
||||
value: "allauth.socialaccount.providers.openid_connect"
|
||||
- name: PAPERLESS_SOCIALACCOUNT_PROVIDERS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: paperless-secrets
|
||||
key: socialaccount-providers
|
||||
- name: PAPERLESS_SOCIALACCOUNT_ALLOW_SIGNUPS
|
||||
value: "true"
|
||||
- name: PAPERLESS_SOCIAL_AUTO_SIGNUP
|
||||
value: "true"
|
||||
- name: PAPERLESS_ACCOUNT_ALLOW_SIGNUPS
|
||||
value: "false"
|
||||
- name: PAPERLESS_REDIRECT_LOGIN_TO_SSO
|
||||
value: "false"
|
||||
volumeMounts: &paperless-mounts
|
||||
- name: data
|
||||
mountPath: /usr/src/paperless/data
|
||||
- name: media
|
||||
mountPath: /usr/src/paperless/media
|
||||
- name: consume
|
||||
mountPath: /usr/src/paperless/consume
|
||||
containers:
|
||||
- name: web
|
||||
image: registry.ops.eblu.me/blumeops/paperless:kustomized
|
||||
ports:
|
||||
- containerPort: 8000
|
||||
name: http
|
||||
env: *paperless-env
|
||||
volumeMounts: *paperless-mounts
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8000
|
||||
initialDelaySeconds: 60
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
|
||||
- name: worker
|
||||
image: registry.ops.eblu.me/blumeops/paperless:kustomized
|
||||
command: ["celery", "--app", "paperless", "worker", "--loglevel", "INFO"]
|
||||
env: *paperless-env
|
||||
volumeMounts: *paperless-mounts
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
|
||||
- name: beat
|
||||
image: registry.ops.eblu.me/blumeops/paperless:kustomized
|
||||
command: ["celery", "--app", "paperless", "beat", "--loglevel", "INFO"]
|
||||
env: *paperless-env
|
||||
volumeMounts: *paperless-mounts
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "20m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
|
||||
- name: consumer
|
||||
image: registry.ops.eblu.me/blumeops/paperless:kustomized
|
||||
command: ["paperless-ngx", "document_consumer"]
|
||||
env: *paperless-env
|
||||
volumeMounts: *paperless-mounts
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
|
||||
volumes:
|
||||
- name: data
|
||||
emptyDir: {}
|
||||
- name: media
|
||||
persistentVolumeClaim:
|
||||
claimName: paperless-media
|
||||
- name: consume
|
||||
emptyDir: {}
|
||||
- name: redis-data
|
||||
emptyDir:
|
||||
sizeLimit: 1Gi
|
||||
31
argocd/manifests/paperless-ringtail/external-secret.yaml
Normal file
31
argocd/manifests/paperless-ringtail/external-secret.yaml
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: paperless-secrets
|
||||
namespace: paperless
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: paperless-secrets
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: db-password
|
||||
remoteRef:
|
||||
key: "Paperless (blumeops)"
|
||||
property: postgresql-password
|
||||
- secretKey: secret-key
|
||||
remoteRef:
|
||||
key: "Paperless (blumeops)"
|
||||
property: secret-key
|
||||
- secretKey: admin-password
|
||||
remoteRef:
|
||||
key: "Paperless (blumeops)"
|
||||
property: admin-password
|
||||
- secretKey: socialaccount-providers
|
||||
remoteRef:
|
||||
key: "Paperless (blumeops)"
|
||||
property: socialaccount-providers
|
||||
21
argocd/manifests/paperless-ringtail/kustomization.yaml
Normal file
21
argocd/manifests/paperless-ringtail/kustomization.yaml
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: paperless
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- pv-nfs.yaml
|
||||
- pvc.yaml
|
||||
- ingress-tailscale.yaml
|
||||
- external-secret.yaml
|
||||
|
||||
images:
|
||||
- name: registry.ops.eblu.me/blumeops/paperless
|
||||
newTag: v2.20.15-1d4cbbf-nix
|
||||
# amd64 valkey built via nix (the v8.1.7-ecded30 tag without -nix is the
|
||||
# arm64 Alpine build for indri and fails on ringtail with exec format error)
|
||||
- name: docker.io/library/redis
|
||||
newName: registry.ops.eblu.me/blumeops/valkey
|
||||
newTag: v8.1.7-ecded30-nix
|
||||
22
argocd/manifests/paperless-ringtail/pv-nfs.yaml
Normal file
22
argocd/manifests/paperless-ringtail/pv-nfs.yaml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
# NFS PersistentVolume for the Paperless document library, mounted from
|
||||
# ringtail. Same sifaka export (/volume1/paperless) as the minikube PV,
|
||||
# but a distinct PV name so both clusters can declare it during the
|
||||
# parallel-run before cutover.
|
||||
#
|
||||
# Prerequisite: sifaka must have an NFS rule granting ringtail Read/Write
|
||||
# (Squash=No mapping) on the paperless share — the same step done for
|
||||
# immich. See [[sifaka-nfs-from-ringtail]].
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: paperless-media-nfs-pv-ringtail
|
||||
spec:
|
||||
capacity:
|
||||
storage: 500Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
storageClassName: ""
|
||||
nfs:
|
||||
server: sifaka
|
||||
path: /volume1/paperless
|
||||
15
argocd/manifests/paperless-ringtail/pvc.yaml
Normal file
15
argocd/manifests/paperless-ringtail/pvc.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# PersistentVolumeClaim for the Paperless document library on ringtail.
|
||||
# Binds the NFS PV for sifaka:/volume1/paperless.
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: paperless-media
|
||||
namespace: paperless
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
storageClassName: ""
|
||||
volumeName: paperless-media-nfs-pv-ringtail
|
||||
resources:
|
||||
requests:
|
||||
storage: 500Gi
|
||||
13
argocd/manifests/paperless-ringtail/service.yaml
Normal file
13
argocd/manifests/paperless-ringtail/service.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: paperless
|
||||
namespace: paperless
|
||||
spec:
|
||||
selector:
|
||||
app: paperless
|
||||
ports:
|
||||
- name: http
|
||||
port: 8000
|
||||
targetPort: 8000
|
||||
protocol: TCP
|
||||
|
|
@ -4,7 +4,10 @@ metadata:
|
|||
name: paperless
|
||||
namespace: paperless
|
||||
spec:
|
||||
replicas: 1
|
||||
# Migrated to ringtail (paperless-ringtail). Scaled to 0 to prevent
|
||||
# double-writing the now-ringtail-owned database; manifest retained for
|
||||
# rollback until the decommission PR. See [[migrate-wave1-ringtail]].
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: paperless
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ resources:
|
|||
- service.yaml
|
||||
- pv-nfs.yaml
|
||||
- pvc.yaml
|
||||
- ingress-tailscale.yaml
|
||||
# ingress removed: name 'paperless' handed off to paperless-ringtail at cutover
|
||||
- external-secret.yaml
|
||||
|
||||
images:
|
||||
|
|
|
|||
72
argocd/manifests/teslamate-ringtail/deployment.yaml
Normal file
72
argocd/manifests/teslamate-ringtail/deployment.yaml
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
# TeslaMate on ringtail k3s — Nix image.
|
||||
#
|
||||
# The Nix image's Entrypoint waits for postgres, runs migrations
|
||||
# (TeslaMate.Release.migrate), then starts the release — so no command
|
||||
# override is needed. Stateless; all data lives in the teslamate database
|
||||
# on the ringtail blumeops-pg (DATABASE_HOST already an in-cluster name,
|
||||
# unchanged from minikube). See [[migrate-wave1-ringtail]].
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: teslamate
|
||||
namespace: teslamate
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: teslamate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: teslamate
|
||||
spec:
|
||||
securityContext:
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
containers:
|
||||
- name: teslamate
|
||||
image: registry.ops.eblu.me/blumeops/teslamate:kustomized
|
||||
ports:
|
||||
- containerPort: 4000
|
||||
env:
|
||||
- name: DATABASE_USER
|
||||
value: "teslamate"
|
||||
- name: DATABASE_PASS
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: teslamate-db
|
||||
key: password
|
||||
- name: DATABASE_NAME
|
||||
value: "teslamate"
|
||||
- name: DATABASE_HOST
|
||||
value: "blumeops-pg-rw.databases.svc.cluster.local"
|
||||
- name: ENCRYPTION_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: teslamate-encryption
|
||||
key: key
|
||||
- name: DISABLE_MQTT
|
||||
value: "true"
|
||||
- name: CHECK_ORIGIN
|
||||
value: "false"
|
||||
- name: TZ
|
||||
value: "America/Los_Angeles"
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 4000
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 4000
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
25
argocd/manifests/teslamate-ringtail/external-secret-db.yaml
Normal file
25
argocd/manifests/teslamate-ringtail/external-secret-db.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# ExternalSecret for TeslaMate database password
|
||||
#
|
||||
# Replaces the manual op inject workflow from secret-db.yaml.tpl
|
||||
#
|
||||
# 1Password item: "TeslaMate" in blumeops vault
|
||||
# Field: "db_password"
|
||||
#
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: teslamate-db
|
||||
namespace: teslamate
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: teslamate-db
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: password
|
||||
remoteRef:
|
||||
key: TeslaMate
|
||||
property: db_password
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
# ExternalSecret for TeslaMate encryption key
|
||||
#
|
||||
# Replaces the manual op inject workflow from secret-encryption-key.yaml.tpl
|
||||
#
|
||||
# 1Password item: "TeslaMate" in blumeops vault
|
||||
# Field: "api_enc_key"
|
||||
#
|
||||
# This key encrypts Tesla API tokens at rest in the database.
|
||||
#
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: teslamate-encryption
|
||||
namespace: teslamate
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: teslamate-encryption
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: key
|
||||
remoteRef:
|
||||
key: TeslaMate
|
||||
property: api_enc_key
|
||||
15
argocd/manifests/teslamate-ringtail/kustomization.yaml
Normal file
15
argocd/manifests/teslamate-ringtail/kustomization.yaml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
|
||||
namespace: teslamate
|
||||
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress-tailscale.yaml
|
||||
- external-secret-db.yaml
|
||||
- external-secret-encryption-key.yaml
|
||||
|
||||
images:
|
||||
- name: registry.ops.eblu.me/blumeops/teslamate
|
||||
newTag: v3.0.0-191be1b-nix
|
||||
12
argocd/manifests/teslamate-ringtail/service.yaml
Normal file
12
argocd/manifests/teslamate-ringtail/service.yaml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: teslamate
|
||||
namespace: teslamate
|
||||
spec:
|
||||
selector:
|
||||
app: teslamate
|
||||
ports:
|
||||
- port: 4000
|
||||
targetPort: 4000
|
||||
type: ClusterIP
|
||||
|
|
@ -4,7 +4,10 @@ metadata:
|
|||
name: teslamate
|
||||
namespace: teslamate
|
||||
spec:
|
||||
replicas: 1
|
||||
# Migrated to ringtail (teslamate-ringtail). Scaled to 0 to prevent
|
||||
# double-writing the now-ringtail-owned database; manifest retained for
|
||||
# rollback until the decommission PR. See [[migrate-wave1-ringtail]].
|
||||
replicas: 0
|
||||
selector:
|
||||
matchLabels:
|
||||
app: teslamate
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ namespace: teslamate
|
|||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
- ingress-tailscale.yaml
|
||||
# ingress removed: name 'tesla' handed off to teslamate-ringtail at cutover
|
||||
- external-secret-db.yaml
|
||||
- external-secret-encryption-key.yaml
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue