feat: add media server

This commit is contained in:
Yuris Cakranegara
2025-08-21 17:42:48 +10:00
parent 60e3a41ac5
commit bce43c4a71
15 changed files with 1239 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# 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

View File

@@ -0,0 +1,65 @@
# 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).

View File

@@ -0,0 +1,88 @@
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
}