diff --git a/nodes/heimdall/apprise/compose.yaml b/nodes/heimdall/apprise/compose.yaml deleted file mode 100644 index e6fbaf6..0000000 --- a/nodes/heimdall/apprise/compose.yaml +++ /dev/null @@ -1,20 +0,0 @@ -services: - apprise-api: - image: lscr.io/linuxserver/apprise-api:latest - container_name: apprise-api - environment: - - PUID=1000 - - PGID=1000 - - TZ=America/New_York - ports: - - 8000:8000 - volumes: - - /mnt/appdata/apprise/data:/config - restart: unless-stopped - networks: - - proxy-net - -networks: - proxy-net: - name: proxy-net - external: true \ No newline at end of file diff --git a/nodes/heimdall/docker_registry/compose.yaml b/nodes/heimdall/docker_registry/compose.yaml deleted file mode 100644 index 0ee3304..0000000 --- a/nodes/heimdall/docker_registry/compose.yaml +++ /dev/null @@ -1,23 +0,0 @@ -services: - registry: - image: registry:2 - container_name: docker_registry - restart: always - environment: - - REGISTRY_STORAGE_DELETE_ENABLED=true - - REGISTRY_HTTP_SECRET=temporary_secret_123 - volumes: - - /mnt/appdata/docker_registry/data:/var/lib/registry - networks: - - proxy-net - labels: - - traefik.enable=true - - traefik.http.routers.registry.rule=Host(`registry.castaldifamily.com`) - - traefik.http.routers.registry.entrypoints=websecure - - traefik.http.routers.registry.tls.certresolver=cloudflare - - traefik.http.services.registry.loadbalancer.server.port=5000 - -networks: - proxy-net: - name: proxy-net - external: true \ No newline at end of file diff --git a/nodes/heimdall/ntfy/compose.yaml b/nodes/heimdall/ntfy/compose.yaml deleted file mode 100644 index ef74e9d..0000000 --- a/nodes/heimdall/ntfy/compose.yaml +++ /dev/null @@ -1,24 +0,0 @@ -name: ntfy -services: - ntfy: - image: binwiederhier/ntfy:latest - container_name: ntfy - restart: unless-stopped - command: - - serve - volumes: - - /mnt/appdata/ntfy/data:/var/lib/ntfy - - /mnt/appdata/ntfy/server.yml:/etc/ntfy/server.yml:ro - networks: - - proxy-net - labels: - - "traefik.enable=true" - - "traefik.http.routers.ntfy.rule=Host(`ntfy.castaldifamily.com`)" - - "traefik.http.routers.ntfy.entrypoints=websecure" - - "traefik.http.routers.ntfy.tls=true" - - "traefik.http.routers.ntfy.tls.certresolver=cloudflare" - - "traefik.http.services.ntfy.loadbalancer.server.port=80" - -networks: - proxy-net: - external: true diff --git a/nodes/heimdall/sparkyfitness/README.md b/nodes/heimdall/sparkyfitness/README.md deleted file mode 100644 index c9de6dd..0000000 --- a/nodes/heimdall/sparkyfitness/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# SparkyFitness - -**Node:** heimdall -**Stack:** `sparkyfitness` -**URL:** https://fitness.castaldifamily.com -**Version:** v0.16.6.1 -**Source:** https://github.com/CodeWithCJ/SparkyFitness -**Docs:** https://codewithcj.github.io/SparkyFitness/ - ---- - -## Overview - -SparkyFitness is a self-hosted, privacy-first fitness and health tracking platform — a full alternative to MyFitnessPal. It stores all data on infrastructure you control with no reliance on third-party services. - -**Core capabilities:** -- Nutrition, exercise, hydration, sleep, fasting, mood, and body measurement tracking -- Goal setting and daily check-ins -- Interactive charts and long-term reports -- Multiple user profiles and family access -- Native OIDC, TOTP, Passkey, and MFA support -- Health platform integrations: Apple Health, Google Health Connect, Fitbit, Withings, Garmin, Polar, Strava -- Food database integrations: OpenFoodFacts, USDA, FatSecret, Nutritionix -- Recipe app integrations: Mealie, Tandoor -- Optional AI chatbot for conversational food/exercise logging (beta) - -**Stack architecture:** - -| Container | Image | Role | -|---|---|---| -| `sparkyfitness-db` | `postgres:18.3-alpine` | PostgreSQL database | -| `sparkyfitness-server` | `codewithcj/sparkyfitness_server:v0.16.6.1` | Node.js backend API | -| `sparkyfitness-frontend` | `codewithcj/sparkyfitness:v0.16.6.1` | React frontend (Nginx) | - ---- - -## Node - -Runs on **heimdall** — the general-purpose applications node. No GPU is required. Workload is typical of a personal web application. - ---- - -## Access - -| Method | Detail | -|---|---| -| **URL** | https://fitness.castaldifamily.com | -| **Auth** | SparkyFitness native auth (TOTP, passkeys, MFA supported) | -| **SSO** | Not configured — OIDC with Authentik can be added via env vars (see `.env.example`) | -| **Admin panel** | Set `SPARKY_FITNESS_ADMIN_EMAIL` in `.env` to auto-grant admin on first start | - -> ⚠️ Set `SPARKY_FITNESS_DISABLE_SIGNUP=true` in `.env` after creating your initial account to prevent open registration. - ---- - -## Appdata - -All persistent data lives under `/mnt/appdata/sparkyfitness/data/` on the NFS share. - -| Path | Contents | Backup Priority | -|---|---|---| -| `postgresql/` | All user data — nutrition logs, workouts, health metrics, accounts | 🔴 Critical | -| `uploads/` | Profile pictures and exercise images | 🟡 Important | -| `backup/` | App-generated PostgreSQL exports | 🟢 Nice-to-have | - -Pre-create these directories before first deploy: - -```bash -mkdir -p /mnt/appdata/sparkyfitness/data/postgresql -mkdir -p /mnt/appdata/sparkyfitness/data/backup -mkdir -p /mnt/appdata/sparkyfitness/data/uploads -``` - ---- - -## Environment Variables - -Managed via `.env` (git-crypt encrypted). See `.env.example` for the full reference. - -### Required Secrets - -| Variable | Purpose | How to Generate | -|---|---|---| -| `SPARKY_FITNESS_DB_PASSWORD` | PostgreSQL superuser password | `openssl rand -base64 24` | -| `SPARKY_FITNESS_APP_DB_PASSWORD` | App-role DB password (limited privileges) | `openssl rand -base64 24` | -| `SPARKY_FITNESS_API_ENCRYPTION_KEY` | 64-char hex key for external integration encryption | `openssl rand -hex 32` | -| `BETTER_AUTH_SECRET` | Signs sessions and encrypts TOTP/2FA data | `openssl rand -hex 32` | - -> ⚠️ `SPARKY_FITNESS_API_ENCRYPTION_KEY` — changing this after data is stored invalidates all encrypted external integration credentials. -> ⚠️ `BETTER_AUTH_SECRET` — changing this after users have enabled TOTP/2FA locks them out. Do not rotate without disabling 2FA first. - -### Non-Secret Configuration (hardcoded in compose.yaml) - -| Variable | Value | Notes | -|---|---|---| -| `SPARKY_FITNESS_FRONTEND_URL` | `https://fitness.castaldifamily.com` | Used for CORS and session binding | -| `SPARKY_FITNESS_DB_HOST` | `sparkyfitness-db` | Internal Docker service name | -| `SPARKY_FITNESS_DB_NAME` | `sparkyfitness_db` | Database name | -| `SPARKY_FITNESS_DB_USER` | `sparky` | Superuser for migrations | -| `SPARKY_FITNESS_APP_DB_USER` | `sparky_app` | Runtime app user (limited privileges) | -| `SPARKY_FITNESS_LOG_LEVEL` | `ERROR` | Use `DEBUG` temporarily for troubleshooting | -| `NODE_ENV` | `production` | | -| `TZ` | `Etc/UTC` | Adjust if local time is needed for date handling | -| `SPARKY_FITNESS_FORCE_EMAIL_LOGIN` | `true` | Failsafe — prevents OIDC misconfiguration lockout | - ---- - -## Networking - -| Network | Type | Used By | -|---|---|---| -| `proxy-net` | External (Traefik) | `sparkyfitness-frontend` only | -| `sparkyfitness-network` | Internal bridge | All three containers | - -No host ports are exposed. All external traffic enters via Traefik. - -### Traefik Routing - -| Field | Value | -|---|---| -| **Router** | `sparkyfitness` | -| **Rule** | `Host(\`fitness.castaldifamily.com\`)` | -| **Entrypoint** | `websecure` (HTTPS) | -| **TLS** | Cloudflare cert resolver | -| **Middleware** | `security-headers@file` | -| **Backend port** | `80` (Nginx inside `sparkyfitness-frontend`) | - -The frontend Nginx container internally proxies API requests to `sparkyfitness-server:3010` over `sparkyfitness-network`. No separate Traefik route is needed for the API. - ---- - -## Dependencies - -| Dependency | Type | Notes | -|---|---|---| -| PostgreSQL 18.3-alpine | Bundled | Self-contained — no shared DB instance | -| Traefik | External (core stack) | Must be running on `proxy-net` | -| Cloudflare DNS | External | Required for TLS cert resolver | - -No Redis, GPU, or kernel module dependencies. - ---- - -## Backup & Recovery - -### What to Back Up - -The `postgresql/` directory is the **single source of truth** for all user data. This must be in the backup rotation before going live. - -``` -/mnt/appdata/sparkyfitness/data/postgresql/ ← Critical -/mnt/appdata/sparkyfitness/data/uploads/ ← Important -``` - -### Restore Steps - -1. Stop the stack: `docker compose down` -2. Restore the PostgreSQL data directory to `/mnt/appdata/sparkyfitness/data/postgresql/` -3. Restore uploads to `/mnt/appdata/sparkyfitness/data/uploads/` -4. Redeploy: `docker compose up -d` -5. Verify the DB is healthy: `docker compose logs sparkyfitness-db` - -### Upgrading - -> ⚠️ Auto-updating containers is **not recommended** by the maintainer. Watchtower should be excluded from this stack. - -1. Review release notes at https://github.com/CodeWithCJ/SparkyFitness/releases -2. Take a backup of `postgresql/` -3. Update image tags in `compose.yaml` -4. Commit and push — Komodo will redeploy via webhook -5. Check logs: `docker compose logs --tail=50` - ---- - -## Known Issues / Notes - -- **Garmin microservice** (`sparkyfitness-garmin`) is still in development upstream. It is commented out of the compose file intentionally. Do not enable until the maintainer marks it stable. -- **Auto-update warning**: The maintainer explicitly warns against automated container updates between releases. Ensure Watchtower is not targeting this stack. -- **Postgres upgrade notice**: The upstream release notes flag a forthcoming mandatory Postgres upgrade. Monitor release notes and follow the [official Postgres upgrade guide](https://codewithcj.github.io/SparkyFitness/install/postgres-upgrade) when the time comes. -- **AI chatbot** is in beta — expect potential bugs if enabled. -- **Family & Friends access** is in beta — expect potential bugs if enabled. diff --git a/nodes/heimdall/sparkyfitness/compose.yaml b/nodes/heimdall/sparkyfitness/compose.yaml deleted file mode 100644 index 71ece33..0000000 --- a/nodes/heimdall/sparkyfitness/compose.yaml +++ /dev/null @@ -1,149 +0,0 @@ -x-info: - repo: https://github.com/CodeWithCJ/SparkyFitness - releases: https://github.com/CodeWithCJ/SparkyFitness/releases - documentation: https://codewithcj.github.io/SparkyFitness/ - -# nodes/heimdall/sparkyfitness/compose.yaml -# Deployed via Komodo | Managed by GitOps -# Service: sparkyfitness -# Source: https://github.com/CodeWithCJ/SparkyFitness - -networks: - proxy-net: - external: true - sparkyfitness-network: - driver: bridge - -services: - - # ── Database ──────────────────────────────────────────────────────────────── - sparkyfitness-db: - image: postgres:18.3-alpine - container_name: sparkyfitness-db - restart: unless-stopped - networks: - - sparkyfitness-network - environment: - POSTGRES_DB: sparkyfitness_db - POSTGRES_USER: sparky - POSTGRES_PASSWORD: "${SPARKY_FITNESS_DB_PASSWORD}" - PUID: 1000 - GUID: 1000 - volumes: - - /mnt/appdata/sparkyfitness/data/postgresql:/var/lib/postgresql/data - healthcheck: - test: ["CMD-SHELL", "pg_isready -U sparky -d sparkyfitness_db"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 30s - deploy: - resources: - limits: - cpus: "1.0" - memory: 512M - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - - # ── Backend Server ─────────────────────────────────────────────────────────── - sparkyfitness-server: - image: codewithcj/sparkyfitness_server:v0.16.6.1 - container_name: sparkyfitness-server - restart: unless-stopped - networks: - - sparkyfitness-network - environment: - # Database connection - SPARKY_FITNESS_DB_HOST: sparkyfitness-db - SPARKY_FITNESS_DB_PORT: 5432 - SPARKY_FITNESS_DB_NAME: sparkyfitness_db - SPARKY_FITNESS_DB_USER: sparky - SPARKY_FITNESS_DB_PASSWORD: "${SPARKY_FITNESS_DB_PASSWORD}" - SPARKY_FITNESS_APP_DB_USER: sparky_app - SPARKY_FITNESS_APP_DB_PASSWORD: "${SPARKY_FITNESS_APP_DB_PASSWORD}" - # Security — values sourced from .env (git-crypt) - SPARKY_FITNESS_API_ENCRYPTION_KEY: "${SPARKY_FITNESS_API_ENCRYPTION_KEY}" - BETTER_AUTH_SECRET: "${BETTER_AUTH_SECRET}" - # Application - SPARKY_FITNESS_FRONTEND_URL: "https://fitness.castaldifamily.com" - SPARKY_FITNESS_LOG_LEVEL: ERROR - NODE_ENV: production - TZ: Etc/UTC - SPARKY_FITNESS_DISABLE_SIGNUP: "false" - SPARKY_FITNESS_FORCE_EMAIL_LOGIN: "true" - ALLOW_PRIVATE_NETWORK_CORS: "false" - PUID: 1000 - GUID: 1000 - # Garmin microservice (disabled — service not yet stable) - # GARMIN_MICROSERVICE_URL: http://sparkyfitness-garmin:8000 - volumes: - - /mnt/appdata/sparkyfitness/data/backup:/app/SparkyFitnessServer/backup - - /mnt/appdata/sparkyfitness/data/uploads:/app/SparkyFitnessServer/uploads - depends_on: - sparkyfitness-db: - condition: service_healthy - healthcheck: - test: ["CMD-SHELL", "wget -qO- http://localhost:3010/ 2>&1 | grep -q '.' || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 60s - deploy: - resources: - limits: - cpus: "1.5" - memory: 512M - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" - - # ── Frontend ───────────────────────────────────────────────────────────────── - sparkyfitness-frontend: - image: codewithcj/sparkyfitness:v0.16.6.1 - container_name: sparkyfitness-frontend - restart: unless-stopped - networks: - - proxy-net - - sparkyfitness-network - environment: - SPARKY_FITNESS_FRONTEND_URL: "https://fitness.castaldifamily.com" - SPARKY_FITNESS_SERVER_HOST: sparkyfitness-server - SPARKY_FITNESS_SERVER_PORT: 3010 - PUID: 1000 - GUID: 1000 - depends_on: - sparkyfitness-server: - condition: service_healthy - healthcheck: - test: ["CMD-SHELL", "wget -q --spider http://localhost:80/ || exit 1"] - interval: 30s - timeout: 10s - retries: 3 - start_period: 30s - labels: - # Enable Traefik - - "traefik.enable=true" - # HTTPS Router - - "traefik.http.routers.sparkyfitness.rule=Host(`fitness.castaldifamily.com`)" - - "traefik.http.routers.sparkyfitness.entrypoints=websecure" - - "traefik.http.routers.sparkyfitness.tls=true" - - "traefik.http.routers.sparkyfitness.tls.certresolver=cloudflare" - - "traefik.http.routers.sparkyfitness.service=sparkyfitness" - - "traefik.http.routers.sparkyfitness.middlewares=security-headers@file" - # Service definition - - "traefik.http.services.sparkyfitness.loadbalancer.server.port=80" - deploy: - resources: - limits: - cpus: "0.5" - memory: 128M - logging: - driver: "json-file" - options: - max-size: "10m" - max-file: "3" diff --git a/nodes/heimdall/weatherchannel/compose.yaml b/nodes/heimdall/weatherchannel/compose.yaml deleted file mode 100644 index b9070e9..0000000 --- a/nodes/heimdall/weatherchannel/compose.yaml +++ /dev/null @@ -1,33 +0,0 @@ -############################################################### -# Networks -############################################################### -networks: - proxy-net: - name: proxy-net - external: true - -############################################################### -# Services -############################################################### -services: - weatherchannel: - image: ghcr.io/mwood77/ws4kp-international - container_name: weatherchannel - restart: unless-stopped - healthcheck: - test: wget --no-verbose --tries=1 --spider http://localhost:8080/ || exit 1 - start_period: 20s - timeout: 3s - interval: 15s - retries: 3 - networks: - - proxy-net - labels: - - "traefik.enable=true" - - "traefik.http.routers.weatherchannel.entrypoints=websecure" - - "traefik.http.routers.weatherchannel.rule=Host(`weather.castaldifamily.com`)" - - "traefik.http.routers.weatherchannel.tls=true" - - "traefik.http.routers.weatherchannel.tls.certresolver=cloudflare" - - "traefik.http.routers.weatherchannel.service=weatherchannel" - - "traefik.http.services.weatherchannel.loadbalancer.server.port=8080" -