Add karakeep, migrated off CasaOS on the Orange Pi
Validate and Deploy to K3s / validate (push) Successful in 24s
Validate and Deploy to K3s / deploy (push) Successful in 31s

Three containers move across as three deployments: the app, meilisearch, and
the headless-chrome renderer. Data (4.2M of SQLite + assets, 3.3M meilisearch
index) copied from /DATA/AppData/karakeep on the Orange Pi to gvolume0 at
/mnt/gvolume0/karakeep/{data,meilisearch}.

MEILI_MASTER_KEY and NEXTAUTH_SECRET are carried over unchanged, not
regenerated -- the first unlocks the existing index and the second signs
existing sessions. OPENAI_API_KEY was already empty on CasaOS, so AI tagging
was unused; the key is kept but blank.

NEXTAUTH_URL changes from http://localhost:14592 to https://keep.wittyoneoff.com
so NextAuth builds correct callback URLs behind the proxy.

chrome.yaml passes EXACTLY the args the CasaOS container used. Adding
--remote-debugging-address/--remote-debugging-port breaks it: the image's
entrypoint runs Chrome on 127.0.0.1:9223 and socat-proxies 9222 -> 9223, so
overriding those flags leaves socat retrying a dead port forever. Learned the
hard way.

App and meilisearch use strategy: Recreate -- single-writer SQLite on a
ReadWriteOnce volume, same rule as apps/tududi.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
jasonwitty
2026-09-20 20:58:39 -07:00
co-authored by Claude Opus 5
parent c01d5852d4
commit 00a1742b85
10 changed files with 322 additions and 0 deletions
+41
View File
@@ -0,0 +1,41 @@
# Headless Chrome used by karakeep to render and screenshot pages.
# Stateless -- no volume, safe to restart freely.
apiVersion: apps/v1
kind: Deployment
metadata:
name: karakeep-chrome
namespace: karakeep
spec:
replicas: 1
selector:
matchLabels:
app: karakeep-chrome
template:
metadata:
labels:
app: karakeep-chrome
spec:
containers:
- name: chrome
image: ghcr.io/karakeep-app/karakeep-chrome:release
# EXACTLY the args the CasaOS container used. Do NOT add
# --remote-debugging-address/--remote-debugging-port: the image's
# entrypoint (/headless-shell/run.sh) already runs Chrome on
# 127.0.0.1:9223 and socat-proxies 9222 -> 9223. Overriding those
# flags moves Chrome off 9223 and socat then logs
# "connect 127.0.0.1:9223: Connection refused" forever.
args:
- --disable-gpu
- --disable-dev-shm-usage
- --hide-scrollbars
- --disable-blink-features=AutomationControlled
- --window-size=1440,900
ports:
- containerPort: 9222
name: cdp
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
+77
View File
@@ -0,0 +1,77 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: karakeep
namespace: karakeep
spec:
replicas: 1
strategy:
# single-writer SQLite (db.db, queue.db) on a ReadWriteOnce volume
type: Recreate
selector:
matchLabels:
app: karakeep
template:
metadata:
labels:
app: karakeep
spec:
containers:
- name: karakeep
image: ghcr.io/karakeep-app/karakeep:0.33.2
env:
- name: DATA_DIR
value: /data
- name: PORT
value: "3000"
- name: NODE_ENV
value: production
- name: NEXT_TELEMETRY_DISABLED
value: "1"
- name: MEILI_ADDR
value: http://karakeep-meilisearch:7700
- name: BROWSER_WEB_URL
value: http://karakeep-chrome:9222
# Was http://localhost:14592 on CasaOS; must be the public URL so
# NextAuth builds correct callback links behind the proxy.
- name: NEXTAUTH_URL
value: https://keep.wittyoneoff.com
- name: NEXTAUTH_URL_INTERNAL
value: http://localhost:3000
- name: MEILI_MASTER_KEY
valueFrom:
secretKeyRef:
name: karakeep
key: meili-master-key
- name: NEXTAUTH_SECRET
valueFrom:
secretKeyRef:
name: karakeep
key: nextauth-secret
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: karakeep
key: openai-api-key
ports:
- containerPort: 3000
name: http
readinessProbe:
httpGet:
path: /
port: 3000
initialDelaySeconds: 20
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
volumeMounts:
- name: data
mountPath: /data
volumes:
- name: data
persistentVolumeClaim:
claimName: karakeep
+30
View File
@@ -0,0 +1,30 @@
# TLS is terminated at the AWS proxy (Cox blocks 80/443 at home), so this
# ingress is plain HTTP, same as every other app here.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: karakeep
namespace: karakeep
spec:
ingressClassName: traefik
rules:
- host: keep.wittyoneoff.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: karakeep
port:
number: 3000
- host: origin-keep.wittyoneoff.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: karakeep
port:
number: 3000
+12
View File
@@ -0,0 +1,12 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
# secret.example.yaml is intentionally NOT listed -- copy it to secret.yaml
# (gitignored), set real values, and apply it once per cluster.
resources:
- namespace.yaml
- storage.yaml
- deployment.yaml
- meilisearch.yaml
- chrome.yaml
- service.yaml
- ingress.yaml
+46
View File
@@ -0,0 +1,46 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: karakeep-meilisearch
namespace: karakeep
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: karakeep-meilisearch
template:
metadata:
labels:
app: karakeep-meilisearch
spec:
containers:
- name: meilisearch
image: getmeili/meilisearch:v1.41.0
env:
- name: MEILI_HTTP_ADDR
value: 0.0.0.0:7700
- name: MEILI_NO_ANALYTICS
value: "true"
- name: MEILI_MASTER_KEY
valueFrom:
secretKeyRef:
name: karakeep
key: meili-master-key
ports:
- containerPort: 7700
name: http
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 1Gi
volumeMounts:
- name: data
mountPath: /meili_data
volumes:
- name: data
persistentVolumeClaim:
claimName: karakeep-meilisearch
+4
View File
@@ -0,0 +1,4 @@
apiVersion: v1
kind: Namespace
metadata:
name: karakeep
+14
View File
@@ -0,0 +1,14 @@
# Copy to secret.yaml (gitignored), set real values, apply once per cluster.
# Carried over from the CasaOS deployment -- NEXTAUTH_SECRET signs existing
# sessions and MEILI_MASTER_KEY unlocks the existing index, so these must be
# the SAME values, not fresh ones.
apiVersion: v1
kind: Secret
metadata:
name: karakeep
namespace: karakeep
type: Opaque
stringData:
meili-master-key: CHANGEME
nextauth-secret: CHANGEME
openai-api-key: CHANGEME
+38
View File
@@ -0,0 +1,38 @@
apiVersion: v1
kind: Service
metadata:
name: karakeep
namespace: karakeep
spec:
type: ClusterIP
selector:
app: karakeep
ports:
- port: 3000
targetPort: 3000
---
apiVersion: v1
kind: Service
metadata:
name: karakeep-meilisearch
namespace: karakeep
spec:
type: ClusterIP
selector:
app: karakeep-meilisearch
ports:
- port: 7700
targetPort: 7700
---
apiVersion: v1
kind: Service
metadata:
name: karakeep-chrome
namespace: karakeep
spec:
type: ClusterIP
selector:
app: karakeep-chrome
ports:
- port: 9222
targetPort: 9222
+59
View File
@@ -0,0 +1,59 @@
# Migrated off CasaOS on the Orange Pi (/DATA/AppData/karakeep) 2026-09-21.
# Two volumes because the app and meilisearch are separate pods.
# karakeep keeps SQLite (db.db, queue.db) in DATA_DIR, so both deployments use
# strategy: Recreate -- exactly the single-writer rule apps/tududi follows.
apiVersion: v1
kind: PersistentVolume
metadata:
name: karakeep-pv
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
volumeMode: Filesystem
hostPath:
path: /mnt/gvolume0/karakeep/data
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: karakeep
namespace: karakeep
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
storageClassName: ""
volumeName: karakeep-pv
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: karakeep-meilisearch-pv
spec:
capacity:
storage: 5Gi
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
volumeMode: Filesystem
hostPath:
path: /mnt/gvolume0/karakeep/meilisearch
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: karakeep-meilisearch
namespace: karakeep
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: ""
volumeName: karakeep-meilisearch-pv
+1
View File
@@ -11,3 +11,4 @@ resources:
- apps/dashwise
- apps/tududi
- apps/limesurvey
- apps/karakeep