diff --git a/Dockerfile b/Dockerfile index 27f3aed..de4d6a8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,8 @@ FROM node:22-slim AS base -# Install bash & curl for entrypoint script compatibility, graphicsmagick for pdf2pic, and vips-dev & build-base for sharp -RUN apt-get update && apt-get install -y bash curl graphicsmagick libvips-dev build-essential +# Install bash & curl for entrypoint script compatibility, graphicsmagick for pdf2pic, and vips-dev & build-base for sharp +RUN apt-get update && apt-get install -y bash curl graphicsmagick libvips-dev build-essential \ + && rm -rf /var/lib/apt/lists/* # All deps stage FROM base AS deps @@ -29,14 +30,14 @@ ARG BUILD_DATE ARG VCS_REF # Labels -LABEL org.opencontainers.image.title="Project N.O.M.A.D" \ - org.opencontainers.image.description="The Project N.O.M.A.D Official Docker image" \ +LABEL org.opencontainers.image.title="Project N.O.M.A.D — Homelab Edition" \ + org.opencontainers.image.description="Network Operations Monitoring and Automation Dashboard — Container-native homelab platform" \ org.opencontainers.image.version="${VERSION}" \ org.opencontainers.image.created="${BUILD_DATE}" \ org.opencontainers.image.revision="${VCS_REF}" \ org.opencontainers.image.vendor="Crosstalk Solutions, LLC" \ - org.opencontainers.image.documentation="https://github.com/CrosstalkSolutions/project-nomad/blob/main/README.md" \ - org.opencontainers.image.source="https://github.com/CrosstalkSolutions/project-nomad" \ + org.opencontainers.image.documentation="https://github.com/DocwatZ/project-nomad-homelab-edition/blob/main/README.md" \ + org.opencontainers.image.source="https://github.com/DocwatZ/project-nomad-homelab-edition" \ org.opencontainers.image.licenses="Apache-2.0" ENV NODE_ENV=production @@ -47,5 +48,13 @@ COPY --from=build /app/build /app COPY package.json /app/version.json COPY admin/docs /app/docs COPY README.md /app/README.md + +# Create storage directory with proper permissions +RUN mkdir -p /app/storage && chown -R node:node /app/storage + EXPOSE 8080 + +HEALTHCHECK --interval=30s --timeout=10s --start-period=60s --retries=5 \ + CMD curl -f http://localhost:8080/api/health || exit 1 + CMD ["node", "./bin/server.js"] \ No newline at end of file diff --git a/README.md b/README.md index 89c2e17..5bbcdf7 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@
-# Project N.O.M.A.D. -### Node for Offline Media, Archives, and Data +# Project N.O.M.A.D. — Homelab Edition +### Network Operations Monitoring and Automation Dashboard -**Knowledge That Never Goes Offline** +**Knowledge That Never Goes Offline — Now Container-Native for Your Homelab** [![Website](https://img.shields.io/badge/Website-projectnomad.us-blue)](https://www.projectnomad.us) [![Discord](https://img.shields.io/badge/Discord-Join%20Community-5865F2)](https://discord.com/invite/crosstalksolutions) @@ -14,34 +14,94 @@ --- -Project N.O.M.A.D. is a self-contained, offline-first knowledge and education server packed with critical tools, knowledge, and AI to keep you informed and empowered—anytime, anywhere. +Project N.O.M.A.D. Homelab Edition is a container-native fork of [Project N.O.M.A.D.](https://github.com/CrosstalkSolutions/project-nomad), optimized for NAS systems and homelab environments. It runs as a portable Docker Compose stack on **Unraid**, **TrueNAS SCALE**, and any standard Docker host. -## Installation & Quickstart -Project N.O.M.A.D. can be installed on any Debian-based operating system (we recommend Ubuntu). Installation is completely terminal-based, and all tools and resources are designed to be accessed through the browser, so there's no need for a desktop environment if you'd rather setup N.O.M.A.D. as a "server" and access it through other clients. +## Quick Start (Docker Compose) -*Note: sudo/root privileges are required to run the install script* - -#### Quick Install ```bash -sudo apt-get update && sudo apt-get install -y curl && curl -fsSL https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/install_nomad.sh -o install_nomad.sh && sudo bash install_nomad.sh +# 1. Clone the repository +git clone https://github.com/DocwatZ/project-nomad-homelab-edition.git +cd project-nomad-homelab-edition + +# 2. Configure environment +cp .env.example .env +sed -i "s/^APP_KEY=replaceme/APP_KEY=$(openssl rand -hex 32)/" .env +sed -i "s/^DB_PASSWORD=replaceme/DB_PASSWORD=$(openssl rand -base64 24)/" .env +sed -i "s/^MYSQL_ROOT_PASSWORD=replaceme/MYSQL_ROOT_PASSWORD=$(openssl rand -base64 24)/" .env + +# 3. Create data directories +sudo mkdir -p /opt/project-nomad/{storage,redis,logs/nginx} + +# 4. Start the stack +docker compose up -d ``` -Project N.O.M.A.D. is now installed on your device! Open a browser and navigate to `http://localhost:8080` (or `http://DEVICE_IP:8080`) to start exploring! +Open `http://localhost:8080` to access the dashboard. -## How It Works -N.O.M.A.D. is a management UI ("Command Center") and API that orchestrates a collection of containerized tools and resources via [Docker](https://www.docker.com/). It handles installation, configuration, and updates for everything — so you don't have to. +## Container Architecture -**Built-in capabilities include:** -- **AI Chat with Knowledge Base** — local AI chat powered by [Ollama](https://ollama.com/), with document upload and semantic search (RAG via [Qdrant](https://qdrant.tech/)) -- **Information Library** — offline Wikipedia, medical references, ebooks, and more via [Kiwix](https://kiwix.org/) -- **Education Platform** — Khan Academy courses with progress tracking via [Kolibri](https://learningequality.org/kolibri/) -- **Offline Maps** — downloadable regional maps via [ProtoMaps](https://protomaps.com) -- **Data Tools** — encryption, encoding, and analysis via [CyberChef](https://gchq.github.io/CyberChef/) -- **Notes** — local note-taking via [FlatNotes](https://github.com/dullage/flatnotes) -- **System Benchmark** — hardware scoring with a [community leaderboard](https://benchmark.projectnomad.us) -- **Easy Setup Wizard** — guided first-time configuration with curated content collections +| Service | Image | Purpose | +|---------|-------|---------| +| **nomad-app** | project-nomad | Web UI + API server | +| **nomad-worker** | project-nomad | Background job processing | +| **nomad-database** | mysql:8.0 | Persistent data storage | +| **nomad-cache** | redis:7-alpine | Cache + job queues | +| **nomad-nginx** | nginx:alpine | Reverse proxy | -N.O.M.A.D. also includes built-in tools like a Wikipedia content selector, ZIM library manager, and content explorer. +All services communicate over a private Docker network (`nomad-internal`). + +## NAS Compatibility + +| Platform | Storage Path | Guide | +|----------|-------------|-------| +| **Unraid** | `/mnt/user/appdata/project-nomad` | [Unraid Guide](docs/homelab/unraid-guide.md) | +| **TrueNAS SCALE** | `/mnt/pool/apps/project-nomad` | [TrueNAS Guide](docs/homelab/truenas-guide.md) | +| **Linux** | `/opt/project-nomad` | [Installation Guide](docs/homelab/installation-guide.md) | + +### NAS Install Templates + +- **Unraid**: [Community Apps XML template](homelab/unraid-template.xml) +- **TrueNAS SCALE**: [Helm chart](homelab/truenas/) +- **Any Docker host**: [docker-compose.yml](docker-compose.yml) + +## Storage Layout + +``` +NOMAD_DATA_DIR/ +├── storage/ # Content files (ZIM, maps, uploads) — NAS share OK +├── redis/ # Redis persistence +└── logs/ + └── nginx/ # Nginx access/error logs +``` + +The MySQL database uses a **Docker named volume** for optimal I/O, avoiding NFS/SMB latency. + +## Reverse Proxy Support + +Works behind common homelab reverse proxies: + +| Proxy | Configuration | +|-------|--------------| +| **Nginx Proxy Manager** | [Setup guide](homelab/reverse-proxy-examples/nginx-proxy-manager.md) | +| **Traefik** | [Traefik config](homelab/reverse-proxy-examples/traefik.yml) | +| **Caddy** | [Caddyfile](homelab/reverse-proxy-examples/caddy.Caddyfile) | + +## Monitoring Agent + +Deploy lightweight monitoring agents on homelab nodes: + +```bash +docker run -d --name nomad-agent \ + -p 9100:9100 \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + -v /proc:/host/proc:ro \ + -e NODE_NAME=my-server \ + nomad-agent +``` + +Features: CPU/RAM/disk metrics, Docker container monitoring, Prometheus `/metrics` endpoint. + +See the [Agent Guide](docs/homelab/agent-guide.md) for details. ## What's Included @@ -56,113 +116,69 @@ N.O.M.A.D. also includes built-in tools like a Wikipedia content selector, ZIM l | System Benchmark | Built-in | Hardware scoring, Builder Tags, and community leaderboard | ## Device Requirements -While many similar offline survival computers are designed to be run on bare-minimum, lightweight hardware, Project N.O.M.A.D. is quite the opposite. To install and run the -available AI tools, we highly encourage the use of a beefy, GPU-backed device to make the most of your install. - -At it's core, however, N.O.M.A.D. is still very lightweight. For a barebones installation of the management application itself, the following minimal specs are required: - -*Note: Project N.O.M.A.D. is not sponsored by any hardware manufacturer and is designed to be as hardware-agnostic as possible. The harware listed below is for example/comparison use only* #### Minimum Specs -- Processor: 2 GHz dual-core processor or better -- RAM: 4GB system memory -- Storage: At least 5 GB free disk space -- OS: Debian-based (Ubuntu recommended) -- Stable internet connection (required during install only) - -To run LLM's and other included AI tools: +- 2 GHz dual-core processor +- 4 GB RAM (8 GB recommended) +- 5 GB free disk space +- Docker 20.10+ and Docker Compose v2+ +- Any Linux, Unraid, or TrueNAS SCALE host #### Optimal Specs -- Processor: AMD Ryzen 7 or Intel Core i7 or better -- RAM: 32 GB system memory -- Graphics: NVIDIA RTX 3060 or AMD equivalent or better (more VRAM = run larger models) -- Storage: At least 250 GB free disk space (preferably on SSD) -- OS: Debian-based (Ubuntu recommended) -- Stable internet connection (required during install only) +- AMD Ryzen 7 / Intel Core i7 or better +- 32 GB RAM +- NVIDIA RTX 3060+ (for AI features) +- 250 GB SSD +- Stable internet connection (for initial content downloads) -**For detailed build recommendations at three price points ($200–$800+), see the [Hardware Guide](https://www.projectnomad.us/hardware).** +## Documentation -Again, Project N.O.M.A.D. itself is quite lightweight - it's the tools and resources you choose to install with N.O.M.A.D. that will determine the specs required for your unique deployment +| Guide | Description | +|-------|-------------| +| [Installation Guide](docs/homelab/installation-guide.md) | Docker Compose setup | +| [Unraid Guide](docs/homelab/unraid-guide.md) | Unraid-specific installation | +| [TrueNAS Guide](docs/homelab/truenas-guide.md) | TrueNAS SCALE installation | +| [Agent Guide](docs/homelab/agent-guide.md) | Monitoring agent setup | +| [Architecture](docs/homelab/architecture.md) | System design and data flow | +| [Monitoring](docs/homelab/monitoring.md) | Monitoring stack and Prometheus integration | + +## Configuration + +All configuration is handled through environment variables in `.env`. See [`.env.example`](.env.example) for all options. + +Key settings: + +| Variable | Default | Description | +|----------|---------|-------------| +| `APP_KEY` | — | Encryption key (**required**) | +| `DB_PASSWORD` | — | Database password (**required**) | +| `URL` | http://localhost:8080 | External access URL | +| `NOMAD_DATA_DIR` | /opt/project-nomad | Host storage directory | +| `PORT` | 8080 | Application port | +| `LOG_LEVEL` | info | Log verbosity | + +## Updating + +```bash +docker compose pull +docker compose up -d +``` ## About Internet Usage & Privacy Project N.O.M.A.D. is designed for offline usage. An internet connection is only required during the initial installation (to download dependencies) and if you (the user) decide to download additional tools and resources at a later time. Otherwise, N.O.M.A.D. does not require an internet connection and has ZERO built-in telemetry. -To test internet connectivity, N.O.M.A.D. attempts to make a request to Cloudflare's utility endpoint, `https://1.1.1.1/cdn-cgi/trace` and checks for a successful response. - -## About Security -By design, Project N.O.M.A.D. is intended to be open and available without hurdles - it includes no authentication. If you decide to connect your device to a local network after install (e.g. for allowing other devices to access it's resources), you can block/open ports to control which services are exposed. - -**Will authentication be added in the future?** Maybe. It's not currently a priority, but if there's enough demand for it, we may consider building in an optional authentication layer in a future release to support uses cases where multiple users need access to the same instance but with different permission levels (e.g. family use with parental controls, classroom use with teacher/admin accounts, etc.). For now, we recommend using network-level controls to manage access if you're planning to expose your N.O.M.A.D. instance to other devices on a local network. N.O.M.A.D. is not designed to be exposed directly to the internet, and we strongly advise against doing so unless you really know what you're doing, have taken appropriate security measures, and understand the risks involved. - ## Contributing -Contributions are welcome and appreciated! Please read this section fully to understand how to contribute to the project. +Contributions are welcome and appreciated! Please read the [Contributing Guide](CONTRIBUTING.md) for details. -### General Guidelines - -- **Open an issue first**: Before starting work on a new feature or bug fix, please open an issue to discuss your proposed changes. This helps ensure that your contribution aligns with the project's goals and avoids duplicate work. Title the issue clearly and provide a detailed description of the problem or feature you want to work on. -- **Fork the repository**: Click the "Fork" button at the top right of the repository page to create a copy of the project under your GitHub account. -- **Create a new branch**: In your forked repository, create a new branch for your work. Use a descriptive name for the branch that reflects the purpose of your changes (e.g., `fix/issue-123` or `feature/add-new-tool`). -- **Make your changes**: Implement your changes in the new branch. Follow the existing code style and conventions used in the project. Be sure to test your changes locally to ensure they work as expected. -- **Add Release Notes**: If your changes include new features, bug fixes, or improvements, please see the "Release Notes" section below to properly document your contribution for the next release. -- **Conventional Commits**: When committing your changes, please use conventional commit messages to provide clear and consistent commit history. The format is `(): `, where: - - `type` is the type of change (e.g., `feat` for new features, `fix` for bug fixes, `docs` for documentation changes, etc.) - - `scope` is an optional area of the codebase that your change affects (e.g., `api`, `ui`, `docs`, etc.) - - `description` is a brief summary of the change -- **Submit a pull request**: Once your changes are ready, submit a pull request to the main repository. Provide a clear description of your changes and reference any related issues. The project maintainers will review your pull request and may provide feedback or request changes before it can be merged. -- **Be responsive to feedback**: If the maintainers request changes or provide feedback on your pull request, please respond in a timely manner. Stale pull requests may be closed if there is no activity for an extended period. -- **Follow the project's code of conduct**: Please adhere to the project's code of conduct when interacting with maintainers and other contributors. Be respectful and considerate in your communications. -- **No guarantee of acceptance**: The project is community-driven, and all contributions are appreciated, but acceptance is not guaranteed. The maintainers will evaluate each contribution based on its quality, relevance, and alignment with the project's goals. -- **Thank you for contributing to Project N.O.M.A.D.!** Your efforts help make this project better for everyone. - -### Versioning -This project uses semantic versioning. The version is managed in the root `package.json` -and automatically updated by semantic-release. For simplicity's sake, the "project-nomad" image -uses the same version defined there instead of the version in `admin/package.json` (stays at 0.0.0), as it's the only published image derived from the code. - -### Release Notes -Human-readable release notes live in [`admin/docs/release-notes.md`](admin/docs/release-notes.md) and are displayed in the Command Center's built-in documentation. - -When working on changes, add a summary to the `## Unreleased` section at the top of that file under the appropriate heading: - -- **Features** — new user-facing capabilities -- **Bug Fixes** — corrections to existing behavior -- **Improvements** — enhancements, refactors, docs, or dependency updates - -Use the format `- **Area**: Description` to stay consistent with existing entries. When a release is triggered, CI automatically stamps the version and date, commits the update, and pushes the content to the GitHub release. +This project uses semantic versioning. The version is managed in the root `package.json` +and automatically updated by semantic-release. ## Community & Resources -- **Website:** [www.projectnomad.us](https://www.projectnomad.us) - Learn more about the project -- **Discord:** [Join the Community](https://discord.com/invite/crosstalksolutions) - Get help, share your builds, and connect with other NOMAD users -- **Benchmark Leaderboard:** [benchmark.projectnomad.us](https://benchmark.projectnomad.us) - See how your hardware stacks up against other NOMAD builds +- **Website:** [www.projectnomad.us](https://www.projectnomad.us) +- **Discord:** [Join the Community](https://discord.com/invite/crosstalksolutions) +- **Benchmark Leaderboard:** [benchmark.projectnomad.us](https://benchmark.projectnomad.us) ## License -Project N.O.M.A.D. is licensed under the [Apache License 2.0](LICENSE). - -## Helper Scripts -Once installed, Project N.O.M.A.D. has a few helper scripts should you ever need to troubleshoot issues or perform maintenance that can't be done through the Command Center. All of these scripts are found in Project N.O.M.A.D.'s install directory, `/opt/project-nomad` - -### - -###### Start Script - Starts all installed project containers -```bash -sudo bash /opt/project-nomad/start_nomad.sh -``` -### - -###### Stop Script - Stops all installed project containers -```bash -sudo bash /opt/project-nomad/stop_nomad.sh -``` -### - -###### Update Script - Attempts to pull the latest images for the Command Center and its dependencies (i.e. mysql) and recreate the containers. Note: this *only* updates the Command Center containers. It does not update the installable application containers - that should be done through the Command Center UI -```bash -sudo bash /opt/project-nomad/update_nomad.sh -``` - -###### Uninstall Script - Need to start fresh? Use the uninstall script to make your life easy. Note: this cannot be undone! -```bash -curl -fsSL https://raw.githubusercontent.com/Crosstalk-Solutions/project-nomad/refs/heads/main/install/uninstall_nomad.sh -o uninstall_nomad.sh && sudo bash uninstall_nomad.sh -``` \ No newline at end of file +Project N.O.M.A.D. is licensed under the [Apache License 2.0](LICENSE). \ No newline at end of file diff --git a/docs/homelab/agent-guide.md b/docs/homelab/agent-guide.md new file mode 100644 index 0000000..3a600cc --- /dev/null +++ b/docs/homelab/agent-guide.md @@ -0,0 +1,146 @@ +# Project N.O.M.A.D. — Agent Installation Guide + +## Overview + +The Nomad monitoring agent is a lightweight container that collects system metrics from remote homelab nodes and reports them to the Nomad server. It exposes a Prometheus-compatible `/metrics` endpoint. + +## Features + +- **System metrics**: CPU usage, memory, disk, network +- **Docker monitoring**: Container status, running/stopped counts +- **Prometheus endpoint**: Native `/metrics` output for scraping +- **Low resource usage**: ~20MB RAM, minimal CPU +- **Auto-reporting**: Sends metrics to Nomad server on configurable interval + +## Quick Start + +### Docker (Recommended) + +```bash +docker run -d \ + --name nomad-agent \ + --restart unless-stopped \ + -p 9100:9100 \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + -v /proc:/host/proc:ro \ + -v /sys:/host/sys:ro \ + -e NOMAD_SERVER_URL=http://your-nomad-server:8080 \ + -e AGENT_SECRET=your-shared-secret \ + -e NODE_NAME=my-server \ + ghcr.io/docwatz/nomad-agent:latest +``` + +### Docker Compose + +Add to your existing `docker-compose.yml` or create a new one: + +```yaml +services: + nomad-agent: + build: ./agent + container_name: nomad-agent + restart: unless-stopped + ports: + - "9100:9100" + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro + - /proc:/host/proc:ro + - /sys:/host/sys:ro + environment: + - NOMAD_SERVER_URL=http://your-nomad-server:8080 + - AGENT_SECRET=your-shared-secret + - NODE_NAME=my-server + - COLLECT_INTERVAL=30 +``` + +### Build from Source + +```bash +cd agent/ +docker build -t nomad-agent . +``` + +## Configuration + +| Variable | Default | Description | +|----------|---------|-------------| +| `AGENT_PORT` | 9100 | Metrics server port | +| `COLLECT_INTERVAL` | 30 | Collection interval in seconds | +| `NOMAD_SERVER_URL` | — | Nomad server URL for reporting | +| `AGENT_SECRET` | — | Shared secret for authentication | +| `NODE_NAME` | hostname | Display name for this node | +| `HOST_PROC` | /host/proc | Path to host /proc mount | +| `HOST_SYS` | /host/sys | Path to host /sys mount | + +## API Endpoints + +| Endpoint | Format | Description | +|----------|--------|-------------| +| `GET /health` | JSON | Health check | +| `GET /metrics` | Prometheus | Prometheus exposition format | +| `GET /api/metrics` | JSON | Full metrics as JSON | + +## Prometheus Integration + +Add the agent as a scrape target in your `prometheus.yml`: + +```yaml +scrape_configs: + - job_name: 'nomad-agent' + static_configs: + - targets: + - 'server1:9100' + - 'server2:9100' + - 'nas:9100' + scrape_interval: 30s +``` + +### Available Metrics + +| Metric | Type | Description | +|--------|------|-------------| +| `nomad_agent_cpu_usage_percent` | gauge | CPU usage percentage | +| `nomad_agent_cpu_count` | gauge | Number of CPU cores | +| `nomad_agent_memory_total_bytes` | gauge | Total memory | +| `nomad_agent_memory_used_bytes` | gauge | Used memory | +| `nomad_agent_memory_usage_percent` | gauge | Memory usage percentage | +| `nomad_agent_uptime_seconds` | gauge | System uptime | +| `nomad_agent_docker_containers` | gauge | Total Docker containers | +| `nomad_agent_docker_container_running` | gauge | Per-container running status | + +## Security + +- The agent runs as a non-root user inside the container +- Docker socket is mounted read-only +- Communication with the Nomad server uses a shared secret via `Authorization: Bearer` header +- For production use, place behind a TLS-terminating reverse proxy + +## Resource Usage + +| Metric | Value | +|--------|-------| +| RAM | ~15-25 MB | +| CPU | < 1% (idle) | +| Image size | ~60 MB | +| Network | ~1 KB per report | + +## Deploying on Multiple Nodes + +Deploy an agent on each homelab node: + +``` +┌─────────────┐ ┌─────────────┐ ┌─────────────┐ +│ Server 1 │ │ Server 2 │ │ NAS │ +│ nomad-agent │ │ nomad-agent │ │ nomad-agent │ +│ :9100 │ │ :9100 │ │ :9100 │ +└──────┬──────┘ └──────┬──────┘ └──────┬──────┘ + │ │ │ + └───────────────────┼───────────────────┘ + │ + ┌──────▼──────┐ + │ Nomad Server│ + │ :8080 │ + └─────────────┘ +``` + +Each agent auto-registers with the Nomad server using `NODE_NAME` and begins sending telemetry at the configured interval. diff --git a/docs/homelab/architecture.md b/docs/homelab/architecture.md new file mode 100644 index 0000000..2122697 --- /dev/null +++ b/docs/homelab/architecture.md @@ -0,0 +1,218 @@ +# Project N.O.M.A.D. — Homelab Edition: Architecture + +## System Overview + +Project N.O.M.A.D. Homelab Edition is a container-native knowledge platform designed for NAS and homelab environments. The architecture prioritizes reliability, minimal resource usage, and compatibility with storage-backed systems. + +## Container Stack + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Docker Host / NAS │ +│ │ +│ ┌────────────────────────────────────────────────────────────┐ │ +│ │ nomad-internal network │ │ +│ │ │ │ +│ │ ┌──────────────┐ ┌──────────────┐ ┌─────────────┐ │ │ +│ │ │ nomad-nginx │ │ nomad-app │ │nomad-worker │ │ │ +│ │ │ (Nginx) ├───►│ (AdonisJS) │ │ (Queue Jobs)│ │ │ +│ │ │ :80/:443 │ │ :8080 │ │ │ │ │ +│ │ └──────────────┘ └──────┬───────┘ └──────┬──────┘ │ │ +│ │ │ │ │ │ +│ │ ┌──────▼───────┐ ┌──────▼──────┐ │ │ +│ │ │nomad-database│ │ nomad-cache │ │ │ +│ │ │ (MySQL 8.0) │ │ (Redis 7) │ │ │ +│ │ │ :3306 │ │ :6379 │ │ │ +│ │ └──────────────┘ └─────────────┘ │ │ +│ └────────────────────────────────────────────────────────────┘ │ +│ │ +│ Volumes: │ +│ ├── nomad-db-data (Docker volume - local SSD) │ +│ ├── NOMAD_DATA_DIR/storage (bind mount - NAS share) │ +│ ├── NOMAD_DATA_DIR/redis (bind mount) │ +│ └── NOMAD_DATA_DIR/logs/nginx (bind mount) │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Service Roles + +### nomad-app (Application Server) + +- **Technology:** Node.js 22 + AdonisJS 6 +- **Role:** Serves the web UI (React/Inertia), handles API requests, manages content +- **Port:** 8080 +- **Dependencies:** nomad-database, nomad-cache + +### nomad-worker (Background Worker) + +- **Technology:** Same image as nomad-app +- **Role:** Processes background jobs — content downloads, AI model downloads, embeddings, benchmarks +- **Queues:** downloads, model-downloads, benchmarks, embeddings +- **Dependencies:** nomad-database, nomad-cache + +### nomad-database (Database) + +- **Technology:** MySQL 8.0 +- **Role:** Persistent storage for services, content metadata, chat sessions, benchmarks, settings +- **Storage:** Docker named volume (optimized for I/O) + +### nomad-cache (Cache / Queue Broker) + +- **Technology:** Redis 7 Alpine +- **Role:** BullMQ job queues, session cache, real-time event transport +- **Config:** AOF persistence, 256MB max memory with LRU eviction + +### nomad-nginx (Reverse Proxy) + +- **Technology:** Nginx Alpine +- **Role:** TLS termination, request routing, static asset caching, WebSocket proxy +- **Ports:** 80 (HTTP), 443 (HTTPS) + +## Data Flow + +### Content Download Pipeline + +``` +User Request + │ + ▼ +nomad-app (API) ──► nomad-cache (Redis Queue) + │ + ▼ + nomad-worker + │ + Downloads content + │ + ▼ + /app/storage/ + (NAS bind mount) +``` + +### AI Chat Pipeline + +``` +User Message + │ + ▼ +nomad-app ──► Ollama (AI Model) + │ │ + │ ▼ + │ Response + RAG context + │ │ + ▼ ▼ +nomad-database (Chat History) +``` + +### Real-Time Updates + +``` +nomad-worker ──► nomad-cache (Redis Pub/Sub) + │ + ▼ + nomad-app (Transmit SSE) + │ + ▼ + Browser (EventSource) +``` + +## Storage Architecture + +### Design Principles + +1. **Database on local volume** — Docker named volume for high-IOPS MySQL operations +2. **Content on NAS share** — Bind mount to NAS storage for large files (ZIM, maps, PDFs) +3. **Logs rotated automatically** — Nginx logs on bind mount, easily accessible + +### Storage Tiers + +| Tier | Type | Use Case | I/O Profile | +|------|------|----------|-------------| +| **Hot** | Docker named volume | MySQL database | High IOPS, small writes | +| **Warm** | Bind mount (SSD/cache) | Redis, temp files | Medium IOPS | +| **Cold** | Bind mount (NAS array) | Content library, backups | Sequential reads, large files | + +### NAS Compatibility + +| Platform | Storage Path | Notes | +|----------|-------------|-------| +| **Unraid** | `/mnt/user/appdata/project-nomad` | Uses cache drive for DB | +| **TrueNAS SCALE** | `/mnt/pool/apps/project-nomad` | ZFS dataset recommended | +| **Linux** | `/opt/project-nomad` | Standard filesystem | +| **Synology** | `/volume1/docker/project-nomad` | Btrfs volume | + +## Network Architecture + +### Internal Network + +All services communicate over the `nomad-internal` bridge network. Only the following ports are exposed to the host: + +| Port | Service | Purpose | +|------|---------|---------| +| 80 | nomad-nginx | HTTP (configurable) | +| 443 | nomad-nginx | HTTPS (configurable) | +| 8080 | nomad-app | Direct app access (optional) | + +### Reverse Proxy Compatibility + +The stack works behind external reverse proxies: + +``` +Internet/LAN + │ + ▼ +┌───────────────────┐ +│ External Proxy │ Nginx Proxy Manager / Traefik / Caddy +│ (TLS termination) │ +└─────────┬─────────┘ + │ + ▼ +┌─────────────────────┐ +│ nomad-nginx (:80) │ OR directly to nomad-app (:8080) +└─────────┬───────────┘ + │ + ▼ +┌─────────────────────┐ +│ nomad-app (:8080) │ +└─────────────────────┘ +``` + +When using an external reverse proxy, you can disable `nomad-nginx`: + +```yaml +# In docker-compose.yml override +services: + nomad-nginx: + profiles: ["proxy"] # Only starts with --profile proxy +``` + +## Resource Requirements + +### Minimum (8 GB RAM system) + +| Service | RAM | CPU | +|---------|-----|-----| +| nomad-app | 256 MB | 0.25 cores | +| nomad-worker | 256 MB | 0.1 cores | +| nomad-database | 256 MB | 0.25 cores | +| nomad-cache | 64 MB | 0.05 cores | +| nomad-nginx | 16 MB | 0.01 cores | +| **Total** | **~850 MB** | **~0.66 cores** | + +### Recommended (32 GB RAM system) + +| Service | RAM | CPU | +|---------|-----|-----| +| nomad-app | 1 GB | 1 core | +| nomad-worker | 2 GB | 1 core | +| nomad-database | 1 GB | 0.5 cores | +| nomad-cache | 256 MB | 0.1 cores | +| nomad-nginx | 32 MB | 0.05 cores | +| **Total** | **~3.3 GB** | **~2.65 cores** | + +## Security Model + +- No authentication by default (network-level access control recommended) +- All inter-service communication on private Docker network +- Database and Redis not exposed to host by default +- Docker socket mounted read-only where possible +- Agent communication via shared secret (Bearer token) diff --git a/docs/homelab/docker-compose-guide.md b/docs/homelab/docker-compose-guide.md new file mode 100644 index 0000000..c5e3d12 --- /dev/null +++ b/docs/homelab/docker-compose-guide.md @@ -0,0 +1,251 @@ +# Project N.O.M.A.D. — Docker Compose Deployment Guide + +## Overview + +This guide covers deploying Project N.O.M.A.D. Homelab Edition using Docker Compose on any Linux host, VM, or NAS system. + +## Prerequisites + +- Docker 20.10+ +- Docker Compose v2+ (comes with Docker Desktop or `docker compose` plugin) +- 4 GB RAM minimum +- 5 GB free disk space + +### Verify Docker Installation + +```bash +docker --version # Docker 20.10+ +docker compose version # Docker Compose v2+ +``` + +## Deployment + +### Step 1: Get the Files + +```bash +git clone https://github.com/DocwatZ/project-nomad-homelab-edition.git +cd project-nomad-homelab-edition +``` + +### Step 2: Configure Environment + +```bash +cp .env.example .env +``` + +Generate secure credentials: + +```bash +# Generate application key +APP_KEY=$(openssl rand -hex 32) +sed -i "s/^APP_KEY=replaceme/APP_KEY=$APP_KEY/" .env + +# Generate database password +DB_PASS=$(openssl rand -base64 24) +sed -i "s/^DB_PASSWORD=replaceme/DB_PASSWORD=$DB_PASS/" .env +sed -i "s/^MYSQL_ROOT_PASSWORD=replaceme/MYSQL_ROOT_PASSWORD=$DB_PASS/" .env +``` + +Configure the external URL: + +```bash +# Replace with your server IP or domain +sed -i "s|^URL=.*|URL=http://$(hostname -I | awk '{print $1}'):8080|" .env +``` + +### Step 3: Create Data Directories + +```bash +NOMAD_DIR=$(grep NOMAD_DATA_DIR .env | cut -d= -f2) +sudo mkdir -p ${NOMAD_DIR}/{storage,redis,logs/nginx} +sudo chown -R $(id -u):$(id -g) ${NOMAD_DIR} +``` + +### Step 4: Launch + +```bash +docker compose up -d +``` + +First launch takes 1-3 minutes for database initialization and migrations. + +### Step 5: Verify + +```bash +# Check all services are running +docker compose ps + +# Check application health +curl -s http://localhost:8080/api/health + +# View logs +docker compose logs -f nomad-app +``` + +## Service Management + +### Start / Stop / Restart + +```bash +# Stop all services +docker compose down + +# Start all services +docker compose up -d + +# Restart a specific service +docker compose restart nomad-app + +# View logs +docker compose logs -f +docker compose logs -f nomad-app +``` + +### Update to Latest Version + +```bash +docker compose pull +docker compose up -d +``` + +### Full Reset (Destroys Data) + +```bash +docker compose down -v +sudo rm -rf /opt/project-nomad/* +docker compose up -d +``` + +## Customization + +### Disable Nginx Proxy + +If you already have a reverse proxy (Traefik, Nginx Proxy Manager, Caddy), you can skip the built-in Nginx: + +```bash +# Start without nginx +docker compose up -d nomad-app nomad-worker nomad-database nomad-cache +``` + +Access the app directly on port 8080. + +### Disable Worker (Lightweight Mode) + +For minimal resource usage, you can run without the dedicated worker. The app will process jobs inline: + +```bash +docker compose up -d nomad-app nomad-database nomad-cache +``` + +> Note: Background downloads and AI features may be slower without the worker. + +### Custom Port + +Edit `.env`: + +``` +PORT=9090 +``` + +### Custom Storage Location + +Edit `.env`: + +``` +NOMAD_DATA_DIR=/mnt/my-nas-share/project-nomad +``` + +## Compose File Structure + +The `docker-compose.yml` defines five services: + +``` +docker-compose.yml +├── nomad-app # Web application (port 8080) +├── nomad-worker # Background job processor +├── nomad-database # MySQL 8.0 database +├── nomad-cache # Redis 7 cache/queue +└── nomad-nginx # Nginx reverse proxy (port 80/443) +``` + +### Override File + +Create a `docker-compose.override.yml` for local customizations: + +```yaml +services: + nomad-app: + # Add extra environment variables + environment: + - NOMAD_API_URL=https://api.projectnomad.io + # Add extra volumes + volumes: + - /mnt/nas-share/content:/app/storage/content:ro +``` + +## Monitoring + +### Health Checks + +All services include health checks. View status: + +```bash +docker compose ps +``` + +### Resource Usage + +```bash +docker stats --no-stream +``` + +### Prometheus Metrics + +Deploy the monitoring agent for Prometheus-compatible metrics: + +```bash +docker compose -f docker-compose.yml -f docker-compose.monitoring.yml up -d +``` + +See [Monitoring Architecture](./monitoring.md) for details. + +## Troubleshooting + +### Service Won't Start + +```bash +# Check logs for the failing service +docker compose logs nomad-app +docker compose logs nomad-database + +# Check if ports are in use +ss -tlnp | grep -E '(8080|3306|6379|80)' +``` + +### Database Connection Error + +The app waits for the database health check. If it times out: + +```bash +# Check database status +docker compose exec nomad-database mysqladmin ping -h localhost + +# Check database logs +docker compose logs nomad-database +``` + +### Permission Denied on Volumes + +```bash +sudo chown -R 1000:1000 /opt/project-nomad/storage +``` + +### Out of Disk Space + +```bash +# Check Docker disk usage +docker system df + +# Clean up unused images +docker system prune -a +``` diff --git a/docs/homelab/installation-guide.md b/docs/homelab/installation-guide.md new file mode 100644 index 0000000..7899cde --- /dev/null +++ b/docs/homelab/installation-guide.md @@ -0,0 +1,163 @@ +# Project N.O.M.A.D. — Homelab Edition: Installation Guide + +## Overview + +Project N.O.M.A.D. Homelab Edition runs as a Docker Compose stack, making it compatible with any system that supports Docker — including NAS platforms like Unraid and TrueNAS SCALE. + +## Prerequisites + +- **Docker** 20.10+ and **Docker Compose** v2+ +- **4 GB RAM** minimum (8 GB+ recommended) +- **5 GB** free disk space (more for content downloads) +- Network access to pull Docker images + +## Quick Start + +### 1. Clone or Download + +```bash +git clone https://github.com/DocwatZ/project-nomad-homelab-edition.git +cd project-nomad-homelab-edition +``` + +Or download and extract the ZIP from the GitHub releases page. + +### 2. Configure Environment + +```bash +# Copy the example environment file +cp .env.example .env + +# Generate a secure application key +APP_KEY=$(openssl rand -hex 32) + +# Generate a secure database password +DB_PASS=$(openssl rand -base64 24) + +# Update .env with generated values +sed -i "s/^APP_KEY=replaceme/APP_KEY=$APP_KEY/" .env +sed -i "s/^DB_PASSWORD=replaceme/DB_PASSWORD=$DB_PASS/" .env +sed -i "s/^MYSQL_ROOT_PASSWORD=replaceme/MYSQL_ROOT_PASSWORD=$DB_PASS/" .env +``` + +Edit `.env` to set your external URL: + +```bash +# Set to your server's IP or domain +URL=http://192.168.1.100:8080 +``` + +### 3. Create Data Directories + +```bash +# Default location (or set NOMAD_DATA_DIR in .env) +sudo mkdir -p /opt/project-nomad/{storage,redis,logs/nginx} +sudo chown -R 1000:1000 /opt/project-nomad +``` + +### 4. Start the Stack + +```bash +docker compose up -d +``` + +### 5. Access the Dashboard + +Open your browser and navigate to the URL you configured (default: `http://localhost:8080`). + +If using the Nginx proxy: `http://localhost` (port 80). + +## Service Architecture + +| Service | Container | Port | Purpose | +|---------|-----------|------|---------| +| **nomad-app** | Nomad application | 8080 | Web UI + API | +| **nomad-worker** | Background jobs | — | Queue processing | +| **nomad-database** | MySQL 8.0 | 3306 (internal) | Persistent data | +| **nomad-cache** | Redis 7 | 6379 (internal) | Cache + job queues | +| **nomad-nginx** | Nginx | 80, 443 | Reverse proxy | + +## Volume Layout + +``` +NOMAD_DATA_DIR/ +├── storage/ # Content files (ZIM, maps, uploads) +├── redis/ # Redis persistence +└── logs/ + └── nginx/ # Nginx access/error logs +``` + +The MySQL database uses a Docker named volume (`nomad-db-data`) for optimal I/O performance. This avoids latency issues common with NFS/SMB-backed storage on NAS systems. + +## Updating + +```bash +# Pull latest images +docker compose pull + +# Recreate containers with new images +docker compose up -d +``` + +## Stopping + +```bash +docker compose down +``` + +To also remove data volumes: + +```bash +docker compose down -v +``` + +## Configuration Reference + +See `.env.example` for all available configuration options. Key settings: + +| Variable | Default | Description | +|----------|---------|-------------| +| `PORT` | 8080 | Application port | +| `APP_KEY` | — | Encryption key (required) | +| `URL` | http://localhost:8080 | External access URL | +| `DB_PASSWORD` | — | Database password (required) | +| `NOMAD_DATA_DIR` | /opt/project-nomad | Host data directory | +| `LOG_LEVEL` | info | Logging verbosity | +| `NGINX_HTTP_PORT` | 80 | Nginx HTTP port | +| `NGINX_HTTPS_PORT` | 443 | Nginx HTTPS port | + +## Troubleshooting + +### Container won't start + +```bash +# Check container logs +docker compose logs nomad-app + +# Verify database is healthy +docker compose ps nomad-database +``` + +### Database connection errors + +Ensure the database is healthy before the app starts. The compose file handles this with health checks, but on slow systems you may need to wait longer: + +```bash +# Check database health +docker compose exec nomad-database mysqladmin ping -h localhost +``` + +### Permission issues on NAS + +Ensure the storage directories are writable by the container user (UID 1000): + +```bash +sudo chown -R 1000:1000 /path/to/your/storage +``` + +## Next Steps + +- [Unraid Installation Guide](./unraid-guide.md) +- [TrueNAS SCALE Installation Guide](./truenas-guide.md) +- [Agent Installation Guide](./agent-guide.md) +- [Architecture Overview](./architecture.md) diff --git a/docs/homelab/monitoring.md b/docs/homelab/monitoring.md new file mode 100644 index 0000000..6a3e89a --- /dev/null +++ b/docs/homelab/monitoring.md @@ -0,0 +1,225 @@ +# Project N.O.M.A.D. — Monitoring Architecture + +## Overview + +The Nomad Homelab Edition includes built-in monitoring capabilities and integrates with standard homelab monitoring stacks. + +## Monitoring Layers + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Nomad Dashboard │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ NAS │ │ Server │ │Container │ │ Network │ │ +│ │ Health │ │ Metrics │ │ Status │ │ Devices │ │ +│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ +│ │ │ │ │ │ +│ ┌────▼────────────▼────────────▼────────────▼─────┐ │ +│ │ Nomad App (Aggregator) │ │ +│ └──────────────────────┬──────────────────────────┘ │ +└─────────────────────────┼──────────────────────────────────┘ + │ + ┌───────────────┼───────────────┐ + │ │ │ + ┌─────▼─────┐ ┌────▼────┐ ┌──────▼──────┐ + │ Agent 1 │ │ Agent 2 │ │ Agent N │ + │ (Server) │ │ (NAS) │ │ (Remote) │ + │ :9100 │ │ :9100 │ │ :9100 │ + └───────────┘ └─────────┘ └─────────────┘ +``` + +## Built-in Monitoring + +### System Resource Monitoring + +The Nomad application provides built-in system information through the system controller: + +- **CPU**: Model, core count, usage +- **Memory**: Total, used, free, usage percentage +- **Disk**: Mount points, usage, filesystem types +- **Docker**: Container status, image versions, resource usage + +### Docker Container Monitoring + +Nomad monitors its own container stack and any Docker containers on the host via the Docker socket: + +- Container health status +- Image versions and update availability +- Resource consumption +- Log access (via Dozzle integration in original install) + +### Health Endpoints + +| Endpoint | Service | Purpose | +|----------|---------|---------| +| `GET /api/health` | nomad-app | Application health | +| `GET /nginx-health` | nomad-nginx | Reverse proxy health | +| `GET /health` | nomad-agent | Agent health | + +## Agent-Based Monitoring + +### Architecture + +The Nomad monitoring agent runs on remote homelab nodes and reports metrics via: + +1. **Push model**: Agent sends JSON metrics to `POST /api/agent/report` on the Nomad server +2. **Pull model**: Prometheus scrapes the agent's `/metrics` endpoint + +### Agent Metrics + +``` +nomad_agent_cpu_usage_percent - CPU utilization +nomad_agent_cpu_count - CPU core count +nomad_agent_memory_total_bytes - Total RAM +nomad_agent_memory_used_bytes - Used RAM +nomad_agent_memory_usage_percent - RAM utilization +nomad_agent_uptime_seconds - System uptime +nomad_agent_docker_containers - Docker container count +nomad_agent_docker_container_running - Per-container status +``` + +### Deployment + +Deploy an agent on each node you want to monitor: + +```bash +docker run -d --name nomad-agent \ + --restart unless-stopped \ + -p 9100:9100 \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + -v /proc:/host/proc:ro \ + -v /sys:/host/sys:ro \ + -e NOMAD_SERVER_URL=http://nomad-server:8080 \ + -e NODE_NAME=$(hostname) \ + nomad-agent +``` + +## Prometheus Integration + +### Full Monitoring Stack + +For a complete monitoring setup, add Prometheus and Grafana to your docker-compose: + +```yaml +# docker-compose.monitoring.yml +services: + prometheus: + image: prom/prometheus:latest + container_name: nomad-prometheus + restart: unless-stopped + ports: + - "9090:9090" + volumes: + - ./monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus-data:/prometheus + networks: + - nomad-internal + + grafana: + image: grafana/grafana:latest + container_name: nomad-grafana + restart: unless-stopped + ports: + - "3000:3000" + volumes: + - grafana-data:/var/lib/grafana + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + networks: + - nomad-internal + +volumes: + prometheus-data: + grafana-data: +``` + +### Prometheus Configuration + +```yaml +# monitoring/prometheus.yml +global: + scrape_interval: 30s + evaluation_interval: 30s + +scrape_configs: + - job_name: 'nomad-agents' + static_configs: + - targets: + - 'nomad-agent:9100' # Local agent + - 'server2:9100' # Remote server + - 'nas:9100' # NAS agent +``` + +### Node Exporter (Optional) + +For deeper host-level metrics, add the Prometheus Node Exporter alongside the Nomad agent: + +```yaml +node-exporter: + image: prom/node-exporter:latest + container_name: nomad-node-exporter + restart: unless-stopped + ports: + - "9101:9100" + volumes: + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/rootfs:ro + command: + - '--path.procfs=/host/proc' + - '--path.sysfs=/host/sys' + - '--path.rootfs=/rootfs' + - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)' +``` + +## Observability + +### Structured Logging + +The Nomad application uses structured JSON logging: + +```json +{ + "level": "info", + "timestamp": "2026-03-13T12:00:00.000Z", + "msg": "HTTP request completed", + "method": "GET", + "url": "/api/health", + "status": 200, + "duration": "12ms" +} +``` + +Configure log level via `LOG_LEVEL` environment variable: `debug`, `info`, `warn`, `error`. + +### Log Access + +```bash +# Application logs +docker compose logs -f nomad-app + +# Worker logs +docker compose logs -f nomad-worker + +# All service logs +docker compose logs -f + +# Nginx access logs (on host) +tail -f ${NOMAD_DATA_DIR}/logs/nginx/access.log +``` + +### Alerting Recommendations + +For homelab alerting, integrate with: + +| Tool | Use Case | +|------|----------| +| **Uptime Kuma** | Service uptime monitoring | +| **Grafana Alerting** | Metric-based alerts | +| **Ntfy** | Push notifications | +| **Gotify** | Self-hosted notifications | + +Example Uptime Kuma monitor: +- **URL**: `http://nomad-app:8080/api/health` +- **Interval**: 60 seconds +- **Expected status**: 200 diff --git a/docs/homelab/truenas-guide.md b/docs/homelab/truenas-guide.md new file mode 100644 index 0000000..66bf503 --- /dev/null +++ b/docs/homelab/truenas-guide.md @@ -0,0 +1,185 @@ +# Project N.O.M.A.D. — TrueNAS SCALE Installation Guide + +## Overview + +TrueNAS SCALE supports both Docker Compose (via custom app) and Helm charts for application deployment. This guide covers both methods. + +## Method 1: Docker Compose via Custom App (Recommended) + +TrueNAS SCALE Electric Eel (24.10+) supports Docker Compose as a custom app deployment method. + +### Prerequisites + +- TrueNAS SCALE 24.10+ (Electric Eel or newer) +- A dataset for application data + +### Steps + +#### 1. Create a Dataset + +1. Go to **Storage** → **Pools** +2. Create a new dataset: `project-nomad` + - Path: `/mnt/pool/apps/project-nomad` + - Record Size: 128K + - Compression: LZ4 + +#### 2. Create Subdirectories + +Open a TrueNAS shell: + +```bash +mkdir -p /mnt/pool/apps/project-nomad/{storage,redis,logs/nginx,config} +``` + +#### 3. Deploy as Custom App + +1. Go to **Apps** → **Discover Apps** → **Custom App** +2. Upload or paste the `docker-compose.yml` from this repository +3. Configure environment variables: + - `APP_KEY`: Generate with `openssl rand -hex 32` + - `DB_PASSWORD`: Set a secure password + - `MYSQL_ROOT_PASSWORD`: Same as DB_PASSWORD + - `URL`: `http://YOUR_TRUENAS_IP:8080` + - `NOMAD_DATA_DIR`: `/mnt/pool/apps/project-nomad` + +#### 4. Start the Application + +Click **Deploy** and wait for all services to start (first launch may take 2-3 minutes). + +## Method 2: Helm Chart + +### Prerequisites + +- TrueNAS SCALE with Kubernetes enabled +- Helm CLI (if deploying manually) + +### Steps + +#### 1. Add the Chart + +```bash +# Clone the repository +git clone https://github.com/DocwatZ/project-nomad-homelab-edition.git +cd project-nomad-homelab-edition/homelab/truenas + +# Install with Helm +helm install project-nomad . \ + --set app.appKey=$(openssl rand -hex 32) \ + --set database.password=$(openssl rand -base64 24) \ + --set database.rootPassword=$(openssl rand -base64 24) \ + --set app.url=http://YOUR_TRUENAS_IP:8080 \ + --set storage.data.hostPath=/mnt/pool/apps/project-nomad/storage +``` + +#### 2. Verify Deployment + +```bash +helm status project-nomad +kubectl get pods -l app.kubernetes.io/instance=project-nomad +``` + +## TrueNAS-Specific Configuration + +### Storage Best Practices + +**ZFS Dataset Recommendations:** + +| Dataset | Record Size | Compression | Purpose | +|---------|-------------|-------------|---------| +| `project-nomad/storage` | 1M | LZ4 | Large content files | +| `project-nomad/database` | 16K | LZ4 | MySQL data (if not using Docker volume) | +| `project-nomad/redis` | 128K | LZ4 | Redis persistence | + +**Performance Tip:** Use a Docker named volume for the MySQL database (default in docker-compose.yml). Docker volumes on TrueNAS use the app pool, which typically has better I/O than network-accessed datasets. + +### Network Configuration + +TrueNAS SCALE apps run in an isolated network by default. To access Nomad from your LAN: + +1. The compose file maps port 8080 to the host +2. Access via `http://YOUR_TRUENAS_IP:8080` + +If using the Nginx proxy (port 80): +- Ensure port 80 isn't used by TrueNAS web UI +- Change `NGINX_HTTP_PORT` in `.env` if needed + +### Permissions + +TrueNAS uses ACLs for dataset permissions. Set the dataset owner: + +```bash +# For Docker Compose deployments +chown -R 1000:1000 /mnt/pool/apps/project-nomad/storage +``` + +Or configure ACLs in the TrueNAS UI: +1. Go to **Storage** → select your dataset → **Edit Permissions** +2. Set User: `1000`, Group: `1000` +3. Apply recursively + +## Updating on TrueNAS + +### Docker Compose Method + +```bash +cd /mnt/pool/apps/project-nomad +docker compose pull +docker compose up -d +``` + +### Helm Method + +```bash +helm upgrade project-nomad ./homelab/truenas \ + --reuse-values +``` + +## Monitoring on TrueNAS + +TrueNAS SCALE includes built-in reporting. You can supplement it with the Nomad monitoring agent: + +```bash +docker run -d --name nomad-agent \ + --restart unless-stopped \ + -p 9100:9100 \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + -v /proc:/host/proc:ro \ + -v /sys:/host/sys:ro \ + -e NOMAD_SERVER_URL=http://nomad-app:8080 \ + -e NODE_NAME=truenas \ + nomad-agent +``` + +## Troubleshooting + +### App Won't Start + +```bash +# Check container status +docker compose ps + +# View logs +docker compose logs nomad-app +docker compose logs nomad-database +``` + +### Database Issues + +If the database fails to initialize: + +```bash +# Check MySQL logs +docker compose logs nomad-database + +# Verify the database volume +docker volume inspect project-nomad_nomad-db-data +``` + +### Port Conflicts + +TrueNAS web UI uses ports 80/443 by default. Adjust in `.env`: + +``` +NGINX_HTTP_PORT=8081 +NGINX_HTTPS_PORT=8443 +``` diff --git a/docs/homelab/unraid-guide.md b/docs/homelab/unraid-guide.md new file mode 100644 index 0000000..fc7a34e --- /dev/null +++ b/docs/homelab/unraid-guide.md @@ -0,0 +1,194 @@ +# Project N.O.M.A.D. — Unraid Installation Guide + +## Overview + +This guide covers installing Project N.O.M.A.D. Homelab Edition on Unraid using either Docker Compose or the Community Apps template. + +## Method 1: Docker Compose (Recommended) + +### Prerequisites + +- Unraid 6.12+ +- Docker enabled in Unraid settings +- Docker Compose plugin installed (available via Community Apps) + +### Steps + +#### 1. Create the Application Directory + +Open an Unraid terminal (or SSH): + +```bash +mkdir -p /mnt/user/appdata/project-nomad +cd /mnt/user/appdata/project-nomad +``` + +#### 2. Download Configuration Files + +```bash +# Download docker-compose.yml +curl -fsSL https://raw.githubusercontent.com/DocwatZ/project-nomad-homelab-edition/main/docker-compose.yml -o docker-compose.yml + +# Download .env.example +curl -fsSL https://raw.githubusercontent.com/DocwatZ/project-nomad-homelab-edition/main/.env.example -o .env + +# Download nginx config +mkdir -p nginx +curl -fsSL https://raw.githubusercontent.com/DocwatZ/project-nomad-homelab-edition/main/nginx/default.conf -o nginx/default.conf + +# Download entrypoint +mkdir -p install +curl -fsSL https://raw.githubusercontent.com/DocwatZ/project-nomad-homelab-edition/main/install/entrypoint.sh -o install/entrypoint.sh +chmod +x install/entrypoint.sh +``` + +#### 3. Configure Environment + +```bash +# Generate secrets +APP_KEY=$(openssl rand -hex 32) +DB_PASS=$(openssl rand -base64 24) + +# Update .env +sed -i "s/^APP_KEY=replaceme/APP_KEY=$APP_KEY/" .env +sed -i "s/^DB_PASSWORD=replaceme/DB_PASSWORD=$DB_PASS/" .env +sed -i "s/^MYSQL_ROOT_PASSWORD=replaceme/MYSQL_ROOT_PASSWORD=$DB_PASS/" .env + +# Set Unraid storage path +sed -i "s|^NOMAD_DATA_DIR=.*|NOMAD_DATA_DIR=/mnt/user/appdata/project-nomad|" .env + +# Set your server URL (replace with your Unraid IP) +sed -i "s|^URL=.*|URL=http://$(hostname -I | awk '{print $1}'):8080|" .env +``` + +#### 4. Create Storage Directories + +```bash +mkdir -p /mnt/user/appdata/project-nomad/{storage,redis,logs/nginx} +``` + +#### 5. Start the Stack + +```bash +docker compose up -d +``` + +#### 6. Access Nomad + +Open your browser: `http://YOUR_UNRAID_IP:8080` + +### Storage Layout on Unraid + +``` +/mnt/user/appdata/project-nomad/ +├── docker-compose.yml +├── .env +├── nginx/ +│ └── default.conf +├── install/ +│ └── entrypoint.sh +├── storage/ # Content files (cache-only share recommended) +├── redis/ # Redis data +└── logs/ + └── nginx/ # Nginx logs +``` + +**Tip:** For large content libraries (ZIM files, maps), consider storing them on a separate Unraid share with cache-preferred settings for better I/O performance. + +## Method 2: Community Apps Template + +### Steps + +1. Install the **Docker Compose Manager** plugin from Community Apps +2. In Unraid web UI, go to **Docker** → **Add Container** +3. Click **Template** and paste the template URL: + ``` + https://raw.githubusercontent.com/DocwatZ/project-nomad-homelab-edition/main/homelab/unraid-template.xml + ``` +4. Configure the required fields: + - **APP_KEY**: Generate with `openssl rand -hex 32` + - **DB_PASSWORD**: Set a secure password + - **URL**: Your Unraid server URL +5. Click **Apply** + +> **Note:** The Community Apps template creates only the Nomad application container. You still need separate MySQL and Redis containers. The Docker Compose method handles all services automatically. + +## Unraid-Specific Tips + +### Use Cache Drive for Database + +For best performance, store the MySQL database on your Unraid cache drive: + +```bash +# In .env, the database uses a Docker named volume by default +# This automatically stores on your cache drive +``` + +### Reverse Proxy with Unraid's SWAG/LSIO + +If you use the SWAG (Secure Web Application Gateway) container: + +1. Create a proxy config in `/mnt/user/appdata/swag/nginx/proxy-confs/`: + +```nginx +# nomad.subdomain.conf +server { + listen 443 ssl; + server_name nomad.*; + include /config/nginx/ssl.conf; + + location / { + proxy_pass http://nomad-app:8080; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + client_max_body_size 10G; + } +} +``` + +2. Ensure SWAG and Nomad share a Docker network. + +### Auto-Start on Boot + +Docker Compose stacks with `restart: unless-stopped` will automatically restart when Unraid boots and Docker starts. + +## Updating on Unraid + +```bash +cd /mnt/user/appdata/project-nomad +docker compose pull +docker compose up -d +``` + +## Troubleshooting + +### Permission Issues + +Unraid runs containers as root by default. If you encounter permission issues: + +```bash +chown -R nobody:users /mnt/user/appdata/project-nomad/storage +chmod -R 755 /mnt/user/appdata/project-nomad/storage +``` + +### Network Conflicts + +If port 8080 conflicts with another container, change the port in `.env`: + +``` +PORT=8088 +``` + +### Logs + +```bash +# View all container logs +docker compose logs -f + +# View specific service logs +docker compose logs -f nomad-app +``` diff --git a/install/entrypoint.sh b/install/entrypoint.sh index 17bdc95..c9ad46d 100644 --- a/install/entrypoint.sh +++ b/install/entrypoint.sh @@ -2,25 +2,68 @@ set -e -echo "Starting entrypoint script..." -echo "Running wait-for-it.sh to ensure MySQL is ready..." +echo "============================================" +echo " Project N.O.M.A.D. — Homelab Edition" +echo " Starting up..." +echo "============================================" -# Use wait-for-it.sh to wait for MySQL to be available -# wait-for-it.sh : [-t timeout] [-- command args] -/usr/local/bin/wait-for-it.sh ${DB_HOST}:${DB_PORT} -t 60 -- echo "MySQL is up and running!" +# --------------------------------------------------------------------------- +# Wait for database to be ready (no external dependencies like wait-for-it.sh) +# --------------------------------------------------------------------------- +DB_HOST="${DB_HOST:-localhost}" +DB_PORT="${DB_PORT:-3306}" +MAX_RETRIES=60 +RETRY_INTERVAL=2 -# Run AdonisJS migrations -echo "Running AdonisJS migrations..." +echo "Waiting for database at ${DB_HOST}:${DB_PORT}..." +retries=0 +while [ $retries -lt $MAX_RETRIES ]; do + if curl -sf "http://${DB_HOST}:${DB_PORT}" >/dev/null 2>&1 || \ + node -e "const net = require('net'); const s = new net.Socket(); s.setTimeout(2000); s.connect(${DB_PORT}, '${DB_HOST}', () => { s.destroy(); process.exit(0); }); s.on('error', () => process.exit(1)); s.on('timeout', () => { s.destroy(); process.exit(1); });" 2>/dev/null; then + echo "Database is ready!" + break + fi + retries=$((retries + 1)) + echo " Waiting for database... (attempt ${retries}/${MAX_RETRIES})" + sleep $RETRY_INTERVAL +done + +if [ $retries -eq $MAX_RETRIES ]; then + echo "ERROR: Database did not become ready in time. Continuing anyway..." +fi + +# --------------------------------------------------------------------------- +# Ensure storage directories exist +# --------------------------------------------------------------------------- +STORAGE_PATH="${NOMAD_STORAGE_PATH:-/app/storage}" +echo "Ensuring storage directories exist at ${STORAGE_PATH}..." +mkdir -p "${STORAGE_PATH}" 2>/dev/null || true + +# --------------------------------------------------------------------------- +# Run database migrations +# --------------------------------------------------------------------------- +echo "Running database migrations..." node ace migration:run --force -# Seed the database if needed +# --------------------------------------------------------------------------- +# Seed the database +# --------------------------------------------------------------------------- echo "Seeding the database..." node ace db:seed -# Start background workers for all queues -echo "Starting background workers for all queues..." -node ace queue:work --all & +# --------------------------------------------------------------------------- +# Start background workers (only if not running as a dedicated worker) +# --------------------------------------------------------------------------- +if [ "${NOMAD_ROLE}" != "worker" ]; then + echo "Starting background workers for all queues..." + node ace queue:work --all & +fi -# Start the AdonisJS application -echo "Starting AdonisJS application..." +# --------------------------------------------------------------------------- +# Start the application +# --------------------------------------------------------------------------- +echo "============================================" +echo " N.O.M.A.D. is ready!" +echo " Listening on port ${PORT:-8080}" +echo "============================================" exec node bin/server.js \ No newline at end of file