Compare commits
7 commits
main
...
deploy-mea
| Author | SHA1 | Date | |
|---|---|---|---|
| b0023fef92 | |||
| 7dce0abbb9 | |||
| c411862fda | |||
| ac83bd14e3 | |||
| 30a114462c | |||
| 5c5fd18cac | |||
| bd2b02f51c |
22 changed files with 489 additions and 3 deletions
|
|
@ -16,6 +16,7 @@ borgmatic_source_directories:
|
||||||
- /opt/homebrew/var/forgejo
|
- /opt/homebrew/var/forgejo
|
||||||
- /Users/erichblume/.config/borgmatic
|
- /Users/erichblume/.config/borgmatic
|
||||||
- /Users/erichblume/Documents
|
- /Users/erichblume/Documents
|
||||||
|
- /Users/erichblume/.local/share/borgmatic/k8s-dumps
|
||||||
|
|
||||||
# Backup repositories
|
# Backup repositories
|
||||||
borgmatic_repositories:
|
borgmatic_repositories:
|
||||||
|
|
@ -31,6 +32,19 @@ borgmatic_repositories:
|
||||||
# BorgBase SSH key (fetched from 1Password in playbook pre_tasks)
|
# BorgBase SSH key (fetched from 1Password in playbook pre_tasks)
|
||||||
borgmatic_borgbase_ssh_key_path: /Users/erichblume/.ssh/borgbase_ed25519
|
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
|
# Exclude patterns
|
||||||
borgmatic_exclude_patterns: []
|
borgmatic_exclude_patterns: []
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,13 @@
|
||||||
key: "u3ugi1x1.repo.borgbase.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGU0mISTyHBw9tBs6SuhSq8tvNM8m9eifQxM+88TowPO"
|
key: "u3ugi1x1.repo.borgbase.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGU0mISTyHBw9tBs6SuhSq8tvNM8m9eifQxM+88TowPO"
|
||||||
state: present
|
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
|
- name: Deploy borgmatic configuration
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: config.yaml.j2
|
src: config.yaml.j2
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,16 @@ exclude_patterns:
|
||||||
|
|
||||||
encryption_passcommand: {{ borgmatic_encryption_passcommand }}
|
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 }}
|
ssh_command: ssh -o IdentitiesOnly=yes -i {{ borgmatic_borgbase_ssh_key_path }}
|
||||||
|
|
||||||
# Retention policy
|
# Retention policy
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,9 @@ caddy_services:
|
||||||
- name: ollama
|
- name: ollama
|
||||||
host: "ollama.{{ caddy_domain }}"
|
host: "ollama.{{ caddy_domain }}"
|
||||||
backend: "https://ollama.tail8d86e.ts.net"
|
backend: "https://ollama.tail8d86e.ts.net"
|
||||||
|
- name: mealie
|
||||||
|
host: "meals.{{ caddy_domain }}"
|
||||||
|
backend: "https://meals.tail8d86e.ts.net"
|
||||||
- name: sifaka
|
- name: sifaka
|
||||||
host: "nas.{{ caddy_domain }}"
|
host: "nas.{{ caddy_domain }}"
|
||||||
backend: "http://sifaka:5000"
|
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
|
provider: !KeyOf jellyfin-provider
|
||||||
meta_launch_url: https://jellyfin.ops.eblu.me
|
meta_launch_url: https://jellyfin.ops.eblu.me
|
||||||
policy_engine_mode: all
|
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:
|
secretKeyRef:
|
||||||
name: authentik-config
|
name: authentik-config
|
||||||
key: argocd-client-secret
|
key: argocd-client-secret
|
||||||
|
- name: AUTHENTIK_MEALIE_CLIENT_SECRET
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: authentik-config
|
||||||
|
key: mealie-client-secret
|
||||||
volumeMounts:
|
volumeMounts:
|
||||||
- name: blueprints
|
- name: blueprints
|
||||||
mountPath: /blueprints/custom
|
mountPath: /blueprints/custom
|
||||||
|
|
|
||||||
|
|
@ -57,3 +57,7 @@ spec:
|
||||||
remoteRef:
|
remoteRef:
|
||||||
key: "Authentik (blumeops)"
|
key: "Authentik (blumeops)"
|
||||||
property: argocd-client-secret
|
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
|
## Future Work
|
||||||
|
|
||||||
- **Additional services:** ArgoCD, Miniflux, Immich
|
- **Additional services:** Miniflux, Immich
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
- [[authentik]] - OIDC identity provider reference
|
- [[authentik]] - OIDC identity provider reference
|
||||||
- [[grafana]] - First OIDC client
|
- [[grafana]] - First OIDC client
|
||||||
|
- [[mealie]] - Recipe manager (public PKCE client)
|
||||||
- [[security-model]] - Network security and access control
|
- [[security-model]] - Network security and access control
|
||||||
- [[deploy-authentik]] - Deployment how-to
|
- [[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 |
|
| [[navidrome]] | https://dj.ops.eblu.me | Music streaming |
|
||||||
| [[jellyfin]] | https://jellyfin.ops.eblu.me | Media server |
|
| [[jellyfin]] | https://jellyfin.ops.eblu.me | Media server |
|
||||||
| [[postgresql]] | pg.ops.eblu.me:5432 | Database |
|
| [[postgresql]] | pg.ops.eblu.me:5432 | Database |
|
||||||
|
| [[mealie]] | https://meals.ops.eblu.me | Recipe manager |
|
||||||
| [[sifaka|Sifaka]] | https://nas.ops.eblu.me | NAS dashboard |
|
| [[sifaka|Sifaka]] | https://nas.ops.eblu.me | NAS dashboard |
|
||||||
|
|
||||||
## Public Services (`*.eblu.me`)
|
## Public Services (`*.eblu.me`)
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ Registry of all applications deployed via [[argocd]].
|
||||||
| `cv` | cv | `argocd/manifests/cv/` | [[cv]] |
|
| `cv` | cv | `argocd/manifests/cv/` | [[cv]] |
|
||||||
| `forgejo-runner` | forgejo-runner | `argocd/manifests/forgejo-runner/` | [[forgejo]] CI |
|
| `forgejo-runner` | forgejo-runner | `argocd/manifests/forgejo-runner/` | [[forgejo]] CI |
|
||||||
| `ollama` | ollama | `argocd/manifests/ollama/` | [[ollama]] |
|
| `ollama` | ollama | `argocd/manifests/ollama/` | [[ollama]] |
|
||||||
|
| `mealie` | mealie | `argocd/manifests/mealie/` | [[mealie]] |
|
||||||
|
|
||||||
## Sync Policies
|
## Sync Policies
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
---
|
---
|
||||||
title: Borgmatic
|
title: Borgmatic
|
||||||
modified: 2026-02-10
|
modified: 2026-03-16
|
||||||
tags:
|
tags:
|
||||||
- service
|
- service
|
||||||
- backup
|
- backup
|
||||||
|
|
@ -26,11 +26,15 @@ Daily backup system using Borg backup, running on indri.
|
||||||
- `/opt/homebrew/var/forgejo` - Git forge data
|
- `/opt/homebrew/var/forgejo` - Git forge data
|
||||||
- `~/.config/borgmatic` - Borgmatic config
|
- `~/.config/borgmatic` - Borgmatic config
|
||||||
- `~/Documents` - Personal documents
|
- `~/Documents` - Personal documents
|
||||||
|
- `~/.local/share/borgmatic/k8s-dumps/` - SQLite dumps from k8s pods
|
||||||
|
|
||||||
**Databases:**
|
**PostgreSQL databases:**
|
||||||
- `miniflux` on [[postgresql]]
|
- `miniflux` on [[postgresql]]
|
||||||
- `teslamate` 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):**
|
**Not backed up (by design):**
|
||||||
- ZIM archives (re-downloadable)
|
- ZIM archives (re-downloadable)
|
||||||
- Prometheus metrics (ephemeral)
|
- 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
|
upstream-source: https://code.forgejo.org/forgejo/runner/releases
|
||||||
notes: Forgejo runner on ringtail via nixpkgs; version tracks flake.lock
|
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
|
- name: unpoller
|
||||||
type: argocd
|
type: argocd
|
||||||
last-reviewed: 2026-03-16
|
last-reviewed: 2026-03-16
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue