Initial cluster config: NixOS flake + k3s + 5 node definitions
- Multi-host flake.nix (3x N97 x86_64 + 2x RPi4 aarch64) - Shared modules: k3s-cluster, tailscale, meos-gateway - Node configs: n97-1 (control plane), n97-2/n97-3 (Ollama workers), rpi4-1/rpi4-2 (ARM workers) - K8s manifests: Forgejo, Postgres (CNPG), Umami, OpenBrain, MeOS Dashboard, MCP servers - Bootstrap and DB migration scripts - Migration plan doc (7-day phased rollout)
This commit is contained in:
parent
791a9b6721
commit
7b5ffe6b11
21 changed files with 1184 additions and 2 deletions
103
apps/forgejo/deployment.yaml
Normal file
103
apps/forgejo/deployment.yaml
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
# Forgejo on k3s
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: forgejo
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: forgejo-data
|
||||
namespace: forgejo
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: local-path
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: forgejo
|
||||
namespace: forgejo
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: forgejo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: forgejo
|
||||
spec:
|
||||
containers:
|
||||
- name: forgejo
|
||||
image: codeberg.org/forgejo/forgejo:11.0
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
name: http
|
||||
- containerPort: 22
|
||||
name: ssh
|
||||
env:
|
||||
- name: USER_UID
|
||||
value: "1000"
|
||||
- name: USER_GID
|
||||
value: "1000"
|
||||
- name: FORGEJO__database__DB_TYPE
|
||||
value: postgres
|
||||
- name: FORGEJO__database__HOST
|
||||
value: postgres-scobleclaw-rw.database.svc.cluster.local:5432
|
||||
- name: FORGEJO__database__NAME
|
||||
value: forgejo
|
||||
- name: FORGEJO__database__USER
|
||||
value: forgejo
|
||||
- name: FORGEJO__database__PASSWD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: postgres-users
|
||||
key: forgejo-password
|
||||
- name: FORGEJO__server__ROOT_URL
|
||||
value: "https://os.scoble.me/forgejo/"
|
||||
- name: FORGEJO__server__HTTP_PORT
|
||||
value: "3000"
|
||||
- name: FORGEJO__server__DOMAIN
|
||||
value: os.scoble.me
|
||||
- name: FORGEJO__server__DISABLE_SSH
|
||||
value: "false"
|
||||
- name: FORGEJO__server__SSH_PORT
|
||||
value: "2222"
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "1"
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: forgejo-data
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: forgejo
|
||||
namespace: forgejo
|
||||
spec:
|
||||
ports:
|
||||
- name: http
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
- name: ssh
|
||||
port: 22
|
||||
targetPort: 22
|
||||
selector:
|
||||
app: forgejo
|
||||
83
apps/mcp-servers/deployment.yaml
Normal file
83
apps/mcp-servers/deployment.yaml
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
# MCP Servers — lightweight Node.js workloads for ARM worker (rpi4-1)
|
||||
# forgejo-mcp, lean-experiment-mcp, wardrobe-ai, homeschool
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: mcp-servers
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: forgejo-mcp
|
||||
namespace: mcp-servers
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: forgejo-mcp
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: forgejo-mcp
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: arm64
|
||||
tolerations:
|
||||
- key: "arm64"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
containers:
|
||||
- name: forgejo-mcp
|
||||
image: node:22-slim
|
||||
command: ["node", "/app/index.js"]
|
||||
workingDir: /app
|
||||
env:
|
||||
- name: FORGEJO_URL
|
||||
value: "http://forgejo.forgejo.svc.cluster.local:3000"
|
||||
- name: FORGEJO_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: forgejo-tokens
|
||||
key: agent-cos
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: lean-experiment-mcp
|
||||
namespace: mcp-servers
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: lean-experiment-mcp
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: lean-experiment-mcp
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: arm64
|
||||
tolerations:
|
||||
- key: "arm64"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
containers:
|
||||
- name: lean-mcp
|
||||
image: node:22-slim
|
||||
command: ["node", "/app/dist/index.js"]
|
||||
workingDir: /app
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
46
apps/meos-dashboard/deployment.yaml
Normal file
46
apps/meos-dashboard/deployment.yaml
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# MeOS Dashboard — stateless React app, can run on any node
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: meos-dashboard
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: meos-dashboard
|
||||
namespace: meos-dashboard
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: meos-dashboard
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: meos-dashboard
|
||||
spec:
|
||||
containers:
|
||||
- name: dashboard
|
||||
image: ghcr.io/scobleclaw/meos-dashboard:latest
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
resources:
|
||||
requests:
|
||||
memory: "64Mi"
|
||||
cpu: "50m"
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "250m"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: meos-dashboard
|
||||
namespace: meos-dashboard
|
||||
spec:
|
||||
ports:
|
||||
- port: 3000
|
||||
targetPort: 3000
|
||||
selector:
|
||||
app: meos-dashboard
|
||||
59
apps/openbrain/deployment.yaml
Normal file
59
apps/openbrain/deployment.yaml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# OpenBrain MCP — deployed to ARM worker (rpi4-1)
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: openbrain
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: openbrain
|
||||
namespace: openbrain
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: openbrain
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: openbrain
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: arm64
|
||||
tolerations:
|
||||
- key: "arm64"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
containers:
|
||||
- name: openbrain
|
||||
image: node:22-slim
|
||||
command: ["node", "/app/index.js"]
|
||||
workingDir: /app
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: NODE_ENV
|
||||
value: production
|
||||
- name: PORT
|
||||
value: "3000"
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "250m"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: openbrain
|
||||
namespace: openbrain
|
||||
spec:
|
||||
ports:
|
||||
- port: 3000
|
||||
targetPort: 3000
|
||||
selector:
|
||||
app: openbrain
|
||||
59
apps/postgres/cluster.yaml
Normal file
59
apps/postgres/cluster.yaml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# CloudNative PG operator (recommended for Postgres on K8s)
|
||||
# https://cloudnative-pg.io/
|
||||
#
|
||||
# Install: kubectl apply --server-side -f https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.25/releases/cnpg-1.25.1.yaml
|
||||
#
|
||||
# Then create the cluster below.
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: postgres-scobleclaw
|
||||
namespace: database
|
||||
spec:
|
||||
instances: 1
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:17.4
|
||||
|
||||
storage:
|
||||
size: 5Gi
|
||||
storageClass: local-path
|
||||
|
||||
resources:
|
||||
requests:
|
||||
memory: "256Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
|
||||
postgresql:
|
||||
parameters:
|
||||
max_connections: "200"
|
||||
shared_buffers: "128MB"
|
||||
|
||||
databases:
|
||||
- name: forgejo
|
||||
owner: forgejo
|
||||
- name: umami
|
||||
owner: umami
|
||||
|
||||
users:
|
||||
- name: forgejo
|
||||
password: "<set-via-secret>"
|
||||
- name: umami
|
||||
password: "<set-via-secret>"
|
||||
|
||||
enableSuperuserAccess: false
|
||||
|
||||
monitoring:
|
||||
enablePodMonitor: false
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: postgres-users
|
||||
namespace: database
|
||||
type: Opaque
|
||||
stringData:
|
||||
forgejo-password: "<change-me>"
|
||||
umami-password: "<change-me>"
|
||||
67
apps/umami/deployment.yaml
Normal file
67
apps/umami/deployment.yaml
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
# Umami analytics — deployed to ARM worker (rpi4-2)
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: umami
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: umami
|
||||
namespace: umami
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: umami
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: umami
|
||||
spec:
|
||||
nodeSelector:
|
||||
kubernetes.io/arch: arm64
|
||||
tolerations:
|
||||
- key: "arm64"
|
||||
operator: "Exists"
|
||||
effect: "NoSchedule"
|
||||
containers:
|
||||
- name: umami
|
||||
image: ghcr.io/umami-software/umami:postgresql-v2
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
env:
|
||||
- name: DATABASE_TYPE
|
||||
value: postgresql
|
||||
- name: DATABASE_URL
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: umami-db
|
||||
key: url
|
||||
- name: APP_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: umami-secret
|
||||
key: secret
|
||||
- name: DISABLE_TELEMETRY
|
||||
value: "1"
|
||||
resources:
|
||||
requests:
|
||||
memory: "128Mi"
|
||||
cpu: "100m"
|
||||
limits:
|
||||
memory: "256Mi"
|
||||
cpu: "500m"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: umami
|
||||
namespace: umami
|
||||
spec:
|
||||
ports:
|
||||
- port: 3000
|
||||
targetPort: 3000
|
||||
selector:
|
||||
app: umami
|
||||
Loading…
Add table
Add a link
Reference in a new issue