From 0fe34fb0e4e1ce08e0432f1e8b0f264b2621aff6 Mon Sep 17 00:00:00 2001 From: Phoenix/HotaruBlaze Date: Fri, 3 Oct 2025 15:49:36 +0100 Subject: [PATCH] Pruning --- README.md | 2 +- modules/01-networking/caddy-proxy/README.md | 12 +- .../cloudflared-tunnel/README.md | 4 +- .../20-services-apps/actualbudget/README.md | 77 ----- modules/20-services-apps/actualbudget/main.tf | 52 ---- modules/20-services-apps/affine/.env.example | 17 -- modules/20-services-apps/affine/README.md | 121 -------- modules/20-services-apps/affine/main.tf | 198 ------------- modules/20-services-apps/arr/.env.example | 34 --- modules/20-services-apps/arr/README.md | 140 --------- modules/20-services-apps/arr/main.tf | 280 ------------------ modules/20-services-apps/copyparty/README.md | 85 ------ modules/20-services-apps/copyparty/main.tf | 89 ------ .../20-services-apps/crawl4ai/.env.example | 18 -- modules/20-services-apps/crawl4ai/README.md | 91 ------ modules/20-services-apps/crawl4ai/main.tf | 99 ------- .../20-services-apps/emulatorjs/.env.example | 3 - modules/20-services-apps/emulatorjs/README.md | 99 ------- modules/20-services-apps/emulatorjs/main.tf | 78 ----- modules/20-services-apps/gluetun/.env.example | 33 --- modules/20-services-apps/gluetun/README.md | 65 ---- modules/20-services-apps/gluetun/main.tf | 88 ------ modules/20-services-apps/n8n/.env.example | 14 - modules/20-services-apps/n8n/README.md | 159 ---------- modules/20-services-apps/n8n/init-data.sh | 13 - modules/20-services-apps/n8n/main.tf | 246 --------------- modules/20-services-apps/nocodb/.env.example | 4 - modules/20-services-apps/nocodb/README.md | 106 ------- modules/20-services-apps/nocodb/main.tf | 129 -------- modules/20-services-apps/ntfy/README.md | 81 ----- modules/20-services-apps/ntfy/main.tf | 58 ---- .../20-services-apps/pterodactyl/README.md | 101 ------- .../pterodactyl/panel/.env.example | 18 -- .../pterodactyl/panel/README.md | 109 ------- .../pterodactyl/panel/main.tf | 164 ---------- .../pterodactyl/wings/README.md | 100 ------- .../pterodactyl/wings/main.tf | 118 -------- .../20-services-apps/qbittorrent/README.md | 117 -------- modules/20-services-apps/qbittorrent/main.tf | 86 ------ modules/20-services-apps/sabnzbd/README.md | 78 ----- modules/20-services-apps/sabnzbd/main.tf | 69 ----- modules/20-services-apps/searxng/README.md | 79 ----- modules/20-services-apps/searxng/main.tf | 60 ---- services/main.tf | 105 ------- services/outputs.tf | 17 +- 45 files changed, 10 insertions(+), 3706 deletions(-) delete mode 100644 modules/20-services-apps/actualbudget/README.md delete mode 100644 modules/20-services-apps/actualbudget/main.tf delete mode 100644 modules/20-services-apps/affine/.env.example delete mode 100644 modules/20-services-apps/affine/README.md delete mode 100644 modules/20-services-apps/affine/main.tf delete mode 100644 modules/20-services-apps/arr/.env.example delete mode 100644 modules/20-services-apps/arr/README.md delete mode 100644 modules/20-services-apps/arr/main.tf delete mode 100644 modules/20-services-apps/copyparty/README.md delete mode 100644 modules/20-services-apps/copyparty/main.tf delete mode 100644 modules/20-services-apps/crawl4ai/.env.example delete mode 100644 modules/20-services-apps/crawl4ai/README.md delete mode 100644 modules/20-services-apps/crawl4ai/main.tf delete mode 100644 modules/20-services-apps/emulatorjs/.env.example delete mode 100644 modules/20-services-apps/emulatorjs/README.md delete mode 100644 modules/20-services-apps/emulatorjs/main.tf delete mode 100644 modules/20-services-apps/gluetun/.env.example delete mode 100644 modules/20-services-apps/gluetun/README.md delete mode 100644 modules/20-services-apps/gluetun/main.tf delete mode 100644 modules/20-services-apps/n8n/.env.example delete mode 100644 modules/20-services-apps/n8n/README.md delete mode 100644 modules/20-services-apps/n8n/init-data.sh delete mode 100644 modules/20-services-apps/n8n/main.tf delete mode 100644 modules/20-services-apps/nocodb/.env.example delete mode 100644 modules/20-services-apps/nocodb/README.md delete mode 100644 modules/20-services-apps/nocodb/main.tf delete mode 100644 modules/20-services-apps/ntfy/README.md delete mode 100644 modules/20-services-apps/ntfy/main.tf delete mode 100644 modules/20-services-apps/pterodactyl/README.md delete mode 100644 modules/20-services-apps/pterodactyl/panel/.env.example delete mode 100644 modules/20-services-apps/pterodactyl/panel/README.md delete mode 100644 modules/20-services-apps/pterodactyl/panel/main.tf delete mode 100644 modules/20-services-apps/pterodactyl/wings/README.md delete mode 100644 modules/20-services-apps/pterodactyl/wings/main.tf delete mode 100644 modules/20-services-apps/qbittorrent/README.md delete mode 100644 modules/20-services-apps/qbittorrent/main.tf delete mode 100644 modules/20-services-apps/sabnzbd/README.md delete mode 100644 modules/20-services-apps/sabnzbd/main.tf delete mode 100644 modules/20-services-apps/searxng/README.md delete mode 100644 modules/20-services-apps/searxng/main.tf diff --git a/README.md b/README.md index f78e85c..f5e2b66 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ homelab/ │ └── docker-service/ # Generic module for deploying Docker containers └── 20-services-apps/ # Application-specific wrapper modules ├── jellyfin/ - ├── affine/ + ├── calibre/ └── ... # Other application modules │ └── services/ # Application services (Docker containers) diff --git a/modules/01-networking/caddy-proxy/README.md b/modules/01-networking/caddy-proxy/README.md index 0c1e151..4b535d1 100644 --- a/modules/01-networking/caddy-proxy/README.md +++ b/modules/01-networking/caddy-proxy/README.md @@ -101,14 +101,14 @@ The `publish_via` field controls which networking module(s) will expose the serv ### Basic Service with Default Settings ```hcl -# Example based on ntfy (reverse-proxy only with direct IP exposure) +# Example based on jellyfin (reverse-proxy only with direct IP exposure) output "service_definition" { - description = "Service definition for a notification service" + description = "Service definition for a media server" value = { - name = "ntfy" - primary_port = 80 - endpoint = "http://ntfy:80" - subdomains = ["ntfy"] + name = "jellyfin" + primary_port = 8096 + endpoint = "http://jellyfin:8096" + subdomains = ["media"] publish_via = "reverse_proxy" # Only expose via Caddy reverse proxy proxied = false # Don't proxy through Cloudflare (expose direct IP) } diff --git a/modules/01-networking/cloudflared-tunnel/README.md b/modules/01-networking/cloudflared-tunnel/README.md index 19eb7ea..85c2067 100644 --- a/modules/01-networking/cloudflared-tunnel/README.md +++ b/modules/01-networking/cloudflared-tunnel/README.md @@ -72,8 +72,8 @@ module "homelab_tunnel" { tunnel_name = "homelab-tunnel" ingress_rules = [ { - hostname = "budget.${module.cloudflare_globals.domain}" - service = "http://actualbudget:5006" + hostname = "media.${module.cloudflare_globals.domain}" + service = "http://jellyfin:8096" } ] } diff --git a/modules/20-services-apps/actualbudget/README.md b/modules/20-services-apps/actualbudget/README.md deleted file mode 100644 index a5a4a62..0000000 --- a/modules/20-services-apps/actualbudget/README.md +++ /dev/null @@ -1,77 +0,0 @@ -# ActualBudget Module - -This module deploys [ActualBudget](https://actualbudget.com/), a personal finance and budgeting application, as a Docker container in the homelab environment. - -## Overview - -The ActualBudget module: - -- Deploys the `actualbudget/actual-server` Docker container -- Persists data to a volume on the host -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "actualbudget" { - source = "./modules/20-services-apps/actualbudget" - volume_path = "/path/to/volumes/actualbudget" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | ---------------------------------------------------------- | -------------- | ---------- | -| `image_tag` | Tag of the ActualBudget image to use | `string` | `"latest"` | -| `volume_path` | Host path for ActualBudget data volume | `string` | - | -| `networks` | List of networks to which the container should be attached | `list(string)` | - | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "actualbudget" - primary_port = 5006 - endpoint = "http://actualbudget:5006" - subdomains = ["budget"] - publish_via = "tunnel" # Only publish through Cloudflare tunnel -} -``` - -## Data Persistence - -ActualBudget stores its data in the `/data` directory inside the container. This is mapped to a volume on the host at `${volume_path}/data`. - -## Integration with Networking Modules - -This service is configured to be exposed through a Cloudflare tunnel for secure remote access, set by `publish_via = "tunnel"`. - -## Example Integration in Main Configuration - -```hcl -module "actualbudget" { - source = "./modules/20-services-apps/actualbudget" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.actualbudget.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/actualbudget/main.tf b/modules/20-services-apps/actualbudget/main.tf deleted file mode 100644 index f1cf633..0000000 --- a/modules/20-services-apps/actualbudget/main.tf +++ /dev/null @@ -1,52 +0,0 @@ -variable "image_tag" { - description = "Tag of the ActualBudget image to use" - type = string - default = "latest" -} - -variable "volume_path" { - description = "Host path for ActualBudget data volume" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) -} - -locals { - container_name = "actualbudget" - image = "actualbudget/actual-server" - image_tag = var.image_tag != "" ? var.image_tag : "latest" - monitoring = true - exposed_port = 5006 - subdomains = ["budget"] - default_volumes = [ - { - container_path = "/data" - host_path = "${var.volume_path}/data" - read_only = false - } - ] -} - -module "actualbudget" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.image_tag - volumes = local.default_volumes - networks = var.networks - monitoring = local.monitoring -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = local.exposed_port - endpoint = "http://${local.container_name}:${local.exposed_port}" - subdomains = local.subdomains - publish_via = "tunnel" - } -} diff --git a/modules/20-services-apps/affine/.env.example b/modules/20-services-apps/affine/.env.example deleted file mode 100644 index c4b2427..0000000 --- a/modules/20-services-apps/affine/.env.example +++ /dev/null @@ -1,17 +0,0 @@ -# Affine Configuration -AFFINE_REVISION=canary -PORT=3010 - -AFFINE_SERVER_HTTPS=true -AFFINE_SERVER_HOST=affine.yourdomain.com -AFFINE_SERVER_NAME='AFFiNE Selfhosted' - -# Database Configuration -DB_USERNAME=affine -DB_PASSWORD=change_this_password -DB_DATABASE=affine - -# R2 Configuration -R2_OBJECT_STORAGE_ACCOUNT_ID= -R2_OBJECT_STORAGE_ACCESS_KEY_ID= -R2_OBJECT_STORAGE_SECRET_ACCESS_KEY= diff --git a/modules/20-services-apps/affine/README.md b/modules/20-services-apps/affine/README.md deleted file mode 100644 index a8294b4..0000000 --- a/modules/20-services-apps/affine/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# AFFiNE Module - -This module deploys [AFFiNE](https://affine.pro/), a privacy-first, local-first, note-taking and knowledge base application, as Docker containers in the homelab environment. - -## Overview - -The AFFiNE module: - -- Deploys four Docker containers: - - `affine_server`: The main AFFiNE application server - - `affine_migration_job`: A container that runs pre-deployment migrations - - `affine_postgres`: A PostgreSQL (pgvector) database backend - - `affine_redis`: A Redis instance for caching and temporary data -- Creates a dedicated Docker network (`affine-network`) for container communication -- Persists data to volumes on the host -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "affine" { - source = "./modules/20-services-apps/affine" - volume_path = "/path/to/volumes/affine" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | -------------------------------------------------------------- | -------------- | ---------- | -| `image_tag` | Tag of the AFFiNE image to use | `string` | `"stable"` | -| `volume_path` | Host path for AFFiNE and database data volumes | `string` | - | -| `networks` | List of additional networks to which AFFiNE should be attached | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "affine_server" - primary_port = 3010 - endpoint = "http://affine_server:3010" - subdomains = ["affine"] - publish_via = "tunnel" # Only publish through Cloudflare tunnel -} -``` - -## Environment Variables - -AFFiNE requires several environment variables to function properly. These are stored in a `.env` file in the module directory and read using the `dotenv` Terraform provider: - -- Database configuration: - - - `DB_USERNAME`: PostgreSQL user - - `DB_PASSWORD`: PostgreSQL password - - `DB_DATABASE`: Database name (defaults to "affine") - -- AFFiNE configuration: - - - `AFFINE_REVISION`: Version of AFFiNE to use ("stable" or "canary") (defaults to "canary") - - `PORT`: External port for the AFFiNE server (defaults to 3010) - - `AFFINE_SERVER_HTTPS`: Whether to use HTTPS (defaults to "true") - - `AFFINE_SERVER_HOST`: Hostname for the AFFiNE server - - `AFFINE_SERVER_NAME`: Name for the AFFiNE server (defaults to "AFFiNE Selfhosted") - -- Cloudflare R2 configuration: - - `R2_OBJECT_STORAGE_ACCOUNT_ID`: Cloudflare R2 account ID - - `R2_OBJECT_STORAGE_ACCESS_KEY_ID`: Cloudflare R2 access key ID - - `R2_OBJECT_STORAGE_SECRET_ACCESS_KEY`: Cloudflare R2 secret access key - -## Data Persistence - -AFFiNE stores its data in three main volumes: - -1. AFFiNE application data: `/root/.affine/storage` in the container, mapped to `${volume_path}/self-host/storage` on the host -2. AFFiNE configuration: `/root/.affine/config` in the container, mapped to `${volume_path}/self-host/config` on the host -3. PostgreSQL data: `/var/lib/postgresql/data` in the container, mapped to `${volume_path}/self-host/postgres/pgdata` on the host - -## Networking - -The module creates a dedicated Docker network named `affine-network` for communication between the AFFiNE components. The AFFiNE server container is also attached to any additional networks specified in the `networks` variable, allowing it to communicate with other services in the homelab. - -## Dependencies - -The AFFiNE containers have the following dependencies: - -- The main `affine_server` depends on PostgreSQL, Redis, and the migration job -- The migration job depends on PostgreSQL and Redis -- Both PostgreSQL and Redis use healthchecks to ensure they're ready before dependent services start - -## Integration with Networking Modules - -This service is configured to be exposed through the Caddy reverse proxy, set by `publish_via = "reverse_proxy"`. - -## Example Integration in Main Configuration - -```hcl -module "affine" { - source = "./modules/20-services-apps/affine" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.affine.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/affine/main.tf b/modules/20-services-apps/affine/main.tf deleted file mode 100644 index 4b64ab2..0000000 --- a/modules/20-services-apps/affine/main.tf +++ /dev/null @@ -1,198 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -variable "image_tag" { - description = "The tag for the affine container image" - type = string - default = "stable" -} - -variable "volume_path" { - description = "Base directory for volumes" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -module "smtp" { - source = "../../00-globals/smtp" -} - -locals { - container_name = "affine-server" - migration_name = "affine-migration-job" - redis_name = "affine-redis" - postgres_name = "affine-postgres" - affine_image = "ghcr.io/yurisasc/affine-graphql" - postgres_image = "pgvector/pgvector" - redis_image = "redis" - affine_tag = provider::dotenv::get_by_key("AFFINE_REVISION", local.env_file) - postgres_tag = "pg16" - redis_tag = "latest" - monitoring = true - env_file = "${path.module}/.env" - affine_internal_port = 3010 - - # Define volumes - affine_volumes = [ - { - host_path = "${var.volume_path}/self-host/storage" - container_path = "/root/.affine/storage" - read_only = false - }, - { - host_path = "${var.volume_path}/self-host/config" - container_path = "/root/.affine/config" - read_only = false - } - ] - - migration_volumes = [ - { - host_path = "${var.volume_path}/self-host/storage" - container_path = "/root/.affine/storage" - read_only = false - }, - { - host_path = "${var.volume_path}/self-host/config" - container_path = "/root/.affine/config" - read_only = false - } - ] - - postgres_volumes = [ - { - host_path = "${var.volume_path}/self-host/postgres/pgdata" - container_path = "/var/lib/postgresql/data" - read_only = false - } - ] - - # Environment variables for postgres - postgres_env_vars = { - POSTGRES_USER = provider::dotenv::get_by_key("DB_USERNAME", local.env_file) - POSTGRES_PASSWORD = provider::dotenv::get_by_key("DB_PASSWORD", local.env_file) - POSTGRES_DB = provider::dotenv::get_by_key("DB_DATABASE", local.env_file) - POSTGRES_INITDB_ARGS = "--data-checksums" - POSTGRES_HOST_AUTH_METHOD = "trust" - } - - # Environment variables for AFFiNE - affine_env_vars = { - REDIS_SERVER_HOST = local.redis_name - DATABASE_URL = "postgresql://${provider::dotenv::get_by_key("DB_USERNAME", local.env_file)}:${provider::dotenv::get_by_key("DB_PASSWORD", local.env_file)}@${local.postgres_name}:5432/${provider::dotenv::get_by_key("DB_DATABASE", local.env_file)}" - AFFINE_INDEXER_ENABLED = "false" - AFFINE_SERVER_HTTPS = provider::dotenv::get_by_key("AFFINE_SERVER_HTTPS", local.env_file) - AFFINE_SERVER_HOST = provider::dotenv::get_by_key("AFFINE_SERVER_HOST", local.env_file) - AFFINE_SERVER_NAME = provider::dotenv::get_by_key("AFFINE_SERVER_NAME", local.env_file) - PORT = provider::dotenv::get_by_key("PORT", local.env_file) - DB_USERNAME = provider::dotenv::get_by_key("DB_USERNAME", local.env_file) - DB_PASSWORD = provider::dotenv::get_by_key("DB_PASSWORD", local.env_file) - DB_DATABASE = provider::dotenv::get_by_key("DB_DATABASE", local.env_file) - MAILER_HOST = module.smtp.mail_host - MAILER_PORT = module.smtp.mail_port - MAILER_USER = module.smtp.mail_username - MAILER_PASSWORD = module.smtp.mail_password - R2_OBJECT_STORAGE_ACCOUNT_ID = provider::dotenv::get_by_key("R2_OBJECT_STORAGE_ACCOUNT_ID", local.env_file) - R2_OBJECT_STORAGE_ACCESS_KEY_ID = provider::dotenv::get_by_key("R2_OBJECT_STORAGE_ACCESS_KEY_ID", local.env_file) - R2_OBJECT_STORAGE_SECRET_ACCESS_KEY = provider::dotenv::get_by_key("R2_OBJECT_STORAGE_SECRET_ACCESS_KEY", local.env_file) - } - - # Healthcheck configuration for Redis - redis_healthcheck = { - test = ["CMD", "redis-cli", "--raw", "incr", "ping"] - interval = "10s" - timeout = "5s" - retries = 5 - start_period = "5s" - } - - # Healthcheck configuration for Postgres - postgres_healthcheck = { - test = ["CMD", "pg_isready", "-U", provider::dotenv::get_by_key("DB_USERNAME", local.env_file), "-d", provider::dotenv::get_by_key("DB_DATABASE", local.env_file)] - interval = "10s" - timeout = "5s" - retries = 5 - start_period = "5s" - } -} - -module "affine_network" { - source = "../../01-networking/docker-network" - name = "affine-network" - subnet = "11.100.0.0/16" - driver = "bridge" -} - -# Create the Redis container -module "redis" { - source = "../../10-services-generic/docker-service" - container_name = local.redis_name - image = local.redis_image - tag = local.redis_tag - networks = [module.affine_network.name] - monitoring = local.monitoring - healthcheck = local.redis_healthcheck -} - -# Create the PostgreSQL container -module "postgres" { - source = "../../10-services-generic/docker-service" - container_name = local.postgres_name - image = local.postgres_image - tag = local.postgres_tag - volumes = local.postgres_volumes - env_vars = local.postgres_env_vars - networks = [module.affine_network.name] - monitoring = local.monitoring - healthcheck = local.postgres_healthcheck -} - -# Create the migration job container -module "migration" { - source = "../../10-services-generic/docker-service" - container_name = local.migration_name - image = local.affine_image - tag = local.affine_tag - volumes = local.migration_volumes - env_vars = local.affine_env_vars - command = ["sh", "-c", "node ./scripts/self-host-predeploy.js"] - networks = [module.affine_network.name] - monitoring = local.monitoring - depends_on = [module.postgres, module.redis] - restart_policy = "no" -} - -# Create the affine container -module "affine" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.affine_image - tag = local.affine_tag - volumes = local.affine_volumes - env_vars = local.affine_env_vars - networks = concat([module.affine_network.name], var.networks) - monitoring = local.monitoring - depends_on = [module.postgres, module.redis, module.migration] -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = local.affine_internal_port - endpoint = "http://${local.container_name}:${local.affine_internal_port}" - subdomains = ["notes"] - publish_via = "reverse_proxy" - proxied = true - } -} diff --git a/modules/20-services-apps/arr/.env.example b/modules/20-services-apps/arr/.env.example deleted file mode 100644 index 05d45f5..0000000 --- a/modules/20-services-apps/arr/.env.example +++ /dev/null @@ -1,34 +0,0 @@ -# Example secrets for the *arr stack (used via terraform-provider-dotenv) -# Copy to .env in this same directory and fill in values. - -# Required keys for helpers -SONARR_API_KEY= -RADARR_API_KEY= -LIDARR_API_KEY= -QBITTORRENT_USERNAME= -QBITTORRENT_PASSWORD= - -# Optional keys for Flaresolverr -LOG_LEVEL=info -LOG_HTML=false -CAPTCHA_SOLVER=none - -# Optional keys for Decluttarr -# Uncomment and adjust as needed. Defaults shown here align with the README. -#DECLUTTARR_LOG_LEVEL=INFO -#DECLUTTARR_TEST_RUN=False -#DECLUTTARR_REMOVE_TIMER=10 -#DECLUTTARR_REMOVE_FAILED=True -#DECLUTTARR_REMOVE_FAILED_IMPORTS=True -#DECLUTTARR_REMOVE_METADATA_MISSING=True -#DECLUTTARR_REMOVE_MISSING_FILES=True -#DECLUTTARR_REMOVE_ORPHANS=True -#DECLUTTARR_REMOVE_SLOW=True -#DECLUTTARR_REMOVE_STALLED=True -#DECLUTTARR_REMOVE_UNMONITORED=True -#DECLUTTARR_RUN_PERIODIC_RESCANS= -#DECLUTTARR_PERMITTED_ATTEMPTS=3 -#DECLUTTARR_REMOVAL_QBIT_TAG=stalled -#DECLUTTARR_MIN_DOWNLOAD_SPEED=100 -#DECLUTTARR_FAILED_IMPORT_MESSAGE_PATTERNS= -#DECLUTTARR_IGNORED_DOWNLOAD_CLIENTS= diff --git a/modules/20-services-apps/arr/README.md b/modules/20-services-apps/arr/README.md deleted file mode 100644 index 0eec3ca..0000000 --- a/modules/20-services-apps/arr/README.md +++ /dev/null @@ -1,140 +0,0 @@ -# *arr Stack Module - -This module deploys the *arr media stack components as Docker containers and provides a service definition for Jellyseerr to be published via your reverse proxy. - -## Overview - -The module includes the following containers: - -- Sonarr (TV) -- Radarr (Movies) -- Lidarr (Music) -- Bazarr (Subtitles) -- Prowlarr (Indexers) -- Jellyseerr (Requests) — published via reverse proxy -- Flaresolverr (optional helper) -- Unpackerr (post-processing) -- Cleanuparr (cleanup helpers) -- Decluttarr (torrent queue cleanup helper) - -All containers share a common `/data` mount (media root) and are attached to the media Docker network. Only Jellyseerr is also attached to the proxy network for reachability by Caddy. - -## Usage - -```hcl -module "arr" { - source = "./modules/20-services-apps/arr" - volume_path = "/srv/appdata/arr" # host path for config directories - data_path = "/srv/data" # host media root, mounted as /data in containers - downloads_path = "/srv/data/torrents" # host downloads dir, mounted for unpackerr - networks = [module.media_docker_network.name] - proxy_networks = [module.homelab_docker_network.name] # so Jellyseerr is reachable by Caddy -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------------ | ------------------------------------------------------------------------------- | -------------- | --------------- | -| `volume_path` | Base directory for config volumes for the *arr stack | `string` | - | -| `data_path` | Base directory for media/data mounted at `/data` | `string` | - | -| `downloads_path` | Directory for downloads mounted at `/data/torrents` (Unpackerr) | `string` | - | -| `networks` | Networks to attach all containers to | `list(string)` | `[]` | -| `proxy_networks` | Extra networks attached only to published services (Jellyseerr) | `list(string)` | `[]` | -| `qbittorrent_host` | Hostname to reach qBittorrent (use `gluetun` when qBittorrent shares Gluetun) | `string` | `"qbittorrent"` | - -## Outputs - -| Output | Description | -| -------------------- | --------------------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "jellyseerr" - primary_port = 5055 - endpoint = "http://jellyseerr:5055" - subdomains = ["req"] - publish_via = "reverse_proxy" - proxied = false -} -``` - -## Environment Variables (.env) - -This module reads secrets via the `dotenv` provider from a `.env` file located in this module directory. Use `.env.example` as a template. - -Required/used keys: - -- `SONARR_API_KEY` — for Unpackerr, Cleanuparr, Decluttarr -- `RADARR_API_KEY` — for Unpackerr, Cleanuparr, Decluttarr -- `LIDARR_API_KEY` — for Decluttarr -- `QBITTORRENT_USERNAME` — for Decluttarr -- `QBITTORRENT_PASSWORD` — for Cleanuparr and Decluttarr -- `LOG_LEVEL` — for Flaresolverr (optional) -- `LOG_HTML` — for Flaresolverr (optional) -- `CAPTCHA_SOLVER` — for Flaresolverr (optional) - -Optional Decluttarr keys (override defaults as needed): - -- `DECLUTTARR_LOG_LEVEL` (default: `INFO`) -- `DECLUTTARR_TEST_RUN` (default: `False`) -- `DECLUTTARR_REMOVE_TIMER` (default: `10`) -- `DECLUTTARR_REMOVE_FAILED` (default: `True`) -- `DECLUTTARR_REMOVE_FAILED_IMPORTS` (default: `True`) -- `DECLUTTARR_REMOVE_METADATA_MISSING` (default: `True`) -- `DECLUTTARR_REMOVE_MISSING_FILES` (default: `True`) -- `DECLUTTARR_REMOVE_ORPHANS` (default: `True`) -- `DECLUTTARR_REMOVE_SLOW` (default: `True`) -- `DECLUTTARR_REMOVE_STALLED` (default: `True`) -- `DECLUTTARR_REMOVE_UNMONITORED` (default: `True`) -- `DECLUTTARR_RUN_PERIODIC_RESCANS` (default: empty) -- `DECLUTTARR_PERMITTED_ATTEMPTS` (default: `3`) -- `DECLUTTARR_REMOVAL_QBIT_TAG` (default: `stalled`) -- `DECLUTTARR_MIN_DOWNLOAD_SPEED` (default: `100`) -- `DECLUTTARR_FAILED_IMPORT_MESSAGE_PATTERNS` (default: empty) -- `DECLUTTARR_IGNORED_DOWNLOAD_CLIENTS` (default: empty) - -Note: `TZ`, `PUID`, `PGID` are injected automatically by the generic docker-service module from `modules/00-globals/system` and do not need to be in this `.env`. - -## Data Persistence - -- Each app stores configuration under `${volume_path}//...` mounted to `/config` (or as noted by the specific app). -- Media library and downloads are accessed under `/data` inside containers, pointing to `data_path` on the host. -- Unpackerr mounts `downloads_path` to `/data/torrents`. - -## Networking - -- All containers join `networks` (media network). -- Jellyseerr additionally joins `proxy_networks` for reverse proxy reachability. - -## Dependencies - -- No explicit inter-container dependencies are defined. Healthchecks are provided for stable orchestration. -- Decluttarr expects to reach `sonarr`, `radarr`, and `lidarr` via internal DNS and qBittorrent at `http://:8080`. When qBittorrent is routed via Gluetun using `network_mode=container:gluetun`, set `qbittorrent_host = "gluetun"`. Ensure they share the same Docker network. - -## Integration with Networking Modules - -This service is configured to be exposed through the Caddy reverse proxy, set by `publish_via = "reverse_proxy"`. - -## Example Integration in Main Configuration - -```hcl -# In services/main.tf -module "arr" { - source = "${local.module_dir}/20-services-apps/arr" - volume_path = "${local.volume_host}/arr" - data_path = local.data_host - downloads_path = "${local.data_host}/torrents" - networks = [module.media_docker_network.name] - proxy_networks = [module.homelab_docker_network.name] - # If qBittorrent shares Gluetun's network namespace, arr should reach it via 'gluetun' - qbittorrent_host = "gluetun" -} -``` - -The service definition is exported by the `services` module as `module.services.service_definitions` and consumed by networking modules in the root `main.tf`. diff --git a/modules/20-services-apps/arr/main.tf b/modules/20-services-apps/arr/main.tf deleted file mode 100644 index 9277087..0000000 --- a/modules/20-services-apps/arr/main.tf +++ /dev/null @@ -1,280 +0,0 @@ -terraform { - required_providers { - dotenv = { source = "germanbrew/dotenv" } - } -} - -variable "volume_path" { - description = "Base directory for config volumes for *arr stack" - type = string -} -variable "data_path" { - description = "Base directory for media/data mounted at /data" - type = string -} -variable "downloads_path" { - description = "Directory for downloads mounted at /data/torrents" - type = string -} -variable "networks" { - description = "Networks to attach all containers to" - type = list(string) - default = [] -} -variable "proxy_networks" { - description = "Extra networks to attach only to published services (e.g., Jellyseerr)" - type = list(string) - default = [] -} - -variable "qbittorrent_host" { - description = "Hostname to reach qBittorrent (use 'qbittorrent' normally, 'gluetun' when qBittorrent shares Gluetun network)" - type = string - default = "qbittorrent" -} - -locals { - env_file = "${path.module}/.env" - monitoring = true - - sonarr_name = "sonarr" - radarr_name = "radarr" - lidarr_name = "lidarr" - bazarr_name = "bazarr" - prowlarr_name = "prowlarr" - jellyseerr_name = "jellyseerr" - flaresolverr_name = "flaresolverr" - unpackerr_name = "unpackerr" - cleanuparr_name = "cleanuparr" - decluttarr_name = "decluttarr" - - sonarr_image = "lscr.io/linuxserver/sonarr" - radarr_image = "lscr.io/linuxserver/radarr" - lidarr_image = "lscr.io/linuxserver/lidarr" - bazarr_image = "lscr.io/linuxserver/bazarr" - prowlarr_image = "lscr.io/linuxserver/prowlarr" - jellyseerr_image = "ghcr.io/fallenbagel/jellyseerr" - flaresolverr_image = "21hsmw/flaresolverr" - unpackerr_image = "ghcr.io/unpackerr/unpackerr" - cleanuparr_image = "ghcr.io/cleanuparr/cleanuparr" - decluttarr_image = "ghcr.io/manimatter/decluttarr" - - sonarr_port = 8989 - radarr_port = 7878 - lidarr_port = 8686 - bazarr_port = 6767 - prowlarr_port = 9696 - jellyseerr_port = 5055 - flaresolverr_port = 8191 - - lidarr_healthcheck = { test = ["CMD", "curl", "--fail", "http://127.0.0.1:${local.lidarr_port}/lidarr/ping"], interval = "60s", timeout = "5s", retries = 10 } - bazarr_healthcheck = { test = ["CMD", "curl", "--fail", "http://127.0.0.1:${local.bazarr_port}/bazarr/ping"], interval = "60s", timeout = "5s", retries = 10 } - jellyseerr_healthcheck = { test = ["CMD", "wget", "http://127.0.0.1:${local.jellyseerr_port}/api/v1/status", "-qO", "/dev/null"], interval = "60s", timeout = "5s", retries = 10 } - - jellyseerr_env = { LOG_LEVEL = "debug" } - flaresolverr_env = { - LOG_LEVEL = try(provider::dotenv::get_by_key("LOG_LEVEL", local.env_file), "") - LOG_HTML = try(provider::dotenv::get_by_key("LOG_HTML", local.env_file), "") - CAPTCHA_SOLVER = try(provider::dotenv::get_by_key("CAPTCHA_SOLVER", local.env_file), "") - } - unpackerr_env = { - UN_SONARR_0_URL = "http://${local.sonarr_name}:${local.sonarr_port}/sonarr" - UN_SONARR_0_API_KEY = provider::dotenv::get_by_key("SONARR_API_KEY", local.env_file) - UN_RADARR_0_URL = "http://${local.radarr_name}:${local.radarr_port}/radarr" - UN_RADARR_0_API_KEY = provider::dotenv::get_by_key("RADARR_API_KEY", local.env_file) - } - cleanuparr_env = { - QUEUECLEANER__ENABLED = true - QUEUECLEANER__IMPORT_FAILED_MAX_STRIKES = 3 - QUEUECLEANER__STALLED_MAX_STRIKES = 3 - QUEUECLEANER__DOWNLOADING_METADATA_MAX_STRIKES = 3 - QUEUECLEANER__STALLED_RESET_STRIKES_ON_PROGRESS = true - TRIGGERS__QUEUECLEANER = "0 0 0/1 * * ?" - CONTENTBLOCKER__ENABLED = true - CONTENTBLOCKER__IGNORED_DOWNLOADS_PATH = "/usr/ignored.txt" - TRIGGERS__CONTENTBLOCKER = "0 0 0/1 * * ?" - DOWNLOAD_CLIENT = "qBittorrent" - QBITTORRENT__URL = "http://${var.qbittorrent_host}:8080" - QBITTORRENT__PASSWORD = provider::dotenv::get_by_key("QBITTORRENT_PASSWORD", local.env_file) - SONARR__ENABLED = true - SONARR__BLOCK__PATH = "/usr/blacklist.json" - SONARR__INSTANCES__0__URL = "http://${local.sonarr_name}:${local.sonarr_port}/sonarr" - SONARR__INSTANCES__0__APIKEY = provider::dotenv::get_by_key("SONARR_API_KEY", local.env_file) - RADARR__ENABLED = true - RADARR__BLOCK__PATH = "/usr/blacklist.json" - RADARR__INSTANCES__0__URL = "http://${local.radarr_name}:${local.radarr_port}/radarr" - RADARR__INSTANCES__0__APIKEY = provider::dotenv::get_by_key("RADARR_API_KEY", local.env_file) - } - - decluttarr_env = { - RADARR_URL = "http://${local.radarr_name}:${local.radarr_port}/radarr" - RADARR_KEY = provider::dotenv::get_by_key("RADARR_API_KEY", local.env_file) - SONARR_URL = "http://${local.sonarr_name}:${local.sonarr_port}/sonarr" - SONARR_KEY = provider::dotenv::get_by_key("SONARR_API_KEY", local.env_file) - LIDARR_URL = "http://${local.lidarr_name}:${local.lidarr_port}/lidarr" - LIDARR_KEY = provider::dotenv::get_by_key("LIDARR_API_KEY", local.env_file) - QBITTORRENT_URL = "http://${var.qbittorrent_host}:8080" - QBITTORRENT_USERNAME = provider::dotenv::get_by_key("QBITTORRENT_USERNAME", local.env_file) - QBITTORRENT_PASSWORD = provider::dotenv::get_by_key("QBITTORRENT_PASSWORD", local.env_file) - LOG_LEVEL = try(provider::dotenv::get_by_key("DECLUTTARR_LOG_LEVEL", local.env_file), "") - TEST_RUN = try(provider::dotenv::get_by_key("DECLUTTARR_TEST_RUN", local.env_file), "") - REMOVE_TIMER = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_TIMER", local.env_file), "") - REMOVE_FAILED = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_FAILED", local.env_file), "") - REMOVE_FAILED_IMPORTS = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_FAILED_IMPORTS", local.env_file), "") - REMOVE_METADATA_MISSING = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_METADATA_MISSING", local.env_file), "") - REMOVE_MISSING_FILES = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_MISSING_FILES", local.env_file), "") - REMOVE_ORPHANS = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_ORPHANS", local.env_file), "") - REMOVE_SLOW = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_SLOW", local.env_file), "") - REMOVE_STALLED = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_STALLED", local.env_file), "") - REMOVE_UNMONITORED = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVE_UNMONITORED", local.env_file), "") - RUN_PERIODIC_RESCANS = try(provider::dotenv::get_by_key("DECLUTTARR_RUN_PERIODIC_RESCANS", local.env_file), "") - PERMITTED_ATTEMPTS = try(provider::dotenv::get_by_key("DECLUTTARR_PERMITTED_ATTEMPTS", local.env_file), "") - NO_STALLED_REMOVAL_QBIT_TAG = try(provider::dotenv::get_by_key("DECLUTTARR_REMOVAL_QBIT_TAG", local.env_file), "") - MIN_DOWNLOAD_SPEED = try(provider::dotenv::get_by_key("DECLUTTARR_MIN_DOWNLOAD_SPEED", local.env_file), "") - FAILED_IMPORT_MESSAGE_PATTERNS = try(provider::dotenv::get_by_key("DECLUTTARR_FAILED_IMPORT_MESSAGE_PATTERNS", local.env_file), "") - IGNORED_DOWNLOAD_CLIENTS = try(provider::dotenv::get_by_key("DECLUTTARR_IGNORED_DOWNLOAD_CLIENTS", local.env_file), "") - } -} - -# Sonarr -module "sonarr" { - source = "../../10-services-generic/docker-service" - container_name = local.sonarr_name - image = local.sonarr_image - volumes = [ - { host_path = "${var.volume_path}/sonarr", container_path = "/config", read_only = false }, - { host_path = var.data_path, container_path = "/data", read_only = false } - ] - networks = var.networks - monitoring = local.monitoring - ports = [{ internal = local.sonarr_port, external = local.sonarr_port, protocol = "tcp" }] -} - -# Radarr -module "radarr" { - source = "../../10-services-generic/docker-service" - container_name = local.radarr_name - image = local.radarr_image - volumes = [ - { host_path = "${var.volume_path}/radarr", container_path = "/config", read_only = false }, - { host_path = var.data_path, container_path = "/data", read_only = false } - ] - networks = var.networks - monitoring = local.monitoring - ports = [{ internal = local.radarr_port, external = local.radarr_port, protocol = "tcp" }] -} - -# Lidarr -module "lidarr" { - source = "../../10-services-generic/docker-service" - container_name = local.lidarr_name - image = local.lidarr_image - volumes = [ - { host_path = "${var.volume_path}/lidarr", container_path = "/config", read_only = false }, - { host_path = var.data_path, container_path = "/data", read_only = false } - ] - networks = var.networks - monitoring = local.monitoring - healthcheck = local.lidarr_healthcheck - ports = [{ internal = local.lidarr_port, external = local.lidarr_port, protocol = "tcp" }] -} - -# Bazarr -module "bazarr" { - source = "../../10-services-generic/docker-service" - container_name = local.bazarr_name - image = local.bazarr_image - volumes = [ - { host_path = "${var.volume_path}/bazarr/config", container_path = "/config", read_only = false }, - { host_path = var.data_path, container_path = "/data", read_only = false } - ] - networks = var.networks - monitoring = local.monitoring - healthcheck = local.bazarr_healthcheck - ports = [{ internal = local.bazarr_port, external = local.bazarr_port, protocol = "tcp" }] -} - -# Prowlarr -module "prowlarr" { - source = "../../10-services-generic/docker-service" - container_name = local.prowlarr_name - image = local.prowlarr_image - volumes = [ - { host_path = "${var.volume_path}/prowlarr", container_path = "/config", read_only = false } - ] - networks = var.networks - monitoring = local.monitoring - ports = [{ internal = local.prowlarr_port, external = local.prowlarr_port, protocol = "tcp" }] -} - -# Jellyseerr (published via reverse proxy) -module "jellyseerr" { - source = "../../10-services-generic/docker-service" - container_name = local.jellyseerr_name - image = local.jellyseerr_image - volumes = [{ host_path = "${var.volume_path}/jellyseerr", container_path = "/app/config", read_only = false }] - env_vars = local.jellyseerr_env - networks = concat(var.networks, var.proxy_networks) - monitoring = local.monitoring - healthcheck = local.jellyseerr_healthcheck - ports = [{ internal = local.jellyseerr_port, external = local.jellyseerr_port, protocol = "tcp" }] -} - -# Flaresolverr -module "flaresolverr" { - source = "../../10-services-generic/docker-service" - container_name = local.flaresolverr_name - image = local.flaresolverr_image - tag = "nodriver" - env_vars = local.flaresolverr_env - networks = var.networks - monitoring = local.monitoring - ports = [{ internal = local.flaresolverr_port, external = local.flaresolverr_port, protocol = "tcp" }] -} - -# Unpackerr -module "unpackerr" { - source = "../../10-services-generic/docker-service" - container_name = local.unpackerr_name - image = local.unpackerr_image - env_vars = local.unpackerr_env - volumes = [{ host_path = var.downloads_path, container_path = "/data/torrents", read_only = false }] - networks = var.networks - monitoring = local.monitoring -} - -# Cleanuparr -module "cleanuparr" { - source = "../../10-services-generic/docker-service" - container_name = local.cleanuparr_name - image = local.cleanuparr_image - env_vars = local.cleanuparr_env - volumes = [ - { host_path = "${var.volume_path}/cleanuparr/logs", container_path = "/var/logs", read_only = false }, - { host_path = "${var.volume_path}/cleanuparr/ignored.txt", container_path = "/usr/ignored.txt", read_only = false }, - { host_path = "${var.volume_path}/cleanuparr/blacklist.json", container_path = "/usr/blacklist.json", read_only = false } - ] - networks = var.networks - monitoring = local.monitoring -} - -module "decluttarr" { - source = "../../10-services-generic/docker-service" - container_name = local.decluttarr_name - image = local.decluttarr_image - env_vars = local.decluttarr_env - networks = var.networks - monitoring = local.monitoring -} - -output "service_definition" { - description = "Service definition for Jellyseerr (reverse proxy)" - value = { - name = local.jellyseerr_name - primary_port = local.jellyseerr_port - endpoint = "http://${local.jellyseerr_name}:${local.jellyseerr_port}" - subdomains = ["req"] - publish_via = "reverse_proxy" - proxied = true - } -} diff --git a/modules/20-services-apps/copyparty/README.md b/modules/20-services-apps/copyparty/README.md deleted file mode 100644 index a4eef40..0000000 --- a/modules/20-services-apps/copyparty/README.md +++ /dev/null @@ -1,85 +0,0 @@ -# Copyparty Module - -This module deploys [copyparty](https://github.com/9001/copyparty), a portable file server. - -## Overview - -The copyparty module: - -- Deploys one Docker container: `copyparty`. -- Mounts a volume for configuration and another for the files to be shared. -- Provides a service definition for integration with networking modules. - -## Usage - -```hcl -module "copyparty" { - source = "./modules/20-services-apps/copyparty" - fileshare_path = "/path/to/your/fileshare/top/folder" - config_path = "/path/to/copyparty/config" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | -| ---------------- | ----------------------------------------------------------- | -| `image_tag` | Tag of the copyparty image to use | -| `fileshare_path` | Host path for the top folder of the file share | -| `config_path` | Host path for copyparty configuration files | -| `networks` | List of additional networks to which copyparty should be attached | -| `puid` | User ID to run the container as | -| `pgid` | Group ID to run the container as | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "copyparty" - primary_port = 3923 - endpoint = "http://copyparty:3923" - subdomains = ["files"] - publish_via = "reverse_proxy" -} -``` - -## Data Persistence - -Copyparty uses two volumes: - -1. Configuration: `/cfg` in the container, mapped to `var.config_path` on the host. -2. File Share: `/w` in the container, mapped to `var.fileshare_path` on the host. - -## Integration with Networking Modules - -This service is configured to be exposed through the Caddy reverse proxy, set by `publish_via = "reverse_proxy"`. - -## Example Integration in Main Configuration - -```hcl -module "copyparty" { - source = "./modules/20-services-apps/copyparty" - fileshare_path = "/mnt/storage/files" - config_path = "${module.system_globals.volume_host}/copyparty/config" - networks = [module.services.homelab_docker_network_name] -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.copyparty.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/copyparty/main.tf b/modules/20-services-apps/copyparty/main.tf deleted file mode 100644 index ffb05ea..0000000 --- a/modules/20-services-apps/copyparty/main.tf +++ /dev/null @@ -1,89 +0,0 @@ -variable "image_tag" { - description = "The tag for the copyparty container image" - type = string - default = "latest" -} - -variable "fileshare_path" { - description = "Path to the top folder of the file share" - type = string -} - -variable "config_path" { - description = "Path to the configuration files for copyparty" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -variable "puid" { - description = "User ID to run the container as" - type = string - default = "1000" -} - -variable "pgid" { - description = "Group ID to run the container as" - type = string - default = "1000" -} - -locals { - container_name = "copyparty" - image = "copyparty/ac" - tag = var.image_tag - monitoring = true - internal_port = 3923 - user = "${var.puid}:${var.pgid}" - volumes = [ - { - host_path = var.config_path - container_path = "/cfg" - read_only = false - }, - { - host_path = var.fileshare_path - container_path = "/w" - read_only = false - } - ] - env_vars = { - LD_PRELOAD = "/usr/lib/libmimalloc-secure.so.2" - PYTHONUNBUFFERED = "1" - } -} - -module "copyparty" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.tag - user = local.user - volumes = local.volumes - env_vars = local.env_vars - networks = var.networks - monitoring = local.monitoring - destroy_grace_seconds = 15 - ports = [ - { - internal = local.internal_port - external = local.internal_port - protocol = "tcp" - } - ] -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = local.internal_port - endpoint = "http://${local.container_name}:${local.internal_port}" - subdomains = ["drive"] - publish_via = "tunnel" - } -} diff --git a/modules/20-services-apps/crawl4ai/.env.example b/modules/20-services-apps/crawl4ai/.env.example deleted file mode 100644 index c1c3c4c..0000000 --- a/modules/20-services-apps/crawl4ai/.env.example +++ /dev/null @@ -1,18 +0,0 @@ -# Crawl4AI Configuration -PORT=11235 - -# API Keys for LLM providers used by Crawl4AI -# OpenAI API key for GPT models -OPENAI_API_KEY= -# DeepSeek API key -DEEPSEEK_API_KEY= -# Anthropic API key for Claude models -ANTHROPIC_API_KEY= -# Groq API key -GROQ_API_KEY= -# Together API key -TOGETHER_API_KEY= -# Mistral API key -MISTRAL_API_KEY= -# Google Gemini API token -GEMINI_API_TOKEN= diff --git a/modules/20-services-apps/crawl4ai/README.md b/modules/20-services-apps/crawl4ai/README.md deleted file mode 100644 index 71e3812..0000000 --- a/modules/20-services-apps/crawl4ai/README.md +++ /dev/null @@ -1,91 +0,0 @@ -# Crawl4AI Module - -This module deploys [Crawl4AI](https://github.com/unclecode/crawl4ai), a web crawling and AI analysis tool, as a Docker container in the homelab environment. - -## Overview - -The Crawl4AI module: - -- Deploys the `unclecode/crawl4ai` Docker container -- Configures resource limits and reservations for memory -- Provides shared memory access for Chrome/Chromium performance -- Supports custom configuration through volume mounting -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "crawl4ai" { - source = "./modules/20-services-apps/crawl4ai" - volume_path = "/path/to/volumes" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| --------------------- | ------------------------------------------------- | -------------- | ----------- | -| `image_tag` | Tag of the Crawl4AI image to use | `string` | `"latest"` | -| `volume_path` | Host path for Crawl4AI data volumes | `string` | - | -| `networks` | List of networks to attach the container to | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "crawl4ai" - primary_port = 11235 - endpoint = "http://crawl4ai:11235" -} -``` - -## Environment Variables - -Crawl4AI requires API keys for various LLM providers. These are configured through a `.env` file in the module directory. You should create this file based on the provided `.env.example` template: - -- `OPENAI_API_KEY`: OpenAI API key -- `DEEPSEEK_API_KEY`: DeepSeek API key -- `ANTHROPIC_API_KEY`: Anthropic API key -- `GROQ_API_KEY`: Groq API key -- `TOGETHER_API_KEY`: Together API key -- `MISTRAL_API_KEY`: Mistral API key -- `GEMINI_API_TOKEN`: Gemini API token - -## Configuration - -Crawl4AI requires a custom configuration file. This is mounted from `${volume_path}/crawl4ai/config.yml` to `/app/config.yml` in the container. - -## Ports - -Crawl4AI exposes one port, which is mapped to host ports defined in the `.env` file: -1. Frontend (port 11235) - The main web interface for accessing games - -## Example Integration in Main Configuration - -```hcl -module "crawl4ai" { - source = "./modules/20-services-apps/crawl4ai" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] - memory_limit = 8192 # 8GB if you need more memory -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.crawl4ai.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/crawl4ai/main.tf b/modules/20-services-apps/crawl4ai/main.tf deleted file mode 100644 index d16ce66..0000000 --- a/modules/20-services-apps/crawl4ai/main.tf +++ /dev/null @@ -1,99 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -variable "image_tag" { - description = "Tag of the Crawl4AI image to use" - type = string - default = "latest" -} - -variable "volume_path" { - description = "Host path for Crawl4AI data volumes" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -locals { - container_name = "crawl4ai" - image = "unclecode/crawl4ai" - image_tag = var.image_tag - monitoring = true - service_port = provider::dotenv::get_by_key("PORT", local.env_file) - env_file = "${path.module}/.env" - - # Define volumes - default_volumes = [ - { - container_path = "/dev/shm" - host_path = "/dev/shm" - read_only = false - }, - { - container_path = "/app/config.yml" - host_path = "${var.volume_path}/config.yml" - read_only = false - } - ] - - # Define ports - ports = [ - { - internal = local.service_port - external = local.service_port - protocol = "tcp" - } - ] - - # Environment variables - env_vars = { - OPENAI_API_KEY = provider::dotenv::get_by_key("OPENAI_API_KEY", local.env_file) - DEEPSEEK_API_KEY = provider::dotenv::get_by_key("DEEPSEEK_API_KEY", local.env_file) - ANTHROPIC_API_KEY = provider::dotenv::get_by_key("ANTHROPIC_API_KEY", local.env_file) - GROQ_API_KEY = provider::dotenv::get_by_key("GROQ_API_KEY", local.env_file) - TOGETHER_API_KEY = provider::dotenv::get_by_key("TOGETHER_API_KEY", local.env_file) - MISTRAL_API_KEY = provider::dotenv::get_by_key("MISTRAL_API_KEY", local.env_file) - GEMINI_API_TOKEN = provider::dotenv::get_by_key("GEMINI_API_TOKEN", local.env_file) - } - - # Healthcheck configuration - healthcheck = { - test = ["CMD", "curl", "-f", "http://localhost:${local.service_port}/health"] - interval = "30s" - timeout = "10s" - retries = 3 - start_period = "40s" - } -} - -module "crawl4ai" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.image_tag - volumes = local.default_volumes - ports = local.ports - env_vars = local.env_vars - networks = var.networks - monitoring = local.monitoring - healthcheck = local.healthcheck - user = "appuser" -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = local.service_port - endpoint = "http://${local.container_name}:${local.service_port}" - } -} diff --git a/modules/20-services-apps/emulatorjs/.env.example b/modules/20-services-apps/emulatorjs/.env.example deleted file mode 100644 index 52884d3..0000000 --- a/modules/20-services-apps/emulatorjs/.env.example +++ /dev/null @@ -1,3 +0,0 @@ -EMULATORJS_FRONTEND_PORT=5823 -EMULATORJS_CONFIG_PORT=5824 -EMULATORJS_BACKEND_PORT=5825 diff --git a/modules/20-services-apps/emulatorjs/README.md b/modules/20-services-apps/emulatorjs/README.md deleted file mode 100644 index 1af12bc..0000000 --- a/modules/20-services-apps/emulatorjs/README.md +++ /dev/null @@ -1,99 +0,0 @@ -# EmulatorJS Module - -This module deploys [EmulatorJS](https://github.com/linuxserver/docker-emulatorjs), a self-hosted retro gaming emulation platform, as a Docker container in the homelab environment. - -## Overview - -The EmulatorJS module: - -- Deploys the `linuxserver/emulatorjs` Docker container -- Persists configuration and game data to volumes on the host -- Exposes multiple ports for frontend, configuration, and backend services -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "emulatorjs" { - source = "./modules/20-services-apps/emulatorjs" - volume_path = "/path/to/volumes/emulatorjs" -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | -------------------------------------------- | -------- | ---------- | -| `image_tag` | Tag of the EmulatorJS image to use | `string` | `"latest"` | -| `volume_path` | Host path for EmulatorJS data volumes | `string` | - | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "emulatorjs" - primary_port = - endpoint = "http://emulatorjs:" -} -``` - -Note that unlike other services, EmulatorJS doesn't specify subdomains or a publish method in its service definition. This may require manual configuration in your networking setup. - -## Ports - -EmulatorJS exposes three ports, which are mapped to host ports defined in the `.env` file: - -1. Frontend (port 80) - The main web interface for accessing games -2. Config (port 3000) - The configuration interface -3. Backend (port 4001) - Backend services - -## Environment Variables - -This module requires the following environment variables to be set in a `.env` file: - -- `EMULATORJS_FRONTEND_PORT`: Host port for the main web interface -- `EMULATORJS_CONFIG_PORT`: Host port for the configuration interface -- `EMULATORJS_BACKEND_PORT`: Host port for backend services - -## Data Persistence - -EmulatorJS stores its data in two volumes: - -1. Configuration: `/config` in the container, mapped to `${volume_path}/config` on the host -2. Game data: `/data` in the container, mapped to `${volume_path}/data` on the host - -## Example Integration in Main Configuration - -```hcl -module "emulatorjs" { - source = "./modules/20-services-apps/emulatorjs" - volume_path = module.system_globals.volume_host -} - -# If you want to expose EmulatorJS via your networking modules, -# you may need to manually configure the service definition: -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.emulatorjs.service_definition, - # Other service definitions - ] -} -``` - -## Additional Configuration - -After deployment, you can access the configuration interface at `http://your-server:` to: - -1. Upload ROM files to the `/data/roms` directory -2. Configure emulation settings -3. Manage game art and metadata diff --git a/modules/20-services-apps/emulatorjs/main.tf b/modules/20-services-apps/emulatorjs/main.tf deleted file mode 100644 index 71e564e..0000000 --- a/modules/20-services-apps/emulatorjs/main.tf +++ /dev/null @@ -1,78 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -variable "image_tag" { - description = "The tag for the EmulatorJS container image" - type = string - default = "latest" -} - -variable "volume_path" { - description = "Base directory for volumes" - type = string -} - -locals { - container_name = "emulatorjs" - image = "linuxserver/emulatorjs" - image_tag = var.image_tag != "" ? var.image_tag : "latest" - monitoring = true - env_file = "${path.module}/.env" - frontend_port = provider::dotenv::get_by_key("EMULATORJS_FRONTEND_PORT", local.env_file) - config_port = provider::dotenv::get_by_key("EMULATORJS_CONFIG_PORT", local.env_file) - backend_port = provider::dotenv::get_by_key("EMULATORJS_BACKEND_PORT", local.env_file) - ports = [ - { - internal = 3000 - external = local.config_port - protocol = "tcp" - }, - { - internal = 80 - external = local.frontend_port - protocol = "tcp" - }, - { - internal = 4001 - external = local.backend_port - protocol = "tcp" - } - ] - volumes = [ - { - host_path = "${var.volume_path}/config" - container_path = "/config" - read_only = false - }, - { - host_path = "${var.volume_path}/data" - container_path = "/data" - read_only = false - } - ] -} - -module "emulatorjs" { - source = "../../10-services-generic/docker-service" - - container_name = local.container_name - image = local.image - tag = local.image_tag - volumes = local.volumes - ports = local.ports - monitoring = local.monitoring -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = module.emulatorjs.container_name - primary_port = local.frontend_port - endpoint = "http://${module.emulatorjs.container_name}:${local.frontend_port}" - } -} diff --git a/modules/20-services-apps/gluetun/.env.example b/modules/20-services-apps/gluetun/.env.example deleted file mode 100644 index 31b4fab..0000000 --- a/modules/20-services-apps/gluetun/.env.example +++ /dev/null @@ -1,33 +0,0 @@ -# Gluetun VPN (.env example) -# Copy to modules/20-services-apps/gluetun/.env and fill in values - -# Provider and VPN type -VPN_SERVICE_PROVIDER=mullvad -VPN_TYPE=wireguard - -# Wireguard credentials (required) -# Generate from Mullvad account: private key and tunnel IP address -WIREGUARD_PRIVATE_KEY= -# Example: 10.64.0.2/32 -WIREGUARD_ADDRESSES= - -# Server selection (one of the following is recommended) -#SERVER_CITIES="Los Angeles" -#SERVER_COUNTRIES="United States" - -# Exact server pinning (optional; supports comma-separated list) -# For Jakarta example: -#SERVER_HOSTNAMES=id-jpu-wg-001 -#SERVER_HOSTNAME=id-jpu-wg-001 - -# Updater period (optional) -#UPDATER_PERIOD=24h - -# Firewall rules (optional) -# Allow outbound traffic to RFC1918 subnets so LAN services can be reached -# Example: 10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 -#FIREWALL_OUTBOUND_SUBNETS= - -# If you need to accept inbound connections through Gluetun (e.g., expose qBittorrent UI), -# publish the port in Terraform and optionally set FIREWALL_INPUT_PORTS as well -#FIREWALL_INPUT_PORTS=8080 diff --git a/modules/20-services-apps/gluetun/README.md b/modules/20-services-apps/gluetun/README.md deleted file mode 100644 index 9e52f64..0000000 --- a/modules/20-services-apps/gluetun/README.md +++ /dev/null @@ -1,65 +0,0 @@ -# Gluetun (Mullvad Wireguard) - -This module runs Gluetun to provide a VPN network stack for other containers. -You can route qBittorrent through Gluetun by setting its `network_mode` to `container:gluetun` using the provided toggle in the qBittorrent module. - -- Image: `qmcgaw/gluetun:v3.39.0` -- Requires: NET_ADMIN capability and `/dev/net/tun` device -- Default: No ports exposed on host. Publish only if you need host access. -- Attach Gluetun to the same Docker network as services that should reach apps running through it (e.g., `media-network`). - -## Usage - -Example in `services/main.tf`: - -```hcl -module "gluetun" { - source = "${local.module_dir}/20-services-apps/gluetun" - volume_path = "${local.volume_host}/gluetun" - networks = [module.media_docker_network.name] - # Optionally expose qBittorrent's Web UI to the host via Gluetun: - # ports = [{ internal = 8080, external = 8080, protocol = "tcp" }] -} - -module "qbittorrent" { - source = "${local.module_dir}/20-services-apps/qbittorrent" - volume_path = "${local.volume_host}/qbittorrent" - downloads_path = "${local.data_host}/torrents" - networks = [module.media_docker_network.name] - connect_via_gluetun = true - gluetun_container_name = "gluetun" -} - -module "arr" { - source = "${local.module_dir}/20-services-apps/arr" - volume_path = "${local.volume_host}/arr" - data_path = local.data_host - downloads_path = "${local.data_host}/torrents" - networks = [module.media_docker_network.name] - proxy_networks = [module.homelab_docker_network.name] - qbittorrent_host = "gluetun" # arr containers will reach qBt at http://gluetun:8080 -} -``` - -## Environment variables - -Place a `.env` file in this module directory (`modules/20-services-apps/gluetun/.env`). See `.env.example` for all options. Key variables: - -- VPN_SERVICE_PROVIDER=mullvad -- VPN_TYPE=wireguard -- WIREGUARD_PRIVATE_KEY=... (required) -- WIREGUARD_ADDRESSES=10.64.0.2/32 (example) -- SERVER_CITIES=... or SERVER_COUNTRIES=... -- SERVER_HOSTNAMES=id-jpu-wg-001 (optional exact server pin; supports comma-separated list) -- UPDATER_PERIOD=24h (optional) -- FIREWALL_OUTBOUND_SUBNETS=10.0.0.0/8,192.168.0.0/16 (optional; allow containers to reach LAN subnets) -- Optional: `FIREWALL_INPUT_PORTS=8080` if you need other containers/LAN to initiate connections to services through Gluetun. - -Notes: -- When qBittorrent shares Gluetun's network, other containers should use `http://gluetun:8080`. -- To access qBittorrent UI from the host, publish `8080/tcp` on Gluetun via this module's `ports` input or set `FIREWALL_INPUT_PORTS` accordingly. -- Do not publish ports on qBittorrent when using Gluetun network mode; publish on Gluetun instead. - -Pinning a specific server: -- Set `SERVER_HOSTNAMES=id-jpu-wg-001` to pin to Mullvad Jakarta `id-jpu-wg-001`. -- The module also accepts `SERVER_HOSTNAME` for compatibility (falls back to it if `SERVER_HOSTNAMES` is not set). diff --git a/modules/20-services-apps/gluetun/main.tf b/modules/20-services-apps/gluetun/main.tf deleted file mode 100644 index 4cce1fd..0000000 --- a/modules/20-services-apps/gluetun/main.tf +++ /dev/null @@ -1,88 +0,0 @@ -terraform { - required_providers { - dotenv = { source = "germanbrew/dotenv" } - } -} - -variable "volume_path" { - description = "Base directory for Gluetun state/config mounted at /gluetun" - type = string -} - -variable "networks" { - description = "Networks to attach Gluetun to" - type = list(string) - default = [] -} - -variable "ports" { - description = "Ports to publish on the Gluetun container (used to reach services connected via network_mode: container:gluetun)" - type = list(object({ - internal = number - external = number - protocol = string - })) - // Default to no published ports. Publish only if you need host access. - default = [] -} - -variable "image_tag" { - description = "Gluetun image tag" - type = string - default = "v3.39.0" -} - -locals { - env_file = "${path.module}/.env" - container_name = "gluetun" - image = "qmcgaw/gluetun" - tag = var.image_tag - monitoring = true - - // Gluetun environment - env_vars = { - VPN_SERVICE_PROVIDER = try(provider::dotenv::get_by_key("VPN_SERVICE_PROVIDER", local.env_file), "mullvad") - VPN_TYPE = try(provider::dotenv::get_by_key("VPN_TYPE", local.env_file), "wireguard") - WIREGUARD_PRIVATE_KEY = provider::dotenv::get_by_key("WIREGUARD_PRIVATE_KEY", local.env_file) - WIREGUARD_ADDRESSES = provider::dotenv::get_by_key("WIREGUARD_ADDRESSES", local.env_file) - SERVER_CITIES = try(provider::dotenv::get_by_key("SERVER_CITIES", local.env_file), "") - SERVER_COUNTRIES = try(provider::dotenv::get_by_key("SERVER_COUNTRIES", local.env_file), "") - SERVER_HOSTNAMES = try( - provider::dotenv::get_by_key("SERVER_HOSTNAMES", local.env_file), - try(provider::dotenv::get_by_key("SERVER_HOSTNAME", local.env_file), "") - ) - UPDATER_PERIOD = try(provider::dotenv::get_by_key("UPDATER_PERIOD", local.env_file), "") - FIREWALL_OUTBOUND_SUBNETS = try(provider::dotenv::get_by_key("FIREWALL_OUTBOUND_SUBNETS", local.env_file), "") - } - - volumes = [ - { - host_path = var.volume_path, - container_path = "/gluetun", - read_only = false - } - ] -} - -module "gluetun" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.tag - env_vars = local.env_vars - volumes = local.volumes - networks = var.networks - monitoring = local.monitoring - - // Grant minimal privileges required by Gluetun - capabilities_add = ["NET_ADMIN"] - devices = [ - { - host_path = "/dev/net/tun" - container_path = "/dev/net/tun" - permissions = "rwm" - } - ] - - ports = var.ports -} diff --git a/modules/20-services-apps/n8n/.env.example b/modules/20-services-apps/n8n/.env.example deleted file mode 100644 index 09aa183..0000000 --- a/modules/20-services-apps/n8n/.env.example +++ /dev/null @@ -1,14 +0,0 @@ -POSTGRES_USER=admin -POSTGRES_PASSWORD= -POSTGRES_DB=n8n -POSTGRES_NON_ROOT_USER= -POSTGRES_NON_ROOT_PASSWORD= -N8N_HOST=localhost -N8N_PORT=5678 -N8N_PROTOCOL=http -WEBHOOK_URL=https://n8n.yourdomain.com/ -NODE_FUNCTION_ALLOW_EXTERNAL=* - -# MCP -N8N_MCP_AUTH_TOKEN= -N8N_API_KEY= diff --git a/modules/20-services-apps/n8n/README.md b/modules/20-services-apps/n8n/README.md deleted file mode 100644 index 1842c1d..0000000 --- a/modules/20-services-apps/n8n/README.md +++ /dev/null @@ -1,159 +0,0 @@ -# n8n Module - -This module deploys [n8n](https://n8n.io/), a workflow automation tool, along with its dependencies and the [n8n-mcp](https://github.com/czlonkowski/n8n-mcp) community node manager, as Docker containers in the homelab environment. - -## Overview - -The n8n module: - -- Deploys four Docker containers: - - `n8n`: The main workflow automation server - - `n8n-postgres`: A PostgreSQL database backend - - `n8n-redis`: A Redis instance for queuing - - `n8n-mcp`: A community node management tool for n8n -- Creates a dedicated Docker network (`n8n-network`) for container communication -- Persists data to volumes on the host -- Provides service definitions for integration with networking modules - -## Usage - -```hcl -module "n8n" { - source = "./modules/20-services-apps/n8n" - volume_path = "/path/to/volumes/n8n" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | ---------------------------------------------------------- | -------------- | ---------- | -| `image_tag` | Tag of the n8n image to use | `string` | `"latest"` | -| `volume_path` | Host path for n8n, Postgres, Redis, and n8n-mcp data volumes | `string` | - | -| `networks` | List of additional networks to which n8n should be attached | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| ---------------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for the n8n container | -| `n8n_mcp_service_definition` | Service definition for the n8n-mcp container | - -## Service Definitions - -This module outputs two service definitions that are used by the networking modules to expose the services. - -### n8n - -```hcl -{ - name = "n8n" - primary_port = 5678 - endpoint = "http://n8n:5678" - subdomains = ["n8n"] - publish_via = "tunnel" -} -``` - -### n8n-mcp - -```hcl -{ - name = "n8n-mcp" - primary_port = 3000 - endpoint = "http://n8n-mcp:3000" - subdomains = ["n8n-mcp"] - publish_via = "tunnel" -} -``` - -## Environment Variables - -The services require several environment variables to function properly. These are stored in a `.env` file in the module directory and read using the `dotenv` Terraform provider: - -- **Database configuration (`n8n-postgres`)**: - - `POSTGRES_USER`: Root PostgreSQL user - - `POSTGRES_PASSWORD`: Root PostgreSQL password - - `POSTGRES_DB`: Database name for n8n - - `POSTGRES_NON_ROOT_USER`: Non-root user for n8n to connect with - - `POSTGRES_NON_ROOT_PASSWORD`: Password for the non-root user - -- **n8n configuration (`n8n`)**: - - `N8N_HOST`: Host for n8n to use - - `N8N_PORT`: Port for n8n to use - - `N8N_PROTOCOL`: Protocol for n8n (http or https) - - `WEBHOOK_URL`: URL for webhooks - - `NODE_FUNCTION_ALLOW_EXTERNAL`: Whether to allow external function calls - -- **n8n-mcp configuration (`n8n-mcp`)**: - - `N8N_MCP_AUTH_TOKEN`: Authentication token for n8n-mcp. - - `N8N_API_KEY`: n8n API key for n8n-mcp to interact with the n8n instance. - -## Data Persistence - -The services store data in several volumes: - -1. **n8n application data**: `/home/node/.n8n` in the container, mapped to `${volume_path}/n8n_storage/_data` on the host -2. **PostgreSQL data**: `/var/lib/postgresql/data` in the container, mapped to `${volume_path}/db_storage/_data` on the host -3. **Redis data**: `/data` in the container, mapped to `${volume_path}/redis_data` on the host -4. **n8n-mcp data**: `/app/data` in the container, mapped to `${volume_path}/n8n_mcp_storage/_data` on the host - -Additionally, an initialization script is mounted to the PostgreSQL container: -- `/docker-entrypoint-initdb.d/init-data.sh` in the container, from `${volume_path}/init-data.sh` on the host - -## Networking - -The module creates a dedicated Docker network named `n8n-network` for communication between all containers. The `n8n` and `n8n-mcp` containers are also attached to any additional networks specified in the `networks` variable, allowing them to communicate with other services in the homelab. - -## Integration with Networking Modules - -The services are configured to be exposed through a Cloudflare tunnel for secure remote access, set by `publish_via = "tunnel"`. - -## Example Integration in Main Configuration - -```hcl -module "n8n" { - source = "./modules/20-services-apps/n8n" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] -} - -# The service definitions are automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.n8n.service_definition, - module.n8n.n8n_mcp_service_definition, - # Other service definitions - ] -} -``` - -## Using n8n-mcp with your IDE - -To connect your IDE to the `n8n-mcp` server, you can use the following configuration in your IDE's settings. This allows the IDE to use the n8n instance as a tool provider. - -Make sure to replace `` with your actual domain and populate the `AUTH_TOKEN` with the value of `N8N_MCP_AUTH_TOKEN` from your `.env` file. - -```json -{ - "mcpServers": { - "n8n-mcp": { - "command": "npx", - "args": [ - "mcp-remote", - "https://n8n-mcp./mcp", - "--header", - "Authorization: Bearer ${AUTH_TOKEN}", - "--transport", - "http-only" - ], - "env": { - "AUTH_TOKEN": "..." - } - } - } -} -``` diff --git a/modules/20-services-apps/n8n/init-data.sh b/modules/20-services-apps/n8n/init-data.sh deleted file mode 100644 index b0e85d0..0000000 --- a/modules/20-services-apps/n8n/init-data.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash -set -e; - - -if [ -n "${POSTGRES_NON_ROOT_USER:-}" ] && [ -n "${POSTGRES_NON_ROOT_PASSWORD:-}" ]; then - psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL - CREATE USER ${POSTGRES_NON_ROOT_USER} WITH PASSWORD '${POSTGRES_NON_ROOT_PASSWORD}'; - GRANT ALL PRIVILEGES ON DATABASE ${POSTGRES_DB} TO ${POSTGRES_NON_ROOT_USER}; - GRANT CREATE ON SCHEMA public TO ${POSTGRES_NON_ROOT_USER}; - EOSQL -else - echo "SETUP INFO: No Environment variables given!" -fi \ No newline at end of file diff --git a/modules/20-services-apps/n8n/main.tf b/modules/20-services-apps/n8n/main.tf deleted file mode 100644 index 361c0e9..0000000 --- a/modules/20-services-apps/n8n/main.tf +++ /dev/null @@ -1,246 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -variable "image_tag" { - description = "The tag for the n8n container image" - type = string - default = "latest" -} - -variable "volume_path" { - description = "Base directory for volumes" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -module "system_globals" { - source = "../../00-globals/system" -} - -locals { - container_name = "n8n" - database_name = "n8n-postgres" - redis_name = "n8n-redis" - n8n_image = "docker.n8n.io/n8nio/n8n" - database_image = "postgres" - redis_image = "redis" - n8n_tag = var.image_tag != "" ? var.image_tag : "latest" - database_tag = "16" - redis_tag = "7-alpine" - monitoring = true - env_file = "${path.module}/.env" - n8n_internal_port = 5678 - - # Define volumes - n8n_volumes = [ - { - host_path = "${var.volume_path}/n8n_storage/_data" - container_path = "/home/node/.n8n" - read_only = false - } - ] - - database_volumes = [ - { - host_path = "${var.volume_path}/db_storage/_data" - container_path = "/var/lib/postgresql/data" - read_only = false - }, - { - host_path = "${var.volume_path}/init-data.sh" - container_path = "/docker-entrypoint-initdb.d/init-data.sh" - read_only = false - } - ] - - # Environment variables for the database - database_env_vars = { - POSTGRES_USER = provider::dotenv::get_by_key("POSTGRES_USER", local.env_file) - POSTGRES_PASSWORD = provider::dotenv::get_by_key("POSTGRES_PASSWORD", local.env_file) - POSTGRES_DB = provider::dotenv::get_by_key("POSTGRES_DB", local.env_file) - POSTGRES_NON_ROOT_USER = provider::dotenv::get_by_key("POSTGRES_NON_ROOT_USER", local.env_file) - POSTGRES_NON_ROOT_PASSWORD = provider::dotenv::get_by_key("POSTGRES_NON_ROOT_PASSWORD", local.env_file) - } - - # Environment variables for n8n - n8n_env_vars = { - DB_TYPE = "postgresdb" - DB_POSTGRESDB_HOST = local.database_name - DB_POSTGRESDB_PORT = 5432 - DB_POSTGRESDB_DATABASE = provider::dotenv::get_by_key("POSTGRES_DB", local.env_file) - DB_POSTGRESDB_USER = provider::dotenv::get_by_key("POSTGRES_NON_ROOT_USER", local.env_file) - DB_POSTGRESDB_PASSWORD = provider::dotenv::get_by_key("POSTGRES_NON_ROOT_PASSWORD", local.env_file) - N8N_HOST = provider::dotenv::get_by_key("N8N_HOST", local.env_file) - N8N_PORT = provider::dotenv::get_by_key("N8N_PORT", local.env_file) - N8N_PROTOCOL = provider::dotenv::get_by_key("N8N_PROTOCOL", local.env_file) - WEBHOOK_URL = provider::dotenv::get_by_key("WEBHOOK_URL", local.env_file) - NODE_FUNCTION_ALLOW_EXTERNAL = provider::dotenv::get_by_key("NODE_FUNCTION_ALLOW_EXTERNAL", local.env_file) - GENERIC_TIMEZONE = module.system_globals.timezone - QUEUE_BULL_REDIS_HOST = local.redis_name - QUEUE_BULL_REDIS_PORT = 6379 - QUEUE_BULL_REDIS_USERNAME = "redis" - QUEUE_BULL_REDIS_PASSWORD = "redis" - } - - # Healthcheck configuration for the database - database_healthcheck = { - test = ["CMD-SHELL", "pg_isready -h localhost -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] - interval = "5s" - timeout = "5s" - retries = 10 - start_period = "10s" - } - - # Healthcheck configuration for Redis - redis_healthcheck = { - test = ["CMD-SHELL", "redis-cli ping"] - interval = "5s" - timeout = "5s" - retries = 10 - start_period = "10s" - } - - # Define Redis volume - redis_volumes = [ - { - host_path = "${var.volume_path}/redis_data" - container_path = "/data" - read_only = false - } - ] - - n8n_mcp_container_name = "n8n-mcp" - n8n_mcp_image = "ghcr.io/czlonkowski/n8n-mcp" - n8n_mcp_tag = "latest" - n8n_mcp_internal_port = 3000 - - n8n_mcp_volumes = [ - { - host_path = "${var.volume_path}/n8n_mcp_storage/_data" - container_path = "/app/data" - read_only = false - } - ] - - n8n_mcp_env_vars = { - MCP_MODE = "http" - USE_FIXED_HTTP = "true" - AUTH_TOKEN = provider::dotenv::get_by_key("N8N_MCP_AUTH_TOKEN", local.env_file) - N8N_API_URL = "http://${local.container_name}:${local.n8n_internal_port}" - N8N_API_KEY = provider::dotenv::get_by_key("N8N_API_KEY", local.env_file) - NODE_ENV = "production" - LOG_LEVEL = "info" - PORT = local.n8n_mcp_internal_port - NODE_DB_PATH = "/app/data/nodes.db" - REBUILD_ON_START = "false" - GENERIC_TIMEZONE = module.system_globals.timezone - } - - n8n_mcp_healthcheck = { - test = ["CMD", "curl", "-f", "http://127.0.0.1:${local.n8n_mcp_internal_port}/health"] - interval = "30s" - timeout = "10s" - retries = 3 - start_period = "40s" - } -} - -module "n8n_network" { - source = "../../01-networking/docker-network" - name = "n8n-network" - driver = "bridge" - subnet = "172.24.0.0/16" -} - -# Create the PostgreSQL container -module "postgres" { - source = "../../10-services-generic/docker-service" - container_name = local.database_name - image = local.database_image - tag = local.database_tag - volumes = local.database_volumes - user = "1000:1000" - env_vars = local.database_env_vars - networks = [module.n8n_network.name] - monitoring = local.monitoring - healthcheck = local.database_healthcheck -} - -# Create the Redis container -module "redis" { - source = "../../10-services-generic/docker-service" - container_name = local.redis_name - image = local.redis_image - tag = local.redis_tag - volumes = local.redis_volumes - user = "1000:1000" - env_vars = { - REDIS_USERNAME = "redis" - REDIS_PASSWORD = "redis" - } - networks = [module.n8n_network.name] - monitoring = local.monitoring - command = ["redis-server", "--requirepass", "redis", "--user", "redis", "on", ">redis", "~*", "+@all"] - healthcheck = local.redis_healthcheck -} - -# Create the n8n container -module "n8n" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.n8n_image - tag = local.n8n_tag - volumes = local.n8n_volumes - user = "1000:1000" - env_vars = local.n8n_env_vars - networks = concat([module.n8n_network.name], var.networks) - monitoring = local.monitoring - depends_on = [module.postgres, module.redis] -} - -# Create the n8n-mcp container -module "n8n_mcp" { - source = "../../10-services-generic/docker-service" - container_name = local.n8n_mcp_container_name - image = local.n8n_mcp_image - tag = local.n8n_mcp_tag - volumes = local.n8n_mcp_volumes - user = "1000:1000" - env_vars = local.n8n_mcp_env_vars - networks = concat([module.n8n_network.name], var.networks) - monitoring = local.monitoring - healthcheck = local.n8n_mcp_healthcheck - depends_on = [module.n8n] -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = local.n8n_internal_port - endpoint = "http://${local.container_name}:${local.n8n_internal_port}" - subdomains = ["n8n"] - publish_via = "tunnel" - } -} - -output "n8n_mcp_service_definition" { - description = "General service definition with optional ingress configuration for n8n-mcp" - value = { - name = local.n8n_mcp_container_name - primary_port = local.n8n_mcp_internal_port - endpoint = "http://${local.n8n_mcp_container_name}:${local.n8n_mcp_internal_port}" - subdomains = ["n8n-mcp"] - publish_via = "tunnel" - } -} diff --git a/modules/20-services-apps/nocodb/.env.example b/modules/20-services-apps/nocodb/.env.example deleted file mode 100644 index a9df4cf..0000000 --- a/modules/20-services-apps/nocodb/.env.example +++ /dev/null @@ -1,4 +0,0 @@ -# Database Configuration -DB_USERNAME=postgres -DB_PASSWORD=change_this_password -DB_DATABASE=root_db \ No newline at end of file diff --git a/modules/20-services-apps/nocodb/README.md b/modules/20-services-apps/nocodb/README.md deleted file mode 100644 index 8760ebf..0000000 --- a/modules/20-services-apps/nocodb/README.md +++ /dev/null @@ -1,106 +0,0 @@ -# NocoDB Module - -This module deploys [NocoDB](https://www.nocodb.com/), an open-source no-code database platform that transforms PostgreSQL into a smart spreadsheet interface, as Docker containers in the homelab environment. - -## Overview - -The NocoDB module: - -- Deploys two Docker containers: - - `nocodb`: The main NocoDB application server - - `nocodb-postgres`: A PostgreSQL database backend -- Creates a dedicated Docker network (`nocodb-network`) for container communication -- Persists data to volumes on the host -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "nocodb" { - source = "./modules/20-services-apps/nocodb" - volume_path = "/path/to/volumes" - networks = ["homelab-network"] - postgres_user = "postgres" - postgres_password = "your_secure_password" - postgres_db = "root_db" -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------------- | -------------------------------------------------------------- | -------------- | ---------------------- | -| `image_tag` | Tag of the NocoDB image to use | `string` | `"latest"` | -| `postgres_image_tag`| Tag of the PostgreSQL image to use | `string` | `"16.6"` | -| `volume_path` | Host path for NocoDB and database data volumes | `string` | - | -| `networks` | List of networks to which NocoDB should be attached | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "nocodb" - primary_port = 8080 - endpoint = "http://nocodb:8080" - subdomains = ["nocodb"] - publish_via = "tunnel" # Only publish through Cloudflare tunnel -} -``` - -## Environment Variables - -NocoDB requires several environment variables to function properly. These are stored in a `.env` file in the module directory and read using the `dotenv` Terraform provider: - -- Database configuration: - - - `DB_USERNAME`: PostgreSQL user - - `DB_PASSWORD`: PostgreSQL password - - `DB_DATABASE`: Database name (defaults to "root_db") - -## Data Persistence - -NocoDB stores its data in two main volumes: - -1. NocoDB application data: `/usr/app/data` in the container, mapped to `${volume_path}/nocodb/data` on the host -2. PostgreSQL data: `/var/lib/postgresql/data` in the container, mapped to `${volume_path}/nocodb/postgres/data` on the host - -## Networking - -The module creates a dedicated Docker network named `nocodb-network` for communication between the NocoDB components. The NocoDB server container is also attached to any additional networks specified in the `networks` variable, allowing it to communicate with other services in the homelab. - -## Dependencies - -The NocoDB container depends on PostgreSQL, which includes a healthcheck to ensure it's ready before NocoDB starts. - -## Integration with Networking Modules - -This service is configured to be exposed through a Cloudflare tunnel for secure remote access, set by `publish_via = "tunnel"`. - -## Example Integration in Main Configuration - -```hcl -module "nocodb" { - source = "./modules/20-services-apps/nocodb" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] - postgres_password = "your_secure_password" -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.nocodb.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/nocodb/main.tf b/modules/20-services-apps/nocodb/main.tf deleted file mode 100644 index 6ad0501..0000000 --- a/modules/20-services-apps/nocodb/main.tf +++ /dev/null @@ -1,129 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -variable "image_tag" { - description = "Tag of the NocoDB image to use" - type = string - default = "latest" -} - -variable "postgres_image_tag" { - description = "Tag of the Postgres image to use" - type = string - default = "16.6" -} - -variable "volume_path" { - description = "Host path for NocoDB data volumes" - type = string -} - -variable "networks" { - description = "List of networks to which the containers should be attached" - type = list(string) - default = [] -} - -locals { - container_name = "nocodb" - postgres_name = "nocodb-postgres" - nocodb_image = "nocodb/nocodb" - postgres_image = "postgres" - nocodb_tag = var.image_tag - postgres_tag = var.postgres_image_tag - monitoring = true - nocodb_internal_port = 8080 - env_file = "${path.module}/.env" - postgres_user = provider::dotenv::get_by_key("DB_USERNAME", local.env_file) - postgres_password = provider::dotenv::get_by_key("DB_PASSWORD", local.env_file) - postgres_db = provider::dotenv::get_by_key("DB_DATABASE", local.env_file) - - # Define volumes - nocodb_volumes = [ - { - host_path = "${var.volume_path}/data" - container_path = "/usr/app/data" - read_only = false - } - ] - - postgres_volumes = [ - { - host_path = "${var.volume_path}/postgres/data" - container_path = "/var/lib/postgresql/data" - read_only = false - } - ] - - # Environment variables for postgres - postgres_env_vars = { - POSTGRES_USER = local.postgres_user - POSTGRES_PASSWORD = local.postgres_password - POSTGRES_DB = local.postgres_db - POSTGRES_INITDB_ARGS = "--data-checksums" - POSTGRES_HOST_AUTH_METHOD = "trust" - } - - # Environment variables for NocoDB - nocodb_env_vars = { - NC_DB = "pg://${local.postgres_name}:5432?u=${local.postgres_user}&p=${local.postgres_password}&d=${local.postgres_db}" - } - - # Healthcheck configuration for Postgres - postgres_healthcheck = { - test = ["CMD", "pg_isready", "-U", local.postgres_user, "-d", local.postgres_db] - interval = "10s" - timeout = "2s" - retries = 10 - start_period = "5s" - } -} - -module "nocodb_network" { - source = "../../01-networking/docker-network" - name = "nocodb-network" - subnet = "11.101.0.0/16" - driver = "bridge" -} - -# Create the PostgreSQL container -module "postgres" { - source = "../../10-services-generic/docker-service" - container_name = local.postgres_name - image = local.postgres_image - tag = local.postgres_tag - volumes = local.postgres_volumes - env_vars = local.postgres_env_vars - networks = [module.nocodb_network.name] - monitoring = local.monitoring - healthcheck = local.postgres_healthcheck -} - -# Create the NocoDB container -module "nocodb" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.nocodb_image - tag = local.nocodb_tag - volumes = local.nocodb_volumes - env_vars = local.nocodb_env_vars - networks = concat([module.nocodb_network.name], var.networks) - monitoring = local.monitoring - depends_on = [module.postgres] -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = local.nocodb_internal_port - endpoint = "http://${local.container_name}:${local.nocodb_internal_port}" - subdomains = ["db"] - publish_via = "tunnel" - } -} diff --git a/modules/20-services-apps/ntfy/README.md b/modules/20-services-apps/ntfy/README.md deleted file mode 100644 index 8bc3d66..0000000 --- a/modules/20-services-apps/ntfy/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# NTFY Module - -This module deploys [NTFY](https://ntfy.sh/), a simple HTTP-based pub-sub notification service, as a Docker container in the homelab environment. - -## Overview - -The NTFY module: - -- Deploys the `binwiederhier/ntfy` Docker container -- Persists configuration and cache data to volumes on the host -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "ntfy" { - source = "./modules/20-services-apps/ntfy" - volume_path = "/path/to/volumes/ntfy" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | ---------------------------------------------------------- | -------------- | ---------- | -| `image_tag` | Tag of the NTFY image to use | `string` | `"latest"` | -| `volume_path` | Host path for NTFY data volumes | `string` | - | -| `networks` | List of networks to which the container should be attached | `list(string)` | - | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "ntfy" - primary_port = 80 - endpoint = "http://ntfy:80" - subdomains = ["ntfy"] - publish_via = "reverse_proxy" # Expose via Caddy reverse proxy - proxied = true # Proxy through Cloudflare -} -``` - -## Data Persistence - -NTFY stores its data in two volumes: - -1. Configuration: `/etc/ntfy` in the container, mapped to `${volume_path}/app` on the host -2. Cache data: `/var/cache/ntfy` in the container, mapped to `${volume_path}/cache` on the host - -## Integration with Networking Modules - -This service is configured to be exposed through the Caddy reverse proxy, set by `publish_via = "reverse_proxy"`. The `proxied = true` setting ensures that the DNS record is proxied through Cloudflare. - -## Example Integration in Main Configuration - -```hcl -module "ntfy" { - source = "./modules/20-services-apps/ntfy" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.ntfy.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/ntfy/main.tf b/modules/20-services-apps/ntfy/main.tf deleted file mode 100644 index f73ca0f..0000000 --- a/modules/20-services-apps/ntfy/main.tf +++ /dev/null @@ -1,58 +0,0 @@ -variable "image_tag" { - description = "Tag of the ntfy image to use" - type = string - default = "latest" -} - -variable "volume_path" { - description = "Host path for ntfy data volume" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) -} - -locals { - container_name = "ntfy" - image = "binwiederhier/ntfy" - image_tag = var.image_tag != "" ? var.image_tag : "latest" - monitoring = true - exposed_port = 80 - subdomains = ["ntfy"] - default_volumes = [ - { - container_path = "/etc/ntfy" - host_path = "${var.volume_path}/app" - read_only = false - }, - { - container_path = "/var/cache/ntfy" - host_path = "${var.volume_path}/cache" - read_only = false - } - ] -} - -module "ntfy" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.image_tag - volumes = local.default_volumes - networks = var.networks - monitoring = local.monitoring -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = local.exposed_port - endpoint = "http://${local.container_name}:${local.exposed_port}" - subdomains = local.subdomains - publish_via = "reverse_proxy" - proxied = true - } -} diff --git a/modules/20-services-apps/pterodactyl/README.md b/modules/20-services-apps/pterodactyl/README.md deleted file mode 100644 index 802b1e7..0000000 --- a/modules/20-services-apps/pterodactyl/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# Pterodactyl Module - -This module is a parent module for deploying the [Pterodactyl](https://pterodactyl.io/) game server management system, which consists of multiple components: - -1. **Panel** - The web-based administration interface and API server -2. **Wings** - The game server agent that controls individual game servers - -## Overview - -The Pterodactyl module consists of two submodules: - -- `panel` - Deploys the Pterodactyl control panel with its database and cache servers -- `wings` - Deploys the Pterodactyl Wings agent for running game servers - -For a complete installation, both components should be deployed. - -## Architecture - -Pterodactyl is designed with a client-server architecture: - -- **Panel (Server)**: The central management interface where administrators create servers, manage users, and configure settings. -- **Wings (Agent)**: Installed on each machine that will run game servers, communicates with the Panel via API. - -In a homelab environment, you might deploy both components on the same machine or separate them for better resource allocation. - -## Usage - -### Deploying Both Components - -```hcl -module "pterodactyl_panel" { - source = "./modules/20-services-apps/pterodactyl/panel" - volume_path = "${var.volume_host}/pterodactyl/panel" - networks = [module.services.homelab_docker_network_name] -} - -module "pterodactyl_wings" { - source = "./modules/20-services-apps/pterodactyl/wings" - volume_path = "${var.volume_host}/pterodactyl/wings" - networks = [module.services.homelab_docker_network_name] -} - -# Include both service definitions in your networking modules -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.pterodactyl_panel.service_definition, - module.pterodactyl_wings.service_definition, - # Other service definitions - ] -} -``` - -## Configuration Requirements - -### Panel Setup - -1. Create a `.env` file in the panel module directory with required variables: - - Database credentials (`MYSQL_PASSWORD`, `MYSQL_ROOT_PASSWORD`, etc.) - - App settings (`APP_URL`, `APP_TIMEZONE`, etc.) - - CORS and proxy settings - -2. SMTP settings are sourced from the global SMTP module - -### Wings Setup - -1. After deploying the Panel, you need to: - - Create a node in the Panel UI - - Download the wings configuration from the Panel - - Place it at `${volume_path}/etc/config.yml` for the Wings module - -## Network Configuration - -Both components create their own dedicated Docker networks: - -- `ptero-panel`: For communication between Panel, database, and cache -- `ptero-wings`: For communication between Wings and game servers - -Additionally, both components need to be connected to your main homelab network to communicate with each other. - -## Service Definitions - -Both components generate service definitions that can be used by your networking modules: - -- Panel: Published on the domain `gameservers.yourdomain.com` -- Wings: Published on the domain `wings.yourdomain.com` - -## Security Considerations - -- Wings requires `privileged` mode to create game server containers -- Panel communicates with Wings via API using a token configured in the wings config.yml - -## Additional Documentation - -For more detailed information about each component, please see: - -- [Panel README](/modules/20-services-apps/pterodactyl/panel/README.md) -- [Wings README](/modules/20-services-apps/pterodactyl/wings/README.md) - -For official Pterodactyl documentation, visit [https://pterodactyl.io/](https://pterodactyl.io/) diff --git a/modules/20-services-apps/pterodactyl/panel/.env.example b/modules/20-services-apps/pterodactyl/panel/.env.example deleted file mode 100644 index 1c64ad4..0000000 --- a/modules/20-services-apps/pterodactyl/panel/.env.example +++ /dev/null @@ -1,18 +0,0 @@ -# Pterodactyl Panel Environment Settings - -# Database Configuration -MYSQL_PASSWORD=secure_database_password_here -MYSQL_ROOT_PASSWORD=secure_root_password_here -MYSQL_DATABASE=pterodactyl -MYSQL_USER=pterodactyl - -# Panel Configuration -APP_URL=https://panel.yourdomain.com -APP_TIMEZONE=Australia/Brisbane -APP_SERVICE_AUTHOR=email@example.com -APP_CORS_ALLOWED_ORIGINS=https://panel.yourdomain.com -TRUSTED_PROXIES="*" # Set this to your proxy IP - -# Optional: Let's Encrypt Settings -# Uncomment and set to your email to use Let's Encrypt -# LE_EMAIL=admin@yourdomain.com diff --git a/modules/20-services-apps/pterodactyl/panel/README.md b/modules/20-services-apps/pterodactyl/panel/README.md deleted file mode 100644 index 4bbc0ee..0000000 --- a/modules/20-services-apps/pterodactyl/panel/README.md +++ /dev/null @@ -1,109 +0,0 @@ -# Pterodactyl Panel Module - -This module deploys [Pterodactyl Panel](https://pterodactyl.io/), a game server management panel, as Docker containers in the homelab environment. - -## Overview - -The Pterodactyl Panel module: - -- Deploys three Docker containers: - - `pterodactyl-panel`: The main web UI and API server - - `pterodactyl-db`: A MariaDB database backend - - `pterodactyl-cache`: A Redis cache server -- Creates a dedicated Docker network (`ptero-panel`) for container communication -- Persists data to volumes on the host -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "pterodactyl_panel" { - source = "./modules/20-services-apps/pterodactyl/panel" - volume_path = "/path/to/volumes/pterodactyl/panel" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | ---------------------------------------------------------- | -------------- | ---------- | -| `image_tag` | Tag of the Pterodactyl Panel image to use | `string` | `"latest"` | -| `volume_path` | Host path for Pterodactyl Panel volumes | `string` | - | -| `networks` | List of networks to which the panel should be attached | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "pterodactyl-panel" - primary_port = 80 - endpoint = "http://pterodactyl-panel:80" - subdomains = ["gameservers"] - publish_via = "tunnel" -} -``` - -## Environment Variables - -Pterodactyl Panel requires several environment variables to function properly. These are stored in a `.env` file in the module directory and read using the `dotenv` Terraform provider. Key variables include: - -- Panel Configuration: - - `APP_URL`: The URL where the panel will be accessed - - `APP_TIMEZONE`: The timezone for the application - - `APP_SERVICE_AUTHOR`: Service author information - -- Database Configuration: - - `MYSQL_PASSWORD`: Database password - - `MYSQL_ROOT_PASSWORD`: Database root password - - `MYSQL_DATABASE`: Database name - - `MYSQL_USER`: Database username - -- Mail Configuration: - - Mail settings are automatically sourced from the global SMTP module - -## Data Persistence - -Pterodactyl Panel stores its data in multiple volumes: - -1. Application data: `/app/var` in the container, mapped to `${volume_path}/var` on the host -2. Nginx configuration: `/etc/nginx/http.d` in the container, mapped to `${volume_path}/nginx` on the host -3. SSL certificates: `/etc/letsencrypt` in the container, mapped to `${volume_path}/certs` on the host -4. Logs: `/app/storage/logs` in the container, mapped to `${volume_path}/logs` on the host -5. Database data: `/var/lib/mysql` in the MariaDB container, mapped to `${volume_path}/database` on the host - -## Networking - -The module creates a dedicated Docker network named `ptero-panel` for communication between the panel, database, and cache containers. The panel container is also attached to any additional networks specified in the `networks` variable, allowing it to communicate with other services in the homelab. - -## Integration with Networking Modules - -This service is configured to be exposed through a Cloudflare tunnel for secure remote access, set by `publish_via = "tunnel"`. - -## Example Integration in Main Configuration - -```hcl -module "pterodactyl_panel" { - source = "./modules/20-services-apps/pterodactyl/panel" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.pterodactyl_panel.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/pterodactyl/panel/main.tf b/modules/20-services-apps/pterodactyl/panel/main.tf deleted file mode 100644 index fb9c3d5..0000000 --- a/modules/20-services-apps/pterodactyl/panel/main.tf +++ /dev/null @@ -1,164 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -module "smtp" { - source = "../../../00-globals/smtp" -} - -variable "image_tag" { - description = "The tag for the Pterodactyl Panel container image" - type = string - default = "latest" -} - -variable "volume_path" { - description = "Base directory for volumes" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -locals { - container_name = "pterodactyl-panel" - database_name = "pterodactyl-db" - cache_name = "pterodactyl-cache" - panel_image = "ghcr.io/pterodactyl/panel" - database_image = "mariadb" - cache_image = "redis" - panel_tag = var.image_tag != "" ? var.image_tag : "latest" - database_tag = "10.5" - cache_tag = "alpine" - monitoring = true - env_file = "${path.module}/.env" - - # Volume paths - panel_volumes = [ - { - host_path = "${var.volume_path}/var" - container_path = "/app/var" - read_only = false - }, - { - host_path = "${var.volume_path}/nginx" - container_path = "/etc/nginx/http.d" - read_only = false - }, - { - host_path = "${var.volume_path}/certs" - container_path = "/etc/letsencrypt" - read_only = false - }, - { - host_path = "${var.volume_path}/logs" - container_path = "/app/storage/logs" - read_only = false - } - ] - - database_volumes = [ - { - host_path = "${var.volume_path}/database" - container_path = "/var/lib/mysql" - read_only = false - } - ] - - # Environment variables - panel_env_vars = { - APP_URL = provider::dotenv::get_by_key("APP_URL", local.env_file) - APP_TIMEZONE = provider::dotenv::get_by_key("APP_TIMEZONE", local.env_file) - APP_SERVICE_AUTHOR = provider::dotenv::get_by_key("APP_SERVICE_AUTHOR", local.env_file) - APP_CORS_ALLOWED_ORIGINS = provider::dotenv::get_by_key("APP_CORS_ALLOWED_ORIGINS", local.env_file) - TRUSTED_PROXIES = provider::dotenv::get_by_key("TRUSTED_PROXIES", local.env_file) - MAIL_FROM = module.smtp.mail_from - MAIL_DRIVER = "smtp" - MAIL_HOST = module.smtp.mail_host - MAIL_PORT = module.smtp.mail_port - MAIL_USERNAME = module.smtp.mail_username - MAIL_PASSWORD = module.smtp.mail_password - MAIL_ENCRYPTION = "false" - DB_PASSWORD = provider::dotenv::get_by_key("MYSQL_PASSWORD", local.env_file) - APP_ENV = "production" - APP_ENVIRONMENT_ONLY = "false" - CACHE_DRIVER = "redis" - SESSION_DRIVER = "redis" - QUEUE_DRIVER = "redis" - REDIS_HOST = local.cache_name - DB_HOST = local.database_name - DB_PORT = "3306" - DB_DATABASE = provider::dotenv::get_by_key("MYSQL_DATABASE", local.env_file) - DB_USERNAME = provider::dotenv::get_by_key("MYSQL_USER", local.env_file) - } - - database_env_vars = { - MYSQL_PASSWORD = provider::dotenv::get_by_key("MYSQL_PASSWORD", local.env_file) - MYSQL_ROOT_PASSWORD = provider::dotenv::get_by_key("MYSQL_ROOT_PASSWORD", local.env_file) - MYSQL_DATABASE = provider::dotenv::get_by_key("MYSQL_DATABASE", local.env_file) - MYSQL_USER = provider::dotenv::get_by_key("MYSQL_USER", local.env_file) - } -} - -# Create a dedicated network for Pterodactyl -module "pterodactyl_network" { - source = "../../../01-networking/docker-network" - name = "ptero-panel" - driver = "bridge" - subnet = "172.20.0.0/16" - attachable = true -} - -# Database container -module "database" { - source = "../../../10-services-generic/docker-service" - container_name = local.database_name - image = local.database_image - tag = local.database_tag - volumes = local.database_volumes - env_vars = local.database_env_vars - networks = [module.pterodactyl_network.name] - command = ["--default-authentication-plugin=mysql_native_password"] - monitoring = local.monitoring -} - -# Cache container -module "cache" { - source = "../../../10-services-generic/docker-service" - container_name = local.cache_name - image = local.cache_image - tag = local.cache_tag - networks = [module.pterodactyl_network.name] - monitoring = local.monitoring -} - -# Panel container -module "panel" { - source = "../../../10-services-generic/docker-service" - container_name = local.container_name - image = local.panel_image - tag = local.panel_tag - volumes = local.panel_volumes - env_vars = local.panel_env_vars - networks = concat([module.pterodactyl_network.name], var.networks) - monitoring = local.monitoring - depends_on = [module.database, module.cache] -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = 80 - endpoint = "http://${local.container_name}:80" - subdomains = ["gameservers"] - publish_via = "tunnel" - } -} diff --git a/modules/20-services-apps/pterodactyl/wings/README.md b/modules/20-services-apps/pterodactyl/wings/README.md deleted file mode 100644 index 99969bc..0000000 --- a/modules/20-services-apps/pterodactyl/wings/README.md +++ /dev/null @@ -1,100 +0,0 @@ -# Pterodactyl Wings Module - -This module deploys [Pterodactyl Wings](https://pterodactyl.io/wings/), the game server agent component of Pterodactyl, as a Docker container in the homelab environment. - -## Overview - -The Pterodactyl Wings module: - -- Deploys the `pterodactyl-wings` Docker container -- Creates a dedicated Docker network (`ptero-wings`) for game server communication -- Persists data to volumes on the host -- Provides service definition for integration with networking modules -- Runs with privileged mode to manage game server containers - -## Usage - -```hcl -module "pterodactyl_wings" { - source = "./modules/20-services-apps/pterodactyl/wings" - volume_path = "/path/to/volumes/pterodactyl/wings" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | ------------------------------------------------------- | -------------- | ----------- | -| `image_tag` | Tag of the Pterodactyl Wings image to use | `string` | `"v1.11.3"` | -| `volume_path` | Host path for Pterodactyl Wings volumes | `string` | - | -| `networks` | List of networks to which wings should be attached | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| -------------------- | ---------------------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "pterodactyl-wings" - primary_port = 443 - endpoint = "http://pterodactyl-wings:443" - subdomains = ["wings"] - publish_via = "tunnel" -} -``` - -## Environment Variables - -Pterodactyl Wings uses the following environment variables: - -- `TZ`: Timezone (set to Australia/Brisbane) -- `WINGS_UID`: User ID for wings process (988) -- `WINGS_GID`: Group ID for wings process (988) -- `WINGS_USERNAME`: Username for wings process ("pterodactyl") - -## Data Persistence - -Pterodactyl Wings uses several volume mounts: - -1. Docker socket: `/var/run/docker.sock` (for controlling game server containers) -2. Docker containers: `/var/lib/docker/containers/` (for accessing container information) -3. SSL certificates: `/etc/ssl/certs` (mounted read-only) -4. Wings configuration: `/etc/pterodactyl/` in the container, mapped to `${volume_path}/etc` -5. Wings data: `/var/lib` in the container, mapped to `${volume_path}/var/lib` -6. Logs: `/var/log/pterodactyl/` in the container, mapped to `${volume_path}/var/log` -7. Temporary files: `${volume_path}/tmp` in the container and host - -## Networking - -The module creates a dedicated Docker network named `ptero-wings` for game server communication. This network is configured with the subnet `172.21.0.0/16` and is made attachable to allow game server containers to connect to it. The wings container is also attached to any additional networks specified in the `networks` variable. - -## Integration with Networking Modules - -This service is configured to be exposed through a Cloudflare tunnel for secure remote access, set by `publish_via = "tunnel"`. - -## Example Integration in Main Configuration - -```hcl -module "pterodactyl_wings" { - source = "./modules/20-services-apps/pterodactyl/wings" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.pterodactyl_wings.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/pterodactyl/wings/main.tf b/modules/20-services-apps/pterodactyl/wings/main.tf deleted file mode 100644 index 51a1984..0000000 --- a/modules/20-services-apps/pterodactyl/wings/main.tf +++ /dev/null @@ -1,118 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -variable "image_tag" { - description = "The tag for the Pterodactyl Wings container image" - type = string - default = "v1.11.3" -} - -variable "volume_path" { - description = "Base directory for volumes" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -locals { - container_name = "pterodactyl-wings" - image = "ghcr.io/pterodactyl/wings" - image_tag = var.image_tag != "" ? var.image_tag : "v1.11.3" - monitoring = false - env_file = "${path.module}/.env" - subdomains = ["wings"] - - # Volumes configuration - volumes = [ - { - host_path = "/var/run/docker.sock" - container_path = "/var/run/docker.sock" - read_only = false - }, - { - host_path = "/var/lib/docker/containers/" - container_path = "/var/lib/docker/containers/" - read_only = false - }, - { - host_path = "/etc/ssl/certs" - container_path = "/etc/ssl/certs" - read_only = true - }, - { - host_path = "${var.volume_path}/etc" - container_path = "/etc/pterodactyl/" - read_only = false - }, - { - host_path = "${var.volume_path}/var/lib" - container_path = "${var.volume_path}/var/lib" - read_only = false - }, - { - host_path = "${var.volume_path}/var/log" - container_path = "/var/log/pterodactyl/" - read_only = false - }, - { - host_path = "${var.volume_path}/tmp" - container_path = "${var.volume_path}/tmp" - read_only = false - }, - ] - - # Environment variables - env_vars = { - TZ = "Australia/Brisbane" - WINGS_UID = 988 - WINGS_GID = 988 - WINGS_USERNAME = "pterodactyl" - } -} - -# Create a custom Docker network for wings -module "wings_network" { - source = "../../../01-networking/docker-network" - - name = "ptero-wings" - driver = "bridge" - attachable = true - subnet = "172.21.0.0/16" - options = { - "com.docker.network.bridge.name" = "ptero-wings" - } -} - -module "wings" { - source = "../../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.image_tag - pgid = 988 - puid = 988 - volumes = local.volumes - env_vars = local.env_vars - networks = concat([module.wings_network.name], var.networks) - monitoring = local.monitoring - privileged = true -} - -output "service_definition" { - description = "General service definition with optional ingress configuration" - value = { - name = local.container_name - primary_port = 443 - endpoint = "http://${local.container_name}:443" - subdomains = local.subdomains - publish_via = "tunnel" - } -} diff --git a/modules/20-services-apps/qbittorrent/README.md b/modules/20-services-apps/qbittorrent/README.md deleted file mode 100644 index fed69c2..0000000 --- a/modules/20-services-apps/qbittorrent/README.md +++ /dev/null @@ -1,117 +0,0 @@ -# qBittorrent Module - -This module deploys qBittorrent as a Docker container with the Vuetorrent UI mod. - -## Overview - -- Container: `qbittorrent` (LinuxServer.io) with `vuetorrent` mod -- Web UI on TCP 8080 -- Mounts `/config` and `/data/torrents` - -## Usage - -Without Gluetun: - -```hcl -module "qbittorrent" { - source = "./modules/20-services-apps/qbittorrent" - volume_path = "/srv/appdata/qbittorrent" - downloads_path = "/srv/data/torrents" - networks = [module.media_docker_network.name] -} -``` - -With Gluetun (recommended for privacy): - -```hcl -module "gluetun" { - source = "./modules/20-services-apps/gluetun" - volume_path = "/srv/appdata/gluetun" - networks = [module.media_docker_network.name] - # Optional: expose qBittorrent UI to the host via Gluetun - # ports = [{ internal = 8080, external = 8080, protocol = "tcp" }] -} - -module "qbittorrent" { - source = "./modules/20-services-apps/qbittorrent" - volume_path = "/srv/appdata/qbittorrent" - downloads_path = "/srv/data/torrents" - networks = [module.media_docker_network.name] - connect_via_gluetun = true - gluetun_container_name = "gluetun" -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ----------------------- | --------------------------------------------------------------- | -------------- | ----------- | -| `volume_path` | Base directory for qBittorrent config | `string` | - | -| `downloads_path` | Directory for downloads mounted at /data/torrents | `string` | - | -| `networks` | Networks to attach (ignored when `connect_via_gluetun` is true) | `list(string)` | `[]` | -| `connect_via_gluetun` | Route qBittorrent through Gluetun (network_mode=container:gluetun) | `bool` | `false` | -| `gluetun_container_name`| Container name of the Gluetun instance | `string` | `"gluetun"` | - -## Outputs - -| Output | Description | -| -------------------- | ----------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules. qBittorrent is not published externally. - -```hcl -{ - name = "qbittorrent" - primary_port = 8080 - endpoint = "http://qbittorrent:8080" -} -``` - -## Environment Variables - -- Defaults: - - `WEBUI_PORT=8080` - - `DOCKER_MODS=ghcr.io/gabe565/linuxserver-mod-vuetorrent` -- `TZ`, `PUID`, and `PGID` are injected by the generic docker-service module from system globals. - -## Data Persistence - -- `/config` -> `${volume_path}` -- `/data/torrents` -> `${downloads_path}` -- Ensure host paths exist and permissions align with the container user. - -## Networking - -- When `connect_via_gluetun = false`: - - Container attaches to `networks` and exposes its Web UI internally at `http://qbittorrent:8080`. -- When `connect_via_gluetun = true`: - - Container runs with `network_mode = container:`. - - Do not publish ports on qBittorrent. If you need host access, publish `8080/tcp` on the Gluetun module instead. - - Other containers should reach the Web UI at `http://gluetun:8080` when on the same Docker network as Gluetun. - -## Dependencies - -- No explicit inter-container dependencies. Healthcheck ensures readiness. - -## Integration with Networking Modules - -This service is not published externally. Its service definition is included in the aggregated `module.services.service_definitions` for internal discovery and potential future use by networking modules. - -## Example Integration in Main Configuration - -```hcl -# In services/main.tf -module "qbittorrent" { - source = "${local.module_dir}/20-services-apps/qbittorrent" - volume_path = "${local.volume_host}/qbittorrent" - downloads_path = "${local.data_host}/torrents" - networks = [module.media_docker_network.name] - connect_via_gluetun = true - gluetun_container_name = "gluetun" -} -``` - -The service definition is exported by the `services` module as `module.services.service_definitions` and consumed by networking modules in the root `main.tf`. diff --git a/modules/20-services-apps/qbittorrent/main.tf b/modules/20-services-apps/qbittorrent/main.tf deleted file mode 100644 index 9177cdd..0000000 --- a/modules/20-services-apps/qbittorrent/main.tf +++ /dev/null @@ -1,86 +0,0 @@ -variable "volume_path" { - description = "Base directory for qBittorrent config" - type = string -} -variable "downloads_path" { - description = "Directory for downloads mounted at /data/torrents" - type = string -} -variable "networks" { - description = "List of networks to attach" - type = list(string) - default = [] -} - -// When true, run qBittorrent through Gluetun by sharing its network namespace -variable "connect_via_gluetun" { - description = "Route qBittorrent through Gluetun (network_mode=container:gluetun)" - type = bool - default = false -} - -variable "gluetun_container_name" { - description = "Container name of the Gluetun instance to share network with" - type = string - default = "gluetun" -} - -locals { - container_name = "qbittorrent" - image = "lscr.io/linuxserver/qbittorrent" - tag = "libtorrentv1" - monitoring = true - internal_port = 8080 - - use_gluetun = var.connect_via_gluetun - gluetun_name = var.gluetun_container_name - network_mode = local.use_gluetun ? "container:${local.gluetun_name}" : "bridge" - - env_vars = { - WEBUI_PORT = "8080" - DOCKER_MODS = "ghcr.io/gabe565/linuxserver-mod-vuetorrent" - } - - volumes = [ - { - host_path = var.volume_path, - container_path = "/config", - read_only = false - }, - { - host_path = var.downloads_path, - container_path = "/data/torrents", - read_only = false - } - ] - - healthcheck = { - test = ["CMD", "curl", "--fail", "http://127.0.0.1:8080", "https://google.com"] - interval = "60s" - timeout = "5s" - retries = 10 - } -} - -module "qbittorrent" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.tag - env_vars = local.env_vars - volumes = local.volumes - network_mode = local.network_mode - networks = local.use_gluetun ? [] : var.networks - monitoring = local.monitoring - healthcheck = local.healthcheck - ports = local.use_gluetun ? [] : [{ internal = local.internal_port, external = local.internal_port, protocol = "tcp" }] -} - -output "service_definition" { - description = "Service definition for qBittorrent (not published)" - value = { - name = local.container_name - primary_port = local.internal_port - endpoint = "http://${local.container_name}:${local.internal_port}" - } -} diff --git a/modules/20-services-apps/sabnzbd/README.md b/modules/20-services-apps/sabnzbd/README.md deleted file mode 100644 index f3e7f27..0000000 --- a/modules/20-services-apps/sabnzbd/README.md +++ /dev/null @@ -1,78 +0,0 @@ -# Sabnzbd Module - -This module deploys Sabnzbd as a Docker container and outputs a non-published service definition. - -## Overview - -- Container: `sabnzbd` (LinuxServer.io) -- Web UI on TCP 8080 -- Mounts `/config` and `/downloads` - -## Usage - -```hcl -module "sabnzbd" { - source = "./modules/20-services-apps/sabnzbd" - volume_path = "/srv/appdata/sabnzbd" # host path for app config - downloads_path = "/srv/data/usenet" # host path for usenet downloads - networks = [module.media_docker_network.name, module.homelab_docker_network.name] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ---------------- | ------------------------------------------- | -------------- | ------- | -| `volume_path` | Base directory for Sabnzbd config | `string` | - | -| `downloads_path` | Directory for downloads mounted at /downloads | `string` | - | -| `networks` | List of networks to attach | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| -------------------- | ------------------------------ | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules. Sabnzbd is not published externally. - -```hcl -{ - name = "sabnzbd" - primary_port = 8080 - endpoint = "http://sabnzbd:8080" -} -``` - -## Environment Variables - -- `TZ`, `PUID`, and `PGID` are injected automatically via system globals in the generic docker-service module. - -## Data Persistence - -- `/config` -> `${volume_path}` -- `/downloads` -> `${downloads_path}` -- Ensure host paths exist and permissions align with the container user. - -## Networking - -- Attaches to `networks` (typically media and homelab). Not published externally; accessible internally. - -## Dependencies - -- No explicit inter-container dependencies. Healthcheck ensures readiness. - -## Example Integration in Main Configuration - -```hcl -# In services/main.tf -module "sabnzbd" { - source = "${local.module_dir}/20-services-apps/sabnzbd" - volume_path = "${local.volume_host}/sabnzbd" - downloads_path = "${local.data_host}/usenet" - networks = [module.media_docker_network.name, module.homelab_docker_network.name] -} -``` - -The service definition is exported by the `services` module as `module.services.service_definitions` and consumed by networking modules in the root `main.tf`. diff --git a/modules/20-services-apps/sabnzbd/main.tf b/modules/20-services-apps/sabnzbd/main.tf deleted file mode 100644 index 2eb79f8..0000000 --- a/modules/20-services-apps/sabnzbd/main.tf +++ /dev/null @@ -1,69 +0,0 @@ -variable "volume_path" { - description = "Base directory for Sabnzbd config" - type = string -} -variable "downloads_path" { - description = "Directory for downloads mounted at /downloads" - type = string -} -variable "networks" { - description = "List of networks to attach" - type = list(string) - default = [] -} - -locals { - container_name = "sabnzbd" - image = "lscr.io/linuxserver/sabnzbd" - tag = "latest" - monitoring = true - internal_port = 8080 - - env_vars = { - # Add typical env like PUID/PGID/TZ if desired via the generic module interface - } - - volumes = [ - { - host_path = var.volume_path, - container_path = "/config", - read_only = false - }, - { - host_path = var.downloads_path, - container_path = "/data/usenet/downloads", - read_only = false - } - ] - - healthcheck = { - test = ["CMD", "curl", "--fail", "http://127.0.0.1:8080"] - interval = "60s" - timeout = "5s" - retries = 10 - } -} - -module "sabnzbd" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.tag - env_vars = local.env_vars - volumes = local.volumes - networks = var.networks - monitoring = local.monitoring - healthcheck = local.healthcheck -} - -output "service_definition" { - description = "Service definition for Sabnzbd (not published)" - value = { - name = local.container_name - primary_port = local.internal_port - endpoint = "http://${local.container_name}:${local.internal_port}" - subdomains = ["sabnzbd"] - publish_via = "tunnel" - proxied = true - } -} diff --git a/modules/20-services-apps/searxng/README.md b/modules/20-services-apps/searxng/README.md deleted file mode 100644 index 2e23c04..0000000 --- a/modules/20-services-apps/searxng/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# SearxNG Module - -This module deploys [SearxNG](https://searx.github.io/searx/), a privacy-respecting metasearch engine, as a Docker container in the homelab environment. - -## Overview - -The SearxNG module: - -- Deploys the `searxng/searxng` Docker container -- Persists configuration data to a volume on the host -- Provides service definition for integration with networking modules - -## Usage - -```hcl -module "searxng" { - source = "./modules/20-services-apps/searxng" - volume_path = "/path/to/volumes/searxng" - networks = ["homelab-network"] -} -``` - -## Variables - -| Variable | Description | Type | Default | -| ------------- | ---------------------------------------------------------- | -------------- | ---------- | -| `image_tag` | Tag of the SearxNG image to use | `string` | `"latest"` | -| `volume_path` | Host path for SearxNG configuration volume | `string` | - | -| `networks` | List of networks to which the container should be attached | `list(string)` | `[]` | - -## Outputs - -| Output | Description | -| -------------------- | --------------------------------------------- | -| `service_definition` | Service definition for integration with networking modules | - -## Service Definition - -This module outputs a service definition that is used by the networking modules to expose the service. - -```hcl -{ - name = "searxng" - primary_port = 8080 - endpoint = "http://searxng:8080" - subdomains = ["search"] - publish_via = "tunnel" # Only publish through Cloudflare tunnel -} -``` - -## Data Persistence - -SearxNG stores its configuration in a single volume: - -- Configuration: `/etc/searxng` in the container, mapped to `${volume_path}/config` on the host - -## Integration with Networking Modules - -This service is configured to be exposed through a Cloudflare tunnel for secure remote access, set by `publish_via = "tunnel"`. - -## Example Integration in Main Configuration - -```hcl -module "searxng" { - source = "./modules/20-services-apps/searxng" - volume_path = module.system_globals.volume_host - networks = [module.services.homelab_docker_network_name] -} - -# The service definition is automatically included in the services output -module "services" { - source = "./modules/services" - # ... - service_definitions = [ - module.searxng.service_definition, - # Other service definitions - ] -} -``` diff --git a/modules/20-services-apps/searxng/main.tf b/modules/20-services-apps/searxng/main.tf deleted file mode 100644 index 50dcc94..0000000 --- a/modules/20-services-apps/searxng/main.tf +++ /dev/null @@ -1,60 +0,0 @@ -terraform { - required_providers { - dotenv = { - source = "germanbrew/dotenv" - } - } -} - -variable "image_tag" { - description = "The tag for the searxng container image" - type = string - default = "latest" -} - -variable "volume_path" { - description = "Base directory for volumes" - type = string -} - -variable "networks" { - description = "List of networks to which the container should be attached" - type = list(string) - default = [] -} - -locals { - container_name = "searxng" - image = "searxng/searxng" - tag = var.image_tag != "" ? var.image_tag : "latest" - monitoring = true - internal_port = 8080 - volumes = [ - { - host_path = "${var.volume_path}/config" - container_path = "/etc/searxng" - read_only = false - } - ] -} - -module "searxng" { - source = "../../10-services-generic/docker-service" - container_name = local.container_name - image = local.image - tag = local.tag - volumes = local.volumes - networks = var.networks - monitoring = local.monitoring -} - -output "service_definition" { - description = "Service definition with ingress configuration" - value = { - name = local.container_name - primary_port = local.internal_port - endpoint = "http://${local.container_name}:${local.internal_port}" - subdomains = ["search"] - publish_via = "tunnel" - } -} diff --git a/services/main.tf b/services/main.tf index 268c8d3..bd48700 100755 --- a/services/main.tf +++ b/services/main.tf @@ -30,72 +30,18 @@ module "media_docker_network" { subnet = "10.110.0.0/16" } -module "actualbudget" { - source = "${local.module_dir}/20-services-apps/actualbudget" - volume_path = "${local.volume_host}/actual" - networks = [module.homelab_docker_network.name] -} - -module "affine" { - source = "${local.module_dir}/20-services-apps/affine" - volume_path = "${local.volume_host}/affine" - networks = [module.homelab_docker_network.name] -} - -module "arr" { - source = "${local.module_dir}/20-services-apps/arr" - volume_path = "${local.volume_host}/arr" - data_path = local.data_host - downloads_path = "${local.data_host}/torrents" - networks = [module.media_docker_network.name] - proxy_networks = [module.homelab_docker_network.name] - qbittorrent_host = "gluetun" -} - module "calibre" { source = "${local.module_dir}/20-services-apps/calibre" volume_path = "${local.volume_host}/calibre" networks = [module.homelab_docker_network.name] } -module "copyparty" { - source = "${local.module_dir}/20-services-apps/copyparty" - fileshare_path = local.root_volume - config_path = "${local.volume_host}/copyparty" - networks = [module.homelab_docker_network.name] -} - -module "crawl4ai" { - source = "${local.module_dir}/20-services-apps/crawl4ai" - volume_path = "${local.volume_host}/crawl4ai" - networks = [module.homelab_docker_network.name] -} - -module "emulatorjs" { - source = "${local.module_dir}/20-services-apps/emulatorjs" - volume_path = "${local.volume_host}/emulatorjs" - image_tag = "1.9.2" -} - module "glance" { source = "${local.module_dir}/20-services-apps/glance" volume_path = "${local.volume_host}/glance" networks = [module.homelab_docker_network.name] } -module "gluetun" { - source = "${local.module_dir}/20-services-apps/gluetun" - volume_path = "${local.volume_host}/gluetun" - networks = [module.media_docker_network.name] - ports = [ - # Expose qBittorrent UI to the host - { - internal = 8080 - external = 8080 - protocol = "tcp" - } - ] -} module "immich" { source = "${local.module_dir}/20-services-apps/immich" @@ -117,23 +63,6 @@ module "linkwarden" { networks = [module.homelab_docker_network.name] } -module "n8n" { - source = "${local.module_dir}/20-services-apps/n8n" - volume_path = "${local.volume_host}/n8n" - networks = [module.homelab_docker_network.name] -} - -module "nocodb" { - source = "${local.module_dir}/20-services-apps/nocodb" - volume_path = "${local.volume_host}/nocodb" - networks = [module.homelab_docker_network.name] -} - -module "ntfy" { - source = "${local.module_dir}/20-services-apps/ntfy" - volume_path = "${local.volume_host}/ntfy" - networks = [module.homelab_docker_network.name] -} module "portainer" { source = "${local.module_dir}/20-services-apps/portainer" @@ -141,37 +70,3 @@ module "portainer" { networks = [module.homelab_docker_network.name] } -module "pterodactyl_panel" { - source = "${local.module_dir}/20-services-apps/pterodactyl/panel" - volume_path = "${local.volume_host}/pterodactyl/panel" - networks = [module.homelab_docker_network.name] -} - -module "pterodactyl_wings" { - source = "${local.module_dir}/20-services-apps/pterodactyl/wings" - volume_path = "${local.volume_host}/pterodactyl/wings" - networks = [module.homelab_docker_network.name] -} - -module "qbittorrent" { - source = "${local.module_dir}/20-services-apps/qbittorrent" - volume_path = "${local.volume_host}/qbittorrent" - downloads_path = "${local.data_host}/torrents" - networks = [module.media_docker_network.name] - connect_via_gluetun = true - gluetun_container_name = "gluetun" - depends_on = [module.gluetun] -} - -module "sabnzbd" { - source = "${local.module_dir}/20-services-apps/sabnzbd" - volume_path = "${local.volume_host}/sabnzbd" - downloads_path = "${local.data_host}/usenet/downloads" - networks = [module.media_docker_network.name, module.homelab_docker_network.name] -} - -module "searxng" { - source = "${local.module_dir}/20-services-apps/searxng" - volume_path = "${local.volume_host}/searxng" - networks = [module.homelab_docker_network.name] -} diff --git a/services/outputs.tf b/services/outputs.tf index 44c2271..8757802 100755 --- a/services/outputs.tf +++ b/services/outputs.tf @@ -4,27 +4,12 @@ output "service_definitions" { description = "Service definitions for all services" value = [ - module.actualbudget.service_definition, - module.affine.service_definition, - module.arr.service_definition, module.calibre.service_definition, - module.copyparty.service_definition, - module.crawl4ai.service_definition, - module.emulatorjs.service_definition, module.glance.service_definition, module.immich.service_definition, module.jellyfin.service_definition, module.linkwarden.service_definition, - module.n8n.service_definition, - module.n8n.n8n_mcp_service_definition, - module.nocodb.service_definition, - module.ntfy.service_definition, - module.portainer.service_definition, - module.pterodactyl_wings.service_definition, - module.pterodactyl_panel.service_definition, - module.qbittorrent.service_definition, - module.sabnzbd.service_definition, - module.searxng.service_definition + module.portainer.service_definition ] }