Compare commits

...

15 Commits

23 changed files with 721 additions and 158 deletions

View File

@@ -11,7 +11,7 @@ grep -E "set .port|server_name" /app/nginxproxy/data/nginx/proxy_host/*.conf \
alias nginx='_ports'
# tmp rebuild for hugo development
alias hugo-rebuild='rm -rf public/ && hugo server --appendPort=false --baseURL="/" --ignoreCache'
alias hugo-rebuild='cd /srv/dev/hugo/wiki && ./preview.sh public'
# helpers for quickly editing aliases
alias src='source ~/.bash_aliases'

3
.gitignore vendored
View File

@@ -40,3 +40,6 @@ dump.rdb
# Logs
*.log
# Desktop Services Store
.DS_Store

View File

@@ -14,6 +14,11 @@ Specialized configuration files (JSON) for [Pterodactyl Panel](https://pterodact
For more details on how to import and use these, see the [Eggs README](./eggs/README.md).
### Configuration Files
This repository also tracks global configuration files that maintain system state and environment consistency:
- `.bash_aliases`: Tracks persistent command-line aliases for custom environment workflows.
## Future Scope
This repository is intended to expand into a comprehensive hub for all configuration types beyond Docker and Pterodactyl, including:

View File

@@ -1,7 +1,7 @@
## HARD MANDATE: PLAN BEFORE ACTION
- **No Unapproved State Changes:** You MUST NOT execute any tool that modifies the filesystem, runs shell commands (excluding read-only discovery like `ls` or `grep`), or changes system state without first presenting a detailed plan and receiving an explicit "Directive" (e.g., "proceed") from the user. This rule is absolute. REQUIRED!
## Gemini Added Memories
## Added Memories
- When working on the Hugo wiki, docs, or Caddy routing, immediately run the docs_architecture skill to understand the decoupled architecture, deployment logic, and known Docker/Caddy networking quirks.
- When writing Hugo/Blowfish Markdown front matter, always include 'author', 'date' (ISO-8601), 'lastmod' (YYYY-MM-DD), and 'tags' (list) at the bottom of the metadata block.
- Never perform manual builds or deployments of the Hugo sites to bypass the CI/CD pipeline. Always rely on the Gitea Actions workflows for deployment.

View File

@@ -1,114 +0,0 @@
# AI Agent Infrastructure Strategy: Homelab Management
This document establishes the architecture, workflow rules, and skill definitions for an AI agent to interface with the self-hosted environment using the Model Context Protocol (MCP) and local system aliases.
---
## 1. Primary AI Workspace and Persistence Directive
The agent must treat the `/srv/configs/ai` directory as its absolute source of truth for its own configuration, execution memory, and custom skill scripts.
### Operational Rules
* **Storage Location:** All agent workflows, system prompts, automated rules, markdown runbooks, and custom skill definitions *must* be stored in `/srv/configs/ai/`.
* **State Management:** Before executing any systemic infrastructure review, the agent must read the current context files in this directory to ensure it is operating under the latest user definitions.
* **No Side Effects:** The agent is strictly prohibited from writing or modifying configuration files outside of this path unless explicitly authorized by the user during an interactive session.
---
## 2. Homelable MCP Server Integration
The agent leverages the Model Context Protocol to interface directly with the `homelable-mcp` container running locally. This allows the agent to bind physical network topologies to visual infrastructure diagrams.
### Connection Architecture
* **Location:** Co-located on the same local host node.
* **Transport Method:** Server-Sent Events (SSE) or local port binding.
* **API Endpoint:** `http://localhost:8001`
### Capabilities Matrix
| Permission | Action | Operational Boundaries |
| ---------- | ------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- |
| **Read** | List all nodes, edges, full canvas, pending devices, scan history. | Executed proactively during an audit cycle. |
| **Write** | Add/update/delete nodes and edges, trigger network scans, approve/hide discovered devices. | **DISABLED BY DEFAULT.** Requires explicit, per-action user confirmation. |
---
## 3. Host System Execution Environment & Aliases
The agent interacts with the host infrastructure by executing commands directly within the host shell session. It relies on system aliases defined in the central configuration file.
* **Alias Configuration File:** `/srv/configs/.bash_aliases`
### Required Core Aliases
The agent must parse and respect the following explicit wrappers found in the alias file:
* `docker-service`: Used to query container states, image updates, and running stacks.
* `nginx`: Used to inspect reverse proxy server blocks, active upstreams, and active domain routings.
---
## 4. Skill Definition: Infrastructure Reconciliation & Audit
### Skill Name
`review_homelab_infrastructure`
### Objective
Cross-reference the active compute and reverse-proxy states against the `homelable` canvas topology and the primary markdown infrastructure log.
### Source of Truth Inventory Documentation
* **Target File:** `/srv/docs/public/projects/homelab/homelab_Infra.md`
* **Policy:** The agent must maintain and preserve the existing structural formatting of this file when suggesting updates.
### Execution Workflow
```
[Trigger: Audit Command]
┌────────────────────────────────────────────────────────┐
│ 1. Parse aliases via `/srv/configs/.bash_aliases` │
│ 2. Read active inventory from `homelab_Infra.md` │
│ 3. Fetch live container states via `docker-service` │
│ 4. Fetch proxy mappings via host `nginx` commands │
│ 5. Pull active topology via Homelable MCP (Port 8001) │
└─────────────────────────┬──────────────────────────────┘
┌────────────────────────────────────────────────────────┐
│ Perform 3-Way Reconciliation │
│ (homelab_Infra.md vs. Live States vs. MCP Canvas) │
└─────────────────────────┬──────────────────────────────┘
┌────────────────────────────────────────────────────────┐
│ Generate Diff Report & Proposed Changes (Markdown) │
└─────────────────────────┬──────────────────────────────┘
{ User Explicitly Approves? }
/ \
(Approved) (Denied)
/ \
▼ ▼
┌─────────────────────────┐ ┌─────────────────────────┐
│ - Update MCP Canvas │ │ Abort & Log Audit │
│ - Append/Modify │ │ Failure in Workspace │
│ homelab_Infra.md │ │ (/srv/configs/ai/) │
└─────────────────────────┘ └─────────────────────────┘
```
### Safety and Guardrails
> [!IMPORTANT]
> **Strict Read-Only Default:** The agent is explicitly forbidden from making automatic writes or commits.
> * **No silent canvas updates:** Every node change, edge modification, or scan approval on the Homelable MCP requires manual confirmation.
> * **No silent documentation updates:** Under no circumstances should the agent modify `/srv/docs/public/projects/homelab/homelab_Infra.md` without presenting a clear diff block and receiving confirmation first.
>
>

BIN
ai/skills/.DS_Store vendored

Binary file not shown.

View File

@@ -1,33 +1,42 @@
---
name: docs_architecture
description: Exact technical architecture, routing, and deployment workflow for WompMacho's decoupled Hugo/Gitea/Nginx Wiki infrastructure.
---
# Docs Architecture Skill
This skill documents the exact technical architecture, routing, and deployment workflow for WompMacho's decoupled Hugo/Gitea/Caddy Wiki infrastructure.
This skill documents the exact technical architecture, routing, and deployment workflow for WompMacho's decoupled Hugo/Gitea/Nginx Wiki infrastructure.
## 🏗️ 1. Core Architecture (Decoupled Repositories)
The system is split into three independent Git repositories hosted on `git.wompmacho.com`:
1. **Framework (`/srv/dev/hugo/wiki`)**: Repository `hugo-framework`
* **Purpose**: The central Hugo engine. Contains the Blowfish theme as a submodule (`themes/blowfish`) and the master `config/_default/params.toml`.
* **Rule**: NEVER place content (`.md` files) here. NEVER place environment-specific overrides (like hardcoded `editURLs` or top-level menus) in the global `params.toml`.
* **Purpose**: The central Hugo engine. The Blowfish theme is fully **decoupled/vendored** directly into the project's root folders (`layouts/`, `assets/`, `static/`, `data/`, `i18n/`, `archetypes/`). No theme submodules are used, protecting the site from upstream updates breaking the build.
* **Rule**: NEVER place content (`.md` files) here. NEVER place environment-specific overrides (like hardcoded `editURLs` or top-level menus) in the global config.
2. **Public Wiki (`/srv/docs/public`)**: Repository `docs-public`
* **Purpose**: Public-facing markdown notes, assets (`/static`), and its own top-bar menu configuration (`config/_default/menus.en.toml`).
* **Deployment Target**: `/srv/caddy/sites/wiki`
* **Deployment Target**: `/srv/www/docs-public`
* **Live URL**: `https://wiki.wompmacho.com` (proxied by NPM).
3. **Private Wiki (`/srv/docs/private`)**: Repository `docs-private`
* **Purpose**: Personal, private markdown notes, assets (`/static`), and its own top-bar menu configuration (`config/_default/menus.en.toml`).
* **Deployment Target**: `/srv/caddy/sites/private-wiki`
* **Deployment Target**: `/srv/www/docs-private`
* **Live URL**: `http://10.0.0.190:9897` (or `http://private` via NPM).
## 💻 2. Local Development ("Live Link")
## 💻 2. Local Development ("Live Link" Automation)
When working on content, the user uses **Code-Server**. To see real-time updates without deploying, the `hugo-framework` repository uses symbolic links.
When working on content, the user uses **Code-Server**. To see real-time updates without deploying, the framework repository uses automated symlinking and preview scripts.
* `content -> /srv/docs/public`
* `static -> /srv/docs/public/static`
**Command to start local preview**: `cd /srv/dev/hugo/wiki && hugo server --bind 0.0.0.0 --appendPort=false --baseURL="/"`
* **`preview.sh [public|private]`**:
* Creates symlinks: `content -> /srv/docs/[public|private]` and `static -> /srv/docs/[public|private]/static`.
* Overwrites or symlinks specific config overrides (like `menus.en.toml`).
* Executes `run-dev.sh` to start the local preview server.
* Traps exits (Ctrl+C, normal exit) to cleanly restore standard framework defaults and remove preview symlinks automatically.
* **`run-dev.sh`**:
* Launches the server bound to `0.0.0.0:1313` with code-server proxy parameters.
* Passes `--renderToMemory` to prevent local watch loops and file write latency.
## 🚀 3. CI/CD Pipeline (Gitea Actions)
@@ -35,7 +44,7 @@ Both `docs-public` and `docs-private` have a `.gitea/workflows/deploy.yaml` acti
### The Build Process
1. **Checkout Content**: The runner checks out the markdown content.
2. **Clone Framework**: The runner natively clones the `hugo-framework` (including submodules) using `git clone --recurse-submodules https://git.wompmacho.com/wompmacho/hugo-framework.git`.
2. **Clone Framework**: The runner clones the central `hugo-framework` repository directly. Because the Blowfish theme is fully decoupled, there is no need for submodule initialization.
3. **Inject Content**: Markdown and static files are copied into the framework's workspace.
4. **Inject Dynamic Menus**: The `config/_default/menus.en.toml` is injected, automatically overriding the framework's top navigation bar for that specific site.
5. **Inject Edit Links (CRITICAL RULE)**: The "Edit this page" link on the bottom of posts MUST NOT be configured via a `params.toml` override, as Hugo replaces the entire file instead of merging it, destroying the Blowfish styling.
@@ -45,17 +54,18 @@ Both `docs-public` and `docs-private` have a `.gitea/workflows/deploy.yaml` acti
## 🌐 4. Web Server Routing (Nginx)
The system relies on two extremely lightweight `nginx:alpine` containers defined in `gitea/docker-compose.yaml` to serve static files. They act as "dumb" file servers behind Nginx Proxy Manager (NPM), bypassing the complex Host header validation and 0-byte security issues inherent to Caddy.
The system relies on two extremely lightweight `nginx:alpine` containers defined in `gitea/docker-compose.yaml` to serve static files. They act as file servers behind Nginx Proxy Manager (NPM).
1. **Public (`public_wiki` - Port 9896)**:
* Mounts `/srv/caddy/sites/wiki` as read-only.
* Mounts `/srv/www/docs-public` as read-only.
* NPM routes `wiki.wompmacho.com` to Docker Host IP on port `9896`.
2. **Private (`private_wiki` - Port 9897)**:
* Mounts `/srv/caddy/sites/private-wiki` as read-only.
* Mounts `/srv/www/docs-private` as read-only.
* NPM routes `http://private` to Docker Host IP on port `9897`.
## 🛠️ 5. Known Quirks and Formatting Rules
* **Submodules**: Always use `git submodule add -b main <url>` and ensure the commit pointer is tracked. Do not copy theme files physically.
* **Decoupled Theme**: The Blowfish theme is committed directly in the main codebase. Do not use Git submodules to track the theme.
* **Blowfish Homepage**: If the homepage is blank, it means there are no recent posts. By default, Blowfish's `home` layout only lists files with `type: "article"` or `type: "posts"`. For generic folders, set `type: "page"` and `layout: "list"` in `_index.md`.
* **Frontmatter**: Blowfish expects standard Hugo frontmatter (`date:` as a string, `authors: ["Name"]` as an array). MkDocs-style nested YAML is incompatible and will crash the build.
* **Troubleshooting**: See `references/troubleshooting.md` for handling theme updates, build loops, and TOC configuration.

View File

@@ -0,0 +1,24 @@
# Hugo/Blowfish Troubleshooting & Pitfalls
## 1. Infinite Rebuild Loops
If Hugo triggers a rebuild loop:
* **Cause**: Hugo watches the `resources/` directory, and the generation of cached/busted assets (like `admonitions.scss_...json`) triggers a new rebuild event.
* **Fix**: Add the following to `config/_default/hugo.toml`:
```toml
[watch]
ignore = ["resources/**"]
```
* **Tool Usage**: If the loop persists, start the server with polling to reduce sensitivity:
`hugo server --poll 1s`
## 2. Table of Contents (TOC) Issues
Blowfish manages TOC natively via `params.toml`. Avoid using manual `[TOC]` or `{{< toc >}}` in your Markdown unless explicitly required by a specific layout.
* **Standard Setup**: Ensure `showTableOfContents = true` is set in the `[article]` and `[list]` sections of your `config/_default/params.toml`.
* **Custom Layouts**: If TOC is rendering in the wrong place, it is likely due to an overridden `layouts/_default/single.html`. Revert to the native theme structure (`container` and `prose` classes) so the Blowfish partials can handle the placement.
* **TOC ScrollSpy Highlighting**: If the TOC fails to highlight active headings when scrolling, verify that `smartTOC = true` is enabled in `params.toml`. If it is nested inside the `[article]` section, ensure that the `layouts/partials/toc.html` checks:
`{{ if or .Site.Params.smartTOC .Site.Params.article.smartTOC }}`
to correctly resolve the nested parameter.
## 3. Theme Updates & Customization
* **Version mismatch**: When Hugo updates, theme templates often break (e.g., `can't evaluate field Locale`).
* **Decoupled Theme Installation**: The theme is fully decoupled/vendored in the root folders. If you need to update Blowfish, you will copy/vendor the new templates into `layouts/` and assets into `assets/`, but you must review custom changes to templates such as `layouts/_default/single.html` and `layouts/partials/toc.html` to make sure custom styles and scripts (like multiple TOC elements or active highlight overrides) are preserved.

View File

@@ -0,0 +1,146 @@
---
name: homelab-infrastructure-review
description: >
Performs a 3-way infrastructure reconciliation audit: homelab_Infra.md
documentation (including Nginx Proxy Route mappings) vs. live Docker container
states vs. homelable MCP canvas topology. Also audits the Homepage Dashboard
services configuration to ensure newly proxied services are accessible.
Triggers ONLY on explicit user command. No automation or cron.
version: 1.4.0
author: wompmacho
date: 2026-05-30
platforms: [linux, macos, windows]
metadata:
hermes:
tags: [homelab, infrastructure, audit, reconciliation, mcp, docker, nginx, homepage]
related_skills: [docker-workflows, workflow-management]
---
# Homelab Infrastructure Review
This skill has been refactored into an intelligent, context-aware auditing engine. It uses a YAML configuration (`scripts/audit_config.yaml`) to understand architectural conventions (aliases, ignored containers) and employs a robust, incremental development workflow. **Always test changes in small, atomic steps** (`syntax check -> run`) to avoid complex debugging cycles. The core logic now uses a Python helper (`scripts/load_config.py`) for configuration and `docker inspect` for accurate health checks.
## 1. Core Objectives
This skill provides a highly-disciplined auditing workflow that:
1. **Identifies all live services** on the host.
2. **Identifies all active proxies and domains** for services by parsing Nginx Proxy Manager config files.
3. **Cross-references** them against the source of truth documentation (`homelab_Infra.md`).
4. **Cross-references** them against the `homelable-mcp` visual canvas topology.
5. **Audits the Homepage Dashboard services configuration** (`/srv/configs/docker_compose/homepage/config/services.yaml`) to ensure all proxied services are accessible.
6. **Generates exact proposed changes** to:
- The markdown documentation (`homelab_Infra.md`)
- The `homelable-mcp` visual canvas nodes and edges
- The Homepage Dashboard services config (`services.yaml`), with services correctly split into their relevant hosts/categories.
---
## 2. Trigger Conditions
This skill is invoked **only on explicit, manual user command** — never automatically or periodically.
Acceptable trigger phrases: "review homelab", "infrastructure audit", "reconcile homelab", "homelab review", "check infrastructure".
No cron, no automated polling, and no silent background execution.
---
## 3. Source Inventory and Boundary Map
| Source Component | Path / Port / Endpoint | Permission Boundaries |
|---|---|---|
| **Infra Document** | `/srv/docs/public/projects/homelab/homelab_Infra.md` | Read: ✅ Write: ❌ (requires explicit user confirmation, never run git commands without auth) |
| **Live Docker State** | Host session via `docker ps` | Read: ✅ Write: ❌ (no automated container lifecycle modifications allowed) |
| **Nginx Proxy Configs** | `/app/nginxproxy/data/nginx/proxy_host/*.conf` | Read: ✅ Write: ❌ (read-only proxy blocks mapping) |
| **Homelable Backend API** | `http://localhost:9445/api/v1` | Read: ✅ Write: ❌ (node/edge canvas edits require explicit confirmation) |
| **Homepage Config** | `/srv/configs/docker_compose/homepage/config/services.yaml` | Read: ✅ Write: ❌ (requires explicit user confirmation) |
| **Homepage Layout** | `/srv/configs/docker_compose/homepage/config/settings.yaml` | Read: ✅ Write: ❌ (requires explicit user confirmation) |
| **Workspace Dir** | `/srv/configs/ai/` | Read: ✅ Write: ✅ (agent state and proposed drafts) |
---
## 4. Reconciliation Logic & Execution Steps
### Step 1: Pre-Flight Safety Checks
Run **in order** before beginning any audit cycle. If any check fails, **simply inform the user directly in the session** and abort (do NOT log failures to a local `errors.log` file):
- **Lock Check:** Verify no concurrent audit process holds `/srv/configs/ai/.audit.lock`.
- **Backend Port Check:** Verify port `9445` is actively listening.
- **Backend API Check:** Verify backend returns `HTTP 200` on `/api/v1/health` using the parsed `MCP_SERVICE_KEY` from `/srv/configs/docker_compose/homelabel/.env`.
- **Source Files Check:** Verify both `/srv/configs/.bash_aliases` and `homelab_Infra.md` exist.
### Step 2: Nginx Proxy Parsing
Parse every proxy configuration file `/app/nginxproxy/data/nginx/proxy_host/*.conf` for:
- `server_name` (Proxy Domain name / Route)
- `set $port` (Forwarded Container Port)
Keep an internal lookup dictionary of `Forwarded Port -> Proxy Domain` for comparison.
### Step 3: Homepage Dashboard Audit
Read `/srv/configs/docker_compose/homepage/config/services.yaml` and compare the list of active proxy routes against the dashboard items. Propose adding newly proxied services so the user can navigate to them.
Homepage services must be strictly split into three distinct categories:
1. **Physical Devices & Networking related** (e.g. Proxmox, TrueNAS, Router, Pi-Hole, Moonlight streaming)
2. **Docker Host Containers** (e.g. nginx-proxy-manager, portainer, immich, vaultwarden, paperless-ngx, slopsmith-web)
3. **TrueNAS Host Containers** (e.g. radarr, sonarr, readarr, bazarr, prowlarr, calibre)
### Step 4: Documentation Formatting Standardization
Both the **`## Docker services`** and the **`## Truenas Services`** markdown tables inside `homelab_Infra.md` must strictly maintain this exact 5-column layout:
`| Container Name | Mapped Ports | Access | Proxy Route / Domain | Description / Role |`
Always parse and print matching columns when proposing or applying documentation edits.
### Step 5: Proxmox LXC/VM Auditing
Proxmox host virtualization represents nodes (containers/VMs) that communicate on the LAN subnet `10.0.0.0/16`.
- Parse the Proxmox Node Table under `### Proxmox node (laptop-proxmox)` in `homelab_Infra.md`.
- Retrieve all network canvas nodes from `http://localhost:9445/api/v1/nodes` matching types `vm`, `lxc`, or direct hosts.
- Compare and verify that active virtualization nodes on your network (such as `invidious` on `10.0.0.217` or `pterodactyl` on `10.0.0.110`) exist inside the documentation table. Propose additions for any unregistered LXCs or VMs.
### Step 6: Homelable MCP Canvas Styling Standards
When registering new Docker containers on the visual canvas using the `POST` endpoint at `/api/v1/nodes`, you must strictly match the styling of other standard services (use `gitea_runner` as reference):
- `"type"` must be `"docker_container"`
- `"hostname"` must be `"docker"`
- `"ip"` must be `""` (empty string, as they reside behind parent container networking)
- `"width"` must be `140.0`
- `"height"` must be `50.0`
- `"bottom_handles"` must be `1`
- `"custom_colors"` must be `{"show_services": false}`
- `"parent_id"` must be set to the Docker parent host node UUID (e.g. `ee61411a-018a-4471-8a76-bcd4a1f5579a`)
---
## 5. Safety and Guardrails
> [!IMPORTANT]
> 1. **No silent writes:** All modifications to `homelab_Infra.md`, the `homelable-mcp` canvas, or `services.yaml` must be presented as a clear diff block and receive explicit, manual user validation.
> 2. **NO UNAUTHORIZED GIT COMMANDS:** Never execute any Git commands (such as `git add`, `git commit`, `git status`, `git diff`, etc.) without the user's explicit, per-action authorization.
> 3. **Preserve Comments:** Do not remove, alter, or strip existing documentation comments or section text blocks.
> 4. **No Errors Log File:** Do not append failures to a local `errors.log` file. Output error details directly to the active console stream to keep workspace logs clean.
---
## 6. Technical Pitfalls & Workarounds
> [!WARNING]
> - **Windows CRLF line endings (`\r\n`):** The Homepage dashboard's `services.yaml` contains Windows CRLF terminators. Traditional bash/unix line-ending regex matches (such as `$`) will **silently fail** to match titles or categories. Always run `tr -d '\r'` to sanitize lines before attempting pattern matches.
> - **Direct REST API bypass:** Querying `homelable-mcp` on `8001` via SSE requires an async JSON-RPC transport negotiation which is fragile and slow. Instead, bypass the MCP layer entirely and query the `homelable-backend` REST API directly on port `9445` at `/api/v1/nodes`. Pass the header `"X-MCP-Service-Key"` parsed from `/srv/configs/docker_compose/homelabel/.env` for instant, authenticated, and robust network nodes lookup.
- **Active Port Verification (Stale Config Filter):** Nginx Proxy Manager configuration files on disk can be stale/leftover (e.g. Moonlight). To completely prevent reporting decommissioned configs, always verify that the port associated with any Nginx config is **actively listening on the host** (or mapped in active docker container port-mappings) before reporting it as an active proxy. If the port is inactive, ignore the proxy block completely.
- **TrueNAS SCALE Node Socket Verification:** Rather than setting up fragile credentials or SSH logins, TrueNAS SCALE container port verification is achieved securely and in milliseconds using lightweight, non-destructive `/dev/tcp/10.0.0.2/[port]` network socket connection handshakes.
- **Proxmox VE API Live Verification:** To verify running LXCs and VMs, query `https://10.0.0.142:8006/api2/json/cluster/resources?type=vm` directly. Load `HOMEPAGE_VAR_proxmox_password` dynamically from `/srv/configs/docker_compose/homepage/.env` at runtime and strip outer double quotes before passing to the Authorization headers. Never hardcode credentials in codebase or transcripts.
- **Homepage Settings Layout Syncing:** When renaming category names in `/srv/configs/docker_compose/homepage/config/services.yaml` (such as `Arr` -> `Truenas Containers`, and `Containers` -> `Docker Containers`), the layout block names inside `/srv/configs/docker_compose/homepage/config/settings.yaml` under `layout:` **must be updated identically** or the cards will fail to render on the dashboard.
- **Do NOT Invent Public Proxies:** Do not invent public proxy domains or automatically propose changing local/VPN hrefs (such as `http://radarr/` or `http://calibre/`) to public domains (like `radarr.wompmacho.com`) in homepage configurations. They must remain as local/VPN URLs exactly as currently configured to preserve network isolation and security.
- **Multi-Alias Substring Matcher:** Nginx Proxy configs often specify multiple space-separated aliases (e.g. `server_name vaultwarden vaultwarden.wompmacho.com;`). When searching for these in `services.yaml` to detect missing cards, token-split the domains and verify if *any* alias matches as a substring within the homepage card `href` or name (case-insensitively). This prevents false-positives (such as flagging Vaultwarden or Gitea as missing).
---
## 7. Skill File Structure
All scripts and files associated with this skill are stored in this self-contained directory:
- `SKILL.md` — Custom skill documentation and procedures.
- `scripts/homelab-infrastructure-review.sh` — Bash shell auditing script.
- `scripts/mcp_client.py` — Python client for direct database and API queries.
- `errors.log` — Legacy log file (decommissioned per safety rules — errors print straight to console).
- `.audit.lock` — Legitimate lock file preventing concurrent executions.

View File

@@ -0,0 +1,46 @@
# Homelable Backend API Reference
Direct REST API access to Pouzor/homelable visualizer backend on port 9445. Bypasses the complex and slow Server-Sent Events (SSE) handshake required on port 8001.
## Authentication
The backend API requires an unauthenticated sub-app service key. Read this key dynamically from:
`/srv/configs/docker_compose/homelabel/.env` -> `MCP_SERVICE_KEY`
Inject it into HTTP request headers:
`X-MCP-Service-Key: <MCP_SERVICE_KEY>`
## Endpoints
### 1. Health Check
`GET http://localhost:9445/api/v1/health`
Returns `{"status": "ok"}` on success (HTTP 200).
### 2. Retrieve Canvas Nodes
`GET http://localhost:9445/api/v1/nodes`
Returns a JSON array of active canvas nodes.
Example Node Object:
```json
{
"type": "computer",
"label": "Game Pc",
"hostname": "DESKTOP-BRHGMFT",
"ip": "10.0.0.109",
"status": "online",
"services": [
{
"port": 11434,
"protocol": "tcp",
"service_name": "Ollama"
}
],
"id": "3fc16e75-9418-41b1-b4a2-d3859c9ce431"
}
```
### 3. Retrieve Canvas Edges
`GET http://localhost:9445/api/v1/edges`
Returns active connection edges between nodes.
### 4. Trigger Network Scan
`POST http://localhost:9445/api/v1/scan/trigger`

View File

@@ -0,0 +1,68 @@
# Homelable Backend Direct REST API Reference
Instead of negotiating a complex and slow Server-Sent Events (SSE) stream over port `8001` (which requires async JSON-RPC sessions and frequently times out), query the REST API on `homelable-backend` directly.
## Endpoint & Port
- **Host Port:** `9445`
- **Base URL:** `http://localhost:9445/api/v1`
## Authentication Header
The backend is authenticated using a special service key header:
```
X-MCP-Service-Key: <MCP_SERVICE_KEY>
```
### Loading Credentials Dynamically
To keep keys safe and secure (without leaking them to the LLM context window), read the key dynamically from the local `.env` configuration file:
- **Path:** `/srv/configs/docker_compose/homelabel/.env`
- **Variable Name:** `MCP_SERVICE_KEY`
## Common Endpoints
### 1. Health Check
- **Path:** `/api/v1/health`
- **Method:** `GET`
- **Response:** `{"status": "ok"}`
### 2. Retrieve Nodes (Canvas Devices)
- **Path:** `/api/v1/nodes`
- **Method:** `GET`
- **Response:** Array of node objects:
```json
[
{
"id": "3fc16e75-9418-41b1-b4a2-d3859c9ce431",
"type": "computer",
"label": "Game Pc",
"ip": "10.0.0.109",
"status": "online",
"services": [
{
"port": 11434,
"protocol": "tcp",
"service_name": "Ollama"
}
]
}
]
```
## Python Integration Example
```python
import os
import requests
env_path = "/srv/configs/docker_compose/homelabel/.env"
service_key = ""
if os.path.exists(env_path):
with open(env_path, "r") as f:
for line in f:
if line.startswith("MCP_SERVICE_KEY="):
service_key = line.split("=", 1)[1].strip()
headers = {"X-MCP-Service-Key": service_key}
resp = requests.get("http://localhost:9445/api/v1/nodes", headers=headers)
nodes = resp.json()
```

View File

@@ -0,0 +1,33 @@
# Teach the skill how to map running containers to their documented names
# This resolves false positives for services running behind a single proxy container.
service_aliases:
nicotine: gluetun
torrent: gluetun
paperless-db: paperless-ngx
paperless-broker: paperless-ngx
guac-postgresql: guacamole
gitea-db-1: gitea
npm-sync: nginx-proxy-manager
homelable-mcp: homelable
homelable-backend: homelable
homelable-frontend: homelable
# Teach the skill how to map container names to their visual canvas labels
# This resolves false positives for services with user-friendly canvas names.
canvas_aliases:
docs-public: hugo (public)
docs-private: hugo (private)
# A simple list of containers to ALWAYS ignore during the audit.
# This is for transient, dev, or intentionally undocumented containers.
ignore_containers:
- openai
- slopsmith-web
- pihole-dns-shim # Managed by pihole LXC, not documented as a separate service.
- immich_machine_learning # Sub-service of Immich
- immich_postgres # Sub-service of Immich
- immich_redis # Sub-service of Immich
- guac-postgresql # Sub-service of Guacamole
- nginx-proxy-manager # Infrastructure, not a user-facing service
- npm-sync # Sub-service of Nginx Proxy Manager
- cloudflare-ddns # Infrastructure, not a user-facing service

View File

@@ -0,0 +1,127 @@
#!/usr/bin/env bash
#
# homelab-infrastructure-review.sh
# Triggers a full, intelligent, context-aware reconciliation audit.
#
set -euo pipefail
# ── Configuration & Logging ──────────────────────────────────────────────────
AUDIT_CONFIG_FILE="/srv/configs/ai/skills/homelab-infrastructure-review/scripts/audit_config.yaml"
INFRA_FILE="/srv/docs/public/projects/homelab/homelab_Infra.md"
LOCK_FILE="/srv/configs/ai/.audit.lock"
log_info() { echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') $*"; }
log_error() { echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') $*" >&2; }
# ── Pre-flight & Lock ────────────────────────────────────────────────────────
acquire_lock() {
if [ -f "$LOCK_FILE" ]; then
local pid; pid=$(cat "$LOCK_FILE" 2>/dev/null)
if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
log_error "Audit already running (PID $pid). Aborting."; exit 1
fi
log_info "Stale lock file found. Cleaning up."; rm -f "$LOCK_FILE"
fi
echo $$ > "$LOCK_FILE"; trap 'rm -f "$LOCK_FILE"' EXIT
}
preflight_checks() {
if [ ! -f "$AUDIT_CONFIG_FILE" ]; then log_error "audit_config.yaml not found. Aborting."; exit 1; fi
log_info "All pre-flight checks passed."
}
# ── Data Sources ─────────────────────────────────────────────────────────────
get_infra_docs() {
local in_services_section=false
while IFS= read -r line || [ -n "$line" ]; do
if [[ "$line" =~ ^##[[:space:]]+(Docker|Truenas)[[:space:]]+services ]]; then
in_services_section=true
elif [[ "$line" =~ ^##[[:space:]] && ! "$line" =~ ^##[[:space:]]+(Docker|Truenas)[[:space:]]+services ]]; then
in_services_section=false
fi
if $in_services_section && [[ "$line" =~ ^\|[[:space:]]*\*\*([a-zA-Z0-9_-]+)\*\* ]]; then
IFS='|' read -r _ name _ <<< "$line"; printf '%s\n' "$(echo "$name" | tr -d '*' | xargs)"
fi
done < "$INFRA_FILE"
}
get_live_docker_containers() {
while IFS= read -r line; do
local name; name=$(echo "$line" | jq -r '.Names')
local health; health=$(docker inspect --format '{{if .State.Health}}{{.State.Health.Status}}{{else}}no_health_check{{end}}' "$name" 2>/dev/null || echo "error")
local status;
case "$health" in
healthy) status="healthy";;
unhealthy) status="UNHEALTHY";;
*) status="no_health_check";;
esac
printf '%s|%s\n' "$name" "$status"
done <<< "$(docker ps --format '{{json .}}')"
}
get_mcp_nodes() {
local service_key=""; if [ -f "/srv/configs/docker_compose/homelabel/.env" ]; then service_key=$(grep -E "^MCP_SERVICE_KEY=" "/srv/configs/docker_compose/homelabel/.env" | cut -d'=' -f2-); fi
curl -s -H "X-MCP-Service-Key: $service_key" "http://localhost:9445/api/v1/nodes" | jq -r '.[]? | .label // .name // "unknown"'
}
# ── Reconciliation Engine ────────────────────────────────────────────────────
if [ -f "$AUDIT_CONFIG_FILE" ]; then
source <(python3 /srv/configs/ai/skills/homelab-infrastructure-review/scripts/load_config.py "$AUDIT_CONFIG_FILE")
fi
reconcile() {
local doc_services=(); while IFS= read -r name; do doc_services+=("$(echo "$name"|tr '[:upper:]' '[:lower:]')"); done < <(get_infra_docs)
local live_services=(); local untracked_report=""; local unhealthy_report=""
while IFS='|' read -r name status; do
local lower_name; lower_name=$(echo "$name" | tr '[:upper:]' '[:lower:]')
live_services+=("$lower_name")
if [[ " ${IGNORE_CONTAINERS} " == *" ${lower_name} "* ]]; then continue; fi
local found=false; for doc in "${doc_services[@]}"; do [ "$lower_name" = "$doc" ] && found=true && break; done
local alias_var="SERVICE_ALIAS_${lower_name//-/_}"
if ! $found && [[ -v $alias_var ]]; then
local target="${!alias_var}"; for doc in "${doc_services[@]}"; do [ "$target" = "$doc" ] && found=true && break; done
fi
if ! $found; then untracked_report+=" ⚠ **$name** is running but not documented.\n"; fi
if [[ "$status" == "UNHEALTHY" ]]; then unhealthy_report+=" ✗ **$name** is **UNHEALTHY**.\n"; fi
done < <(get_live_docker_containers)
local missing_report=""
for doc in "${doc_services[@]}"; do
if [ "$doc" = "dev" ] || [ "$doc" = "calibre" ]; then continue; fi # Exceptions
local found=false; for live in "${live_services[@]}"; do [ "$doc" = "$live" ] && found=true && break; done
if ! $found; then missing_report+=" ✗ **$doc** is documented but not running.\n"; fi
done
local mcp_nodes=(); while IFS= read -r label; do mcp_nodes+=("$(echo "$label"|tr '[:upper:]' '[:lower:]'|tr ' _-' ' ')"); done < <(get_mcp_nodes)
local canvas_report=""
for name in "${live_services[@]}"; do
if [[ " ${IGNORE_CONTAINERS} " == *" ${name} "* ]]; then continue; fi
local found=false; for node in "${mcp_nodes[@]}"; do [[ "$node" == *"$name"* || "$name" == *"$node"* ]] && found=true && break; done
local alias_var="CANVAS_ALIAS_${name//-/_}"
if ! $found && [[ -v $alias_var ]]; then
local target="${!alias_var}"; for node in "${mcp_nodes[@]}"; do [ "$target" = "$node" ] && found=true && break; done
fi
if ! $found; then canvas_report+=" ⚠ **$name** is running but not on the canvas.\n"; fi
done
printf "## Homelab Audit Report\nGenerated: $(date)\n\n"
printf "### Docker vs. Documentation\n"
if [ -z "$untracked_report$missing_report$unhealthy_report" ]; then
printf " ✅ All services are aligned and healthy.\n"
else
printf "%s%s%s" "$untracked_report" "$missing_report" "$unhealthy_report"
fi
printf "\n### Docker vs. Canvas\n"
if [ -z "$canvas_report" ]; then printf " ✅ All services are mapped.\n"; else printf "%s" "$canvas_report"; fi
}
# ── Main Execution ───────────────────────────────────────────────────────────
main() {
log_info "Starting Homelab Infrastructure Audit"
acquire_lock
preflight_checks
log_info "Running reconciliation..."
reconcile
}
main "$@"

View File

@@ -0,0 +1,30 @@
import yaml
import sys
def main(config_path):
try:
with open(config_path, 'r') as f:
config = yaml.safe_load(f)
if 'service_aliases' in config and config['service_aliases']:
for key, value in config['service_aliases'].items():
print(f"export SERVICE_ALIAS_{key.replace('-', '_')}='{value}'")
if 'canvas_aliases' in config and config['canvas_aliases']:
for key, value in config['canvas_aliases'].items():
print(f"export CANVAS_ALIAS_{key.replace('-', '_')}='{value}'")
if 'ignore_containers' in config and config['ignore_containers']:
ignore_list = " ".join(config['ignore_containers'])
print(f"export IGNORE_CONTAINERS='{ignore_list}'")
except Exception as e:
print(f"# Config load failed: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == "__main__":
if len(sys.argv) > 1:
main(sys.argv[1])
else:
print("# Usage: python load_config.py <path_to_config.yaml>", file=sys.stderr)
sys.exit(1)

View File

@@ -0,0 +1,115 @@
#!/usr/bin/env python3
"""
MCP Homelable Integration Module.
Communicates directly with the homelable-backend on port 9445
for canvas topology queries, node/edge retrieval, and scanning.
"""
import os
import json
import logging
import time
from typing import Optional, List, Dict
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
BACKEND_URL = "http://localhost:9445/api/v1"
ENV_PATH = "/srv/configs/docker_compose/homelabel/.env"
def _get_service_key() -> str:
"""Read service key dynamically from .env to authenticate request securely."""
try:
if os.path.exists(ENV_PATH):
with open(ENV_PATH, "r") as f:
for line in f:
if line.startswith("MCP_SERVICE_KEY="):
return line.split("=", 1)[1].strip()
except Exception as exc:
logger.error(f"Failed to read service key from {ENV_PATH}: {exc}")
return ""
def health_check(endpoint: str = BACKEND_URL) -> bool:
"""Check if homelable-backend endpoint is reachable."""
try:
import requests
except ImportError:
# Socket check fallback
import socket
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(3)
s.connect(('localhost', 9445))
s.close()
logger.info("Backend socket check: port 9445 is open")
return True
except (ConnectionRefusedError, socket.timeout, OSError):
logger.warning("Backend socket check failed: port 9445 unreachable")
return False
key = _get_service_key()
headers = {"X-MCP-Service-Key": key}
try:
resp = requests.get(f"{endpoint}/health", headers=headers, timeout=5)
if resp.status_code == 200:
logger.info("Homelable-backend health check: OK")
return True
else:
logger.warning(f"Homelable-backend health check failed with HTTP {resp.status_code}")
return False
except requests.RequestException as exc:
logger.warning(f"Homelable-backend health check exception: {exc}")
return False
def fetch_nodes(endpoint: str = BACKEND_URL) -> Optional[List[Dict]]:
"""Fetch nodes directly from homelable-backend."""
if not health_check(endpoint):
return None
try:
import requests
except ImportError:
logger.error("requests library not installed")
return None
key = _get_service_key()
headers = {"X-MCP-Service-Key": key}
try:
resp = requests.get(f"{endpoint}/nodes", headers=headers, timeout=5)
resp.raise_for_status()
nodes = resp.json()
logger.info(f"Fetched {len(nodes)} nodes directly from backend")
return nodes
except requests.RequestException as exc:
logger.error(f"Failed to fetch nodes from backend: {exc}")
return None
def log_mcp_error(source: str, output: str) -> None:
"""Write MCP-related errors to errors.log."""
timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
log_line = f"[{timestamp}] ERROR: Homelable {source} {output}.\n"
errors_log_path = "/srv/configs/ai/errors.log"
try:
with open(errors_log_path, "a") as f:
f.write(log_line)
except OSError:
print(log_line, file=__import__("sys").stderr)
if __name__ == "__main__":
logger.info("Testing backend integration...")
if health_check():
nodes = fetch_nodes()
if nodes:
print(f"\nSuccessfully retrieved {len(nodes)} nodes:")
for node in nodes[:5]:
print(f" - {node.get('label', 'unknown')} ({node.get('ip', 'no IP')}) [Status: {node.get('status', 'n/a')}]")
else:
print("Failed to retrieve nodes.")
else:
print("Backend is unreachable.")

View File

@@ -23,3 +23,12 @@ This skill allows Gemini to efficiently manage the lifecycle of project workflow
- **Action Items**:
- [ ] [Specific task]
- **Verification**: [Public-domain method to verify success]
---
## Pitfalls & Best Practices
- **Avoid Absolute Positioning in Layouts**: When customizing themes, prefer Flexbox or Grid over `position: absolute` for structural elements like Sidebars or TOCs to prevent content overlap and preserve responsive layout integrity.
- **Global Theme Settings**: For TOC rendering in Blowfish, rely on `params.toml` configuration (`showTableOfContents = true`) rather than custom Markdown shortcodes (`[TOC]` or `{{< toc >}}`), which are often theme-incompatible.
- **Custom CSS vs Theme Overrides**: Always use `assets/css/custom.css` for structural overrides. Never modify theme-internal files (`themes/blowfish/...`), as they will be overwritten during updates.
- **Container Widths**: When overriding container widths, use relative units (percentages) and `!important` sparingly to avoid breaking theme-defined responsive breakpoints.

18
docker_compose/farmOS/.gitignore vendored Normal file
View File

@@ -0,0 +1,18 @@
# ------------------------------------------------------------------------------
# SECRETS AND CREDENTIALS (NEVER COMMIT THESE)
# ------------------------------------------------------------------------------
# Environment variables (API keys, DB passwords, etc.)
.env
.env.*
!.env.example
# Local Development DBs (often contain real-ish data)
*.db
*.sqlite
dump.rdb
# Data
/keys
/sites
/db

View File

@@ -0,0 +1,40 @@
# farmOS - https://farmos.org/ - https://git.drupalcode.org/project/farm/-/blob/4.x/docker/docker-compose.production.yml?ref_type=heads
---
services:
farmOS-db:
container_name: farmOS-db
image: postgres:17
volumes:
- './db:/var/lib/postgresql/data'
environment:
# Set a strong password and optionally change the user/database name.
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
restart: unless-stopped
farmOS-www:
container_name: farmOS-www
depends_on:
- farmOS-db
# Update this to the latest stable version before deploying.
image: farmos/farmos:latest
volumes:
- './sites:/opt/drupal/web/sites'
- './keys:/opt/drupal/keys'
ports:
- '${FARMOS_HTTP_PORT}:80'
restart: unless-stopped
labels:
# Nginx Proxy Manager Automation
- "npm.proxy.domains=farm.wompmacho.com"
- "npm.proxy.host=${DOCKER_HOST_IP}"
- "npm.proxy.port=${FARMOS_HTTP_PORT}"
- "npm.proxy.scheme=http"
- "npm.proxy.ssl_verify=false"
- "npm.proxy.websockets=true"
- "npm.proxy.ssl.force=true"
- "npm.proxy.ssl.certificate.id=2"
#- "npm.proxy.advanced.config=location = / { return 301 /guacamole/; }"
# Pi-hole Automation (Point to Docker Host/Proxy IP)
- "pihole.custom-record=[[\"farm.wompmacho.com\", \"${DOCKER_HOST_IP}\"]]"

View File

@@ -2,7 +2,7 @@
---
services:
backend:
image: ghcr.io/pouzor/homelable-backend:latest
image: ghcr.io/pouzor/homelable-backend:2.5.1
container_name: homelable-backend
restart: unless-stopped
env_file:
@@ -18,7 +18,7 @@
- "${HOMELABEL_BACK_PORT}:8000"
mcp:
image: ghcr.io/pouzor/homelable-mcp:latest
image: ghcr.io/pouzor/homelable-mcp:2.5.1
container_name: homelable-mcp
restart: unless-stopped
env_file:
@@ -32,7 +32,7 @@
frontend:
#image: ghcr.io/pouzor/homelable-frontend-standalone:latest # standalone version
image: ghcr.io/pouzor/homelable-frontend:latest
image: ghcr.io/pouzor/homelable-frontend:2.5.1
container_name: homelable-frontend
restart: unless-stopped
ports:
@@ -41,12 +41,12 @@
- backend
labels:
# Nginx Proxy Manager Automation
- "npm.proxy.domains=homelable"
- "npm.proxy.domains=homelable.wompmacho.com"
- "npm.proxy.host=${DOCKER_HOST_IP}"
- "npm.proxy.port=${HOMELABEL_FRONT_PORT}"
- "npm.proxy.scheme=http"
#- "npm.proxy.websockets=true"
- "npm.proxy.websockets=true"
#- "npm.proxy.ssl_verify=false"
#- "npm.proxy.ssl.force=true"
#- "npm.proxy.ssl.certificate.id=1"
- "pihole.custom-record=[[\"homelable\", \"${DOCKER_HOST_IP}\"]]"
- "npm.proxy.ssl.force=true"
- "npm.proxy.ssl.certificate.id=2"
- "pihole.custom-record=[[\"homelable.wompmacho.com\", \"${DOCKER_HOST_IP}\"]]"

View File

@@ -56,7 +56,7 @@
type: pterodactyl
url: https://games.wompmacho.com
key: {{HOMEPAGE_VAR_pterodactyl_apikey}}
- Arr:
- Truenas Containers:
- Jellyfin:
icon: jellyfin.svg
href: "https://jellyfin.wompmacho.com/"
@@ -151,7 +151,7 @@
type: prowlarr
url: http://truenas:30050
key: {{HOMEPAGE_VAR_prowlarr_apikey}}
- Containers:
- Docker Containers:
- vaultwarden:
icon: vaultwarden.svg
href: "https://vaultwarden.wompmacho.com"
@@ -197,11 +197,6 @@
href: "https://wompmacho.com/login"
target: _self
description: LinkStack
# - sure:
# icon: http://sure/assets/logomark-color-92ed1e7e.svg
# href: "http://sure/"
# target: _self
# description: sure finance app
- paperless:
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/png/paperless-ngx.png
href: "http://paperless/"
@@ -248,11 +243,11 @@
href: "http://reaper/"
target: _self
description: reaper in docker
- Slopsmith-Web:
icon: https://cdn2.steamgriddb.com/icon/dfb61b74af460c2fd68bb8266f9f0814/32/256x256.png
href: "https://slopsmith/"
target: _self
description: slopsmith in docker
# - Slopsmith-Web:
# icon: https://cdn2.steamgriddb.com/icon/dfb61b74af460c2fd68bb8266f9f0814/32/256x256.png
# href: "https://slopsmith/"
# target: _self
# description: slopsmith in docker
- guacamole:
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/apache-guacamole.svg
href: "https://guac.wompmacho.com/"
@@ -270,6 +265,11 @@
description: neko browser
- homelable:
icon: https://cdn.jsdelivr.net/gh/homarr-labs/dashboard-icons/svg/homelable.svg
href: "http://homelable/view?key=live"
href: "https://homelable.wompmacho.com/view?key=live"
target: _self
description: homelable
- farmOS:
icon: https://cdn.fosstodon.org/accounts/avatars/109/321/909/625/316/786/original/763a2ed219103fd0.jpg
href: "https://farm.wompmacho.com/"
target: _self
description: farmOS

View File

@@ -23,10 +23,10 @@ layout:
useEqualHeights: true
style: row
columns: 4
Containers:
Docker Containers:
style: row
columns: 8
Arr:
Truenas Containers:
useEqualHeights: true
style: row
columns: 5

View File

@@ -1,6 +1,6 @@
# Open WebUI - https://docs.openwebui.com/getting-started/quick-start/
services:
openwebui:
openai:
container_name: openai
image: ghcr.io/open-webui/open-webui:latest
restart: unless-stopped

View File

@@ -1,12 +1,14 @@
# paperless-ngx - https://awesome-docker-compose.com/paperless-ngx
services:
broker:
container_name: paperless-broker
image: docker.io/library/redis:7
restart: unless-stopped
volumes:
- ${PAPERLESS_DATA}/redis-data:/data
db:
container_name: paperless-db
image: docker.io/library/postgres:16
restart: unless-stopped
volumes:
@@ -17,6 +19,7 @@ services:
POSTGRES_PASSWORD: ${DB_PASSWORD}
webserver:
container_name: paperless-ngx
image: ghcr.io/paperless-ngx/paperless-ngx:latest
restart: unless-stopped
depends_on: