Deploy Mealie recipe manager (#299)
## Summary - Deploy Mealie (self-hosted recipe manager) on minikube-indri via ArgoCD - Build container from source via forge mirror (`mirrors/mealie`) — multi-stage Dockerfile with Node.js frontend + Python/uv backend - Add Caddy proxy entry for `meals.ops.eblu.me` - Part of a larger meal planning pipeline: Mealie stores categorized recipes, a planner script selects balanced meals, and Ollama generates unified cooking timelines ## Status - [x] Mirror mealie repo on forge - [x] Dockerfile (from-source build) - [x] ArgoCD app + k8s manifests - [x] Caddy proxy entry - [x] Service docs, routing table, app registry - [ ] Local Dagger build test - [ ] Container build + push to registry - [ ] Update kustomization.yaml with real image tag - [ ] Deploy and verify - [ ] Provision Caddy ## Test plan - Build container locally via `dagger call build --src=. --container-name=mealie` - Trigger CI build via `mise run container-build-and-release mealie` - Deploy from branch: `argocd app set mealie --revision deploy-mealie && argocd app sync mealie` - Verify Mealie UI at `https://meals.ops.eblu.me` - Verify API docs at `https://meals.ops.eblu.me/docs` Reviewed-on: #299
This commit is contained in:
parent
b54d87e071
commit
11330ebea0
22 changed files with 489 additions and 3 deletions
|
|
@ -16,6 +16,7 @@ borgmatic_source_directories:
|
|||
- /opt/homebrew/var/forgejo
|
||||
- /Users/erichblume/.config/borgmatic
|
||||
- /Users/erichblume/Documents
|
||||
- /Users/erichblume/.local/share/borgmatic/k8s-dumps
|
||||
|
||||
# Backup repositories
|
||||
borgmatic_repositories:
|
||||
|
|
@ -31,6 +32,19 @@ borgmatic_repositories:
|
|||
# BorgBase SSH key (fetched from 1Password in playbook pre_tasks)
|
||||
borgmatic_borgbase_ssh_key_path: /Users/erichblume/.ssh/borgbase_ed25519
|
||||
|
||||
# Directory for pre-backup database dumps from k8s pods
|
||||
borgmatic_k8s_dump_dir: /Users/erichblume/.local/share/borgmatic/k8s-dumps
|
||||
|
||||
# K8s SQLite databases to dump before backup via kubectl exec
|
||||
# Each entry runs: kubectl exec <pod-selector> -- sqlite3 <path> ".backup /tmp/backup.db"
|
||||
# then copies the dump to borgmatic_k8s_dump_dir/<name>.db
|
||||
borgmatic_k8s_sqlite_dumps:
|
||||
- name: mealie
|
||||
namespace: mealie
|
||||
label_selector: app=mealie
|
||||
db_path: /app/data/mealie.db
|
||||
context: minikube-indri
|
||||
|
||||
# Exclude patterns
|
||||
borgmatic_exclude_patterns: []
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,13 @@
|
|||
key: "u3ugi1x1.repo.borgbase.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGU0mISTyHBw9tBs6SuhSq8tvNM8m9eifQxM+88TowPO"
|
||||
state: present
|
||||
|
||||
- name: Ensure k8s dump directory exists
|
||||
ansible.builtin.file:
|
||||
path: "{{ borgmatic_k8s_dump_dir }}"
|
||||
state: directory
|
||||
mode: '0700'
|
||||
when: borgmatic_k8s_sqlite_dumps | length > 0
|
||||
|
||||
- name: Deploy borgmatic configuration
|
||||
ansible.builtin.template:
|
||||
src: config.yaml.j2
|
||||
|
|
|
|||
|
|
@ -31,6 +31,16 @@ exclude_patterns:
|
|||
|
||||
encryption_passcommand: {{ borgmatic_encryption_passcommand }}
|
||||
|
||||
{% if borgmatic_k8s_sqlite_dumps %}
|
||||
# Pre-backup: dump SQLite databases from k8s pods
|
||||
# Uses sqlite3 .backup for a safe, consistent copy (no corruption from concurrent writes)
|
||||
before_backup:
|
||||
- mkdir -p {{ borgmatic_k8s_dump_dir }}
|
||||
{% for db in borgmatic_k8s_sqlite_dumps %}
|
||||
- /opt/homebrew/bin/kubectl --context={{ db.context }} exec -n {{ db.namespace }} deploy/{{ db.name }} -- python3 -c "import sqlite3; sqlite3.connect('{{ db.db_path }}').backup(sqlite3.connect('/tmp/{{ db.name }}-backup.db'))" && /opt/homebrew/bin/kubectl --context={{ db.context }} cp {{ db.namespace }}/$(/opt/homebrew/bin/kubectl --context={{ db.context }} get pod -n {{ db.namespace }} -l {{ db.label_selector }} -o jsonpath='{.items[0].metadata.name}'):/tmp/{{ db.name }}-backup.db {{ borgmatic_k8s_dump_dir }}/{{ db.name }}.db
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
ssh_command: ssh -o IdentitiesOnly=yes -i {{ borgmatic_borgbase_ssh_key_path }}
|
||||
|
||||
# Retention policy
|
||||
|
|
|
|||
|
|
@ -91,6 +91,9 @@ caddy_services:
|
|||
- name: ollama
|
||||
host: "ollama.{{ caddy_domain }}"
|
||||
backend: "https://ollama.tail8d86e.ts.net"
|
||||
- name: mealie
|
||||
host: "meals.{{ caddy_domain }}"
|
||||
backend: "https://meals.tail8d86e.ts.net"
|
||||
- name: sifaka
|
||||
host: "nas.{{ caddy_domain }}"
|
||||
backend: "http://sifaka:5000"
|
||||
|
|
|
|||
17
argocd/apps/mealie.yaml
Normal file
17
argocd/apps/mealie.yaml
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: mealie
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: default
|
||||
source:
|
||||
repoURL: ssh://forgejo@forge.ops.eblu.me:2222/eblume/blumeops.git
|
||||
targetRevision: main
|
||||
path: argocd/manifests/mealie
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: mealie
|
||||
syncPolicy:
|
||||
syncOptions:
|
||||
- CreateNamespace=true
|
||||
|
|
@ -345,3 +345,47 @@ data:
|
|||
provider: !KeyOf jellyfin-provider
|
||||
meta_launch_url: https://jellyfin.ops.eblu.me
|
||||
policy_engine_mode: all
|
||||
|
||||
mealie.yaml: |
|
||||
version: 1
|
||||
metadata:
|
||||
name: BlumeOps Mealie SSO
|
||||
labels:
|
||||
blueprints.goauthentik.io/description: "Mealie OIDC provider and application"
|
||||
entries:
|
||||
# OAuth2 provider for Mealie (confidential — Mealie requires client_secret)
|
||||
- model: authentik_providers_oauth2.oauth2provider
|
||||
id: mealie-provider
|
||||
identifiers:
|
||||
name: Mealie
|
||||
attrs:
|
||||
name: Mealie
|
||||
authorization_flow: !Find [authentik_flows.flow, [slug, default-provider-authorization-implicit-consent]]
|
||||
invalidation_flow: !Find [authentik_flows.flow, [slug, default-provider-invalidation-flow]]
|
||||
client_type: confidential
|
||||
client_id: mealie
|
||||
client_secret: !Env AUTHENTIK_MEALIE_CLIENT_SECRET
|
||||
redirect_uris:
|
||||
- matching_mode: strict
|
||||
url: https://meals.ops.eblu.me/login
|
||||
- matching_mode: strict
|
||||
url: https://meals.tail8d86e.ts.net/login
|
||||
signing_key: !Find [authentik_crypto.certificatekeypair, [name, authentik Self-signed Certificate]]
|
||||
property_mappings:
|
||||
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, openid]]
|
||||
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, email]]
|
||||
- !Find [authentik_providers_oauth2.scopemapping, [scope_name, profile]]
|
||||
sub_mode: hashed_user_id
|
||||
include_claims_in_id_token: true
|
||||
|
||||
# Mealie application — all authenticated users allowed (admin mapped via OIDC_ADMIN_GROUP)
|
||||
- model: authentik_core.application
|
||||
id: mealie-app
|
||||
identifiers:
|
||||
slug: mealie
|
||||
attrs:
|
||||
name: Mealie
|
||||
slug: mealie
|
||||
provider: !KeyOf mealie-provider
|
||||
meta_launch_url: https://meals.ops.eblu.me
|
||||
policy_engine_mode: all
|
||||
|
|
|
|||
|
|
@ -78,6 +78,11 @@ spec:
|
|||
secretKeyRef:
|
||||
name: authentik-config
|
||||
key: argocd-client-secret
|
||||
- name: AUTHENTIK_MEALIE_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: authentik-config
|
||||
key: mealie-client-secret
|
||||
volumeMounts:
|
||||
- name: blueprints
|
||||
mountPath: /blueprints/custom
|
||||
|
|
|
|||
|
|
@ -57,3 +57,7 @@ spec:
|
|||
remoteRef:
|
||||
key: "Authentik (blumeops)"
|
||||
property: argocd-client-secret
|
||||
- secretKey: mealie-client-secret
|
||||
remoteRef:
|
||||
key: "Authentik (blumeops)"
|
||||
property: mealie-client-secret
|
||||
|
|
|
|||
79
argocd/manifests/mealie/deployment.yaml
Normal file
79
argocd/manifests/mealie/deployment.yaml
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: mealie
|
||||
namespace: mealie
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mealie
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mealie
|
||||
spec:
|
||||
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-oidc
|
||||
key: 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"
|
||||
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
|
||||
19
argocd/manifests/mealie/external-secret.yaml
Normal file
19
argocd/manifests/mealie/external-secret.yaml
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
---
|
||||
apiVersion: external-secrets.io/v1
|
||||
kind: ExternalSecret
|
||||
metadata:
|
||||
name: mealie-oidc
|
||||
namespace: mealie
|
||||
spec:
|
||||
refreshInterval: 1h
|
||||
secretStoreRef:
|
||||
kind: ClusterSecretStore
|
||||
name: onepassword-blumeops
|
||||
target:
|
||||
name: mealie-oidc
|
||||
creationPolicy: Owner
|
||||
data:
|
||||
- secretKey: client-secret
|
||||
remoteRef:
|
||||
key: "Authentik (blumeops)"
|
||||
property: mealie-client-secret
|
||||
25
argocd/manifests/mealie/ingress-tailscale.yaml
Normal file
25
argocd/manifests/mealie/ingress-tailscale.yaml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: mealie-tailscale
|
||||
namespace: mealie
|
||||
annotations:
|
||||
tailscale.com/proxy-class: "default"
|
||||
tailscale.com/proxy-group: "ingress"
|
||||
gethomepage.dev/enabled: "true"
|
||||
gethomepage.dev/name: "Mealie"
|
||||
gethomepage.dev/group: "Home"
|
||||
gethomepage.dev/icon: "mealie.png"
|
||||
gethomepage.dev/description: "Recipe manager"
|
||||
gethomepage.dev/href: "https://meals.ops.eblu.me"
|
||||
gethomepage.dev/pod-selector: "app=mealie"
|
||||
spec:
|
||||
ingressClassName: tailscale
|
||||
defaultBackend:
|
||||
service:
|
||||
name: mealie
|
||||
port:
|
||||
number: 9000
|
||||
tls:
|
||||
- hosts:
|
||||
- meals
|
||||
15
argocd/manifests/mealie/kustomization.yaml
Normal file
15
argocd/manifests/mealie/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.12.0-5c5fd18
|
||||
13
argocd/manifests/mealie/pvc.yaml
Normal file
13
argocd/manifests/mealie/pvc.yaml
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: mealie-data
|
||||
namespace: mealie
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
storageClassName: standard
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
13
argocd/manifests/mealie/service.yaml
Normal file
13
argocd/manifests/mealie/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
|
||||
142
containers/mealie/Dockerfile
Normal file
142
containers/mealie/Dockerfile
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# Mealie — self-hosted recipe manager
|
||||
# Built from source via forge mirror of mealie-recipes/mealie
|
||||
# Based on upstream docker/Dockerfile (multi-stage: Node frontend + Python backend)
|
||||
|
||||
ARG CONTAINER_APP_VERSION=v3.12.0
|
||||
|
||||
###############################################
|
||||
# Frontend Build
|
||||
###############################################
|
||||
FROM node:24-slim AS frontend-builder
|
||||
|
||||
ARG CONTAINER_APP_VERSION
|
||||
RUN apt-get update && apt-get install --no-install-recommends -y git ca-certificates && rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN git clone --depth 1 --branch ${CONTAINER_APP_VERSION} \
|
||||
https://forge.ops.eblu.me/mirrors/mealie.git /src
|
||||
|
||||
WORKDIR /src/frontend
|
||||
|
||||
RUN yarn install \
|
||||
--prefer-offline \
|
||||
--frozen-lockfile \
|
||||
--non-interactive \
|
||||
--production=false \
|
||||
--network-timeout 1000000
|
||||
|
||||
RUN yarn generate
|
||||
|
||||
###############################################
|
||||
# Python Base
|
||||
###############################################
|
||||
FROM python:3.12-slim AS python-base
|
||||
|
||||
ENV MEALIE_HOME="/app"
|
||||
ENV PYTHONUNBUFFERED=1 \
|
||||
PYTHONDONTWRITEBYTECODE=1 \
|
||||
PIP_NO_CACHE_DIR=off \
|
||||
PIP_DISABLE_PIP_VERSION_CHECK=on \
|
||||
PIP_DEFAULT_TIMEOUT=100 \
|
||||
VENV_PATH="/opt/mealie"
|
||||
|
||||
ENV PATH="$VENV_PATH/bin:$PATH"
|
||||
|
||||
RUN useradd -u 911 -U -d $MEALIE_HOME -s /bin/bash abc \
|
||||
&& usermod -G users abc \
|
||||
&& mkdir $MEALIE_HOME
|
||||
|
||||
###############################################
|
||||
# Backend Package Build
|
||||
###############################################
|
||||
FROM python-base AS backend-builder
|
||||
|
||||
ARG CONTAINER_APP_VERSION
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y curl git ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN pip install uv
|
||||
|
||||
RUN git clone --depth 1 --branch ${CONTAINER_APP_VERSION} \
|
||||
https://forge.ops.eblu.me/mirrors/mealie.git /src
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
COPY --from=frontend-builder /src/frontend/dist ./mealie/frontend
|
||||
|
||||
RUN uv build --out-dir dist
|
||||
|
||||
RUN uv export --no-editable --no-emit-project --extra pgsql --format requirements-txt --output-file dist/requirements.txt \
|
||||
&& MEALIE_VERSION=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml', 'rb'))['project']['version'])") \
|
||||
&& echo "mealie[pgsql]==${MEALIE_VERSION} \\" >> dist/requirements.txt \
|
||||
&& pip hash dist/mealie-${MEALIE_VERSION}-py3-none-any.whl | tail -n1 | tr -d '\n' >> dist/requirements.txt \
|
||||
&& echo " \\" >> dist/requirements.txt \
|
||||
&& pip hash dist/mealie-${MEALIE_VERSION}.tar.gz | tail -n1 >> dist/requirements.txt
|
||||
|
||||
###############################################
|
||||
# Python Venv Build
|
||||
###############################################
|
||||
FROM python-base AS venv-builder
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
build-essential \
|
||||
libpq-dev \
|
||||
libwebp-dev \
|
||||
ffmpeg \
|
||||
libsasl2-dev libldap2-dev libssl-dev \
|
||||
gnupg gnupg2 gnupg1 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN python3 -m venv --upgrade-deps $VENV_PATH
|
||||
|
||||
COPY --from=backend-builder /src/dist /dist
|
||||
|
||||
RUN . $VENV_PATH/bin/activate \
|
||||
&& pip install --require-hashes -r /dist/requirements.txt --find-links /dist
|
||||
|
||||
###############################################
|
||||
# Production Image
|
||||
###############################################
|
||||
FROM python-base AS production
|
||||
|
||||
ENV PRODUCTION=true
|
||||
ENV TESTING=false
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install --no-install-recommends -y \
|
||||
curl \
|
||||
ffmpeg \
|
||||
gosu \
|
||||
iproute2 \
|
||||
libldap-common \
|
||||
libldap2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
RUN mkdir -p /run/secrets
|
||||
|
||||
COPY --from=venv-builder $VENV_PATH $VENV_PATH
|
||||
|
||||
ENV NLTK_DATA="/nltk_data/"
|
||||
RUN mkdir -p $NLTK_DATA
|
||||
RUN python -m nltk.downloader -d $NLTK_DATA averaged_perceptron_tagger_eng
|
||||
|
||||
VOLUME ["$MEALIE_HOME/data/"]
|
||||
ENV APP_PORT=9000
|
||||
|
||||
EXPOSE ${APP_PORT}
|
||||
|
||||
COPY --from=backend-builder /src/docker/healthcheck.sh $MEALIE_HOME/healthcheck.sh
|
||||
RUN chmod +x $MEALIE_HOME/healthcheck.sh
|
||||
HEALTHCHECK CMD $MEALIE_HOME/healthcheck.sh
|
||||
|
||||
ENV HOST=0.0.0.0
|
||||
|
||||
COPY --from=backend-builder /src/docker/entry.sh $MEALIE_HOME/run.sh
|
||||
RUN chmod +x $MEALIE_HOME/run.sh
|
||||
|
||||
LABEL org.opencontainers.image.title="Mealie"
|
||||
LABEL org.opencontainers.image.description="Self-hosted recipe manager"
|
||||
LABEL org.opencontainers.image.source="https://github.com/mealie-recipes/mealie"
|
||||
|
||||
ENTRYPOINT ["/app/run.sh"]
|
||||
1
docs/changelog.d/deploy-mealie.feature.md
Normal file
1
docs/changelog.d/deploy-mealie.feature.md
Normal file
|
|
@ -0,0 +1 @@
|
|||
Deploy Mealie recipe manager on minikube-indri for meal planning and prep automation.
|
||||
|
|
@ -76,11 +76,12 @@ Authentik enforces TOTP MFA on its default authentication flow (`not_configured_
|
|||
|
||||
## Future Work
|
||||
|
||||
- **Additional services:** ArgoCD, Miniflux, Immich
|
||||
- **Additional services:** Miniflux, Immich
|
||||
|
||||
## Related
|
||||
|
||||
- [[authentik]] - OIDC identity provider reference
|
||||
- [[grafana]] - First OIDC client
|
||||
- [[mealie]] - Recipe manager (public PKCE client)
|
||||
- [[security-model]] - Network security and access control
|
||||
- [[deploy-authentik]] - Deployment how-to
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ DNS points to [[indri]]'s Tailscale IP. TLS via Let's Encrypt (ACME DNS-01 with
|
|||
| [[navidrome]] | https://dj.ops.eblu.me | Music streaming |
|
||||
| [[jellyfin]] | https://jellyfin.ops.eblu.me | Media server |
|
||||
| [[postgresql]] | pg.ops.eblu.me:5432 | Database |
|
||||
| [[mealie]] | https://meals.ops.eblu.me | Recipe manager |
|
||||
| [[sifaka|Sifaka]] | https://nas.ops.eblu.me | NAS dashboard |
|
||||
|
||||
## Public Services (`*.eblu.me`)
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ Registry of all applications deployed via [[argocd]].
|
|||
| `cv` | cv | `argocd/manifests/cv/` | [[cv]] |
|
||||
| `forgejo-runner` | forgejo-runner | `argocd/manifests/forgejo-runner/` | [[forgejo]] CI |
|
||||
| `ollama` | ollama | `argocd/manifests/ollama/` | [[ollama]] |
|
||||
| `mealie` | mealie | `argocd/manifests/mealie/` | [[mealie]] |
|
||||
|
||||
## Sync Policies
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
---
|
||||
title: Borgmatic
|
||||
modified: 2026-02-10
|
||||
modified: 2026-03-16
|
||||
tags:
|
||||
- service
|
||||
- backup
|
||||
|
|
@ -26,11 +26,15 @@ Daily backup system using Borg backup, running on indri.
|
|||
- `/opt/homebrew/var/forgejo` - Git forge data
|
||||
- `~/.config/borgmatic` - Borgmatic config
|
||||
- `~/Documents` - Personal documents
|
||||
- `~/.local/share/borgmatic/k8s-dumps/` - SQLite dumps from k8s pods
|
||||
|
||||
**Databases:**
|
||||
**PostgreSQL databases:**
|
||||
- `miniflux` on [[postgresql]]
|
||||
- `teslamate` on [[postgresql]]
|
||||
|
||||
**K8s SQLite databases (pre-backup dump via kubectl exec):**
|
||||
- [[mealie]] - Recipe manager (`/app/data/mealie.db`)
|
||||
|
||||
**Not backed up (by design):**
|
||||
- ZIM archives (re-downloadable)
|
||||
- Prometheus metrics (ephemeral)
|
||||
|
|
|
|||
61
docs/reference/services/mealie.md
Normal file
61
docs/reference/services/mealie.md
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
---
|
||||
title: Mealie
|
||||
modified: 2026-03-16
|
||||
tags:
|
||||
- service
|
||||
- recipes
|
||||
---
|
||||
|
||||
# Mealie
|
||||
|
||||
Self-hosted recipe manager with a REST API. Part of the meal planning pipeline: Mealie stores categorized recipes, a planner script selects balanced meals, and [[ollama]] generates a unified cooking timeline.
|
||||
|
||||
## Quick Reference
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **URL** | https://meals.ops.eblu.me |
|
||||
| **Tailscale URL** | https://meals.tail8d86e.ts.net |
|
||||
| **Namespace** | `mealie` |
|
||||
| **Image** | `registry.ops.eblu.me/blumeops/mealie` (built from source) |
|
||||
| **Database** | SQLite (local, at `/app/data/`) |
|
||||
| **API Docs** | https://meals.ops.eblu.me/docs |
|
||||
| **Upstream** | https://github.com/mealie-recipes/mealie |
|
||||
| **Manifests** | `argocd/manifests/mealie/` |
|
||||
|
||||
## Features
|
||||
|
||||
- Full REST API (FastAPI) for recipe CRUD, filtering by tag/category
|
||||
- Structured recipe data: ingredients (quantity/unit/food), step-by-step instructions
|
||||
- Built-in meal planning and shopping lists
|
||||
- Recipe import from URLs
|
||||
- API token auth for automation
|
||||
- OIDC login via [[authentik]] (confidential client)
|
||||
|
||||
## Authentication
|
||||
|
||||
OIDC via [[authentik]] using a confidential client. Client secret stored in 1Password (`Authentik (blumeops)` / `mealie-client-secret`) and delivered via ExternalSecret. All Authentik users can log in; members of the `admins` group get Mealie admin privileges via `OIDC_ADMIN_GROUP`.
|
||||
|
||||
## Storage
|
||||
|
||||
- 2Gi PVC at `/app/data/` via `standard` storageClassName (minikube-hostpath)
|
||||
- SQLite database (sufficient for single-user)
|
||||
- Recipe images and assets stored alongside the database
|
||||
|
||||
## Backup
|
||||
|
||||
SQLite database backed up via [[borgmatic]]'s `before_backup` hook. Borgmatic runs `kubectl exec` to create a safe `.backup` copy (via Python's `sqlite3` module), then `kubectl cp` to the host. The dump lands in `~/.local/share/borgmatic/k8s-dumps/mealie.db` and is included in both local (sifaka) and offsite (BorgBase) backups.
|
||||
|
||||
## Networking
|
||||
|
||||
| Endpoint | Reachable from |
|
||||
|----------|----------------|
|
||||
| `https://meals.ops.eblu.me` | Tailnet clients (via Caddy) |
|
||||
| `https://meals.tail8d86e.ts.net` | Tailnet clients |
|
||||
| `http://mealie.mealie.svc.cluster.local:9000` | In-cluster |
|
||||
|
||||
## Related
|
||||
|
||||
- [[authentik]] — OIDC identity provider
|
||||
- [[ollama]] — LLM backend for meal timeline generation
|
||||
- [[borgmatic]] — Data backup
|
||||
|
|
@ -253,6 +253,13 @@ services:
|
|||
upstream-source: https://code.forgejo.org/forgejo/runner/releases
|
||||
notes: Forgejo runner on ringtail via nixpkgs; version tracks flake.lock
|
||||
|
||||
- name: mealie
|
||||
type: argocd
|
||||
last-reviewed: 2026-03-16
|
||||
current-version: "v3.12.0"
|
||||
upstream-source: https://github.com/mealie-recipes/mealie/releases
|
||||
notes: Recipe manager; built from source via forge mirror
|
||||
|
||||
- name: unpoller
|
||||
type: argocd
|
||||
last-reviewed: 2026-03-16
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue