Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,14 @@ This repository follows a strict Pull Request-based workflow to ensure changes a
5. **Merge**: Once the plan is approved, merge the Pull Request into `main`.
6. **Apply Changes**: The `run-apply.yml` action must be triggered manually from the GitHub Actions UI to apply the changes to production. This is a deliberate safety measure.

## ⚙️ Configuration

This project is configured primarily through YAML files located in the `config/` directory. For detailed instructions on how to structure these files, please refer to the documentation in the `docs/` directory:

* **Stack Configuration Guide**: [stack-config.md](docs/stack-config.md)
* **Service Configuration Guide**: [service-config.md)


## 🔐 Secrets Management

Secrets are managed securely without ever being committed to the repository.
Expand Down
2 changes: 1 addition & 1 deletion config/stacks/karakeep.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ karakeep:
networks:
karakeep:
internal: true
driver: "overlay"
driver: "bridge"
services:
karakeep:
dns:
Expand Down
72 changes: 72 additions & 0 deletions docs/service-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
## ⚙️ Standalone Service Definitions

Standalone service definition files define individual services or global configuration parameters that are directly consumed by specific Terraform modules. These are distinct from the more structured "Stack Configuration Files" and are typically used for services that don't fit into a multi-service stack pattern.

### File Location

All standalone service definition files must be placed in the `config/services/` directory. Each file should have a `.yaml` extension. Terraform will merge all top-level keys from all `.yaml` files found in this directory into a single `local.services` object.

### Structure

Each service definition file should define a top-level key that represents a unique identifier for the service or configuration. Under this key, you define the specific parameters for that service.

```yaml
your_service_identifier: # e.g., flaresolverr, deluge-vpn
# Required: The canonical name of the service (used for Docker container name, etc.)
service_name: "MyStandaloneService"
# Required: The Docker image name and tag
image_name: "myrepo/my-standalone-service:latest"
# Required: The static IP address assigned to the container on its primary bridge network (e.g., br1)
ip_address: "192.168.5.X"
# Optional: The port the service listens on inside the container (used for web_ui, etc.)
service_port: 8080
# Optional: List of environment variables in "KEY=VALUE" format
env:
- "TZ=America/Chicago"
- "SOME_VAR=some_value"
# Optional: List of bind mounts in "host_path:container_path[:ro]" format
mounts:
- "/mnt/user/data:/data"
- "/etc/localtime:/etc/localtime:ro"
# Optional: Linux capabilities to add to the container
capabilities:
add:
- "NET_ADMIN"
- "SYS_PTRACE"
drop:
- "MKNOD"
- "SETUID"
# Optional: URL to an icon for the service (e.g., for Unraid UI)
icon: "https://example.com/icon.png"
# Optional: URL for the service's web UI (if not automatically derived)
web_ui: "http://my-service.local:8080"
# Other optional parameters that can be passed to the underlying 'docker' module:
# container_restart: "unless-stopped" # Default: "unless-stopped"
# container_user: "1000:100" # Default: null
# container_ports: [] # List of objects {internal=number, external=optional(number), ...}
# container_volumes: [] # List of objects for Docker-managed volumes
# container_dns_servers: ["8.8.8.8"] # Default: ["8.8.8.8", "1.1.1.1"]
# container_privileged_mode: false # Default: false
# container_network_mode: "bridge" # Default: null

# Example of a non-Docker configuration:
authentik:
admin-user: "your_authentik_admin_username"
```

### Field Meanings and Usage

* **`your_service_identifier`**: This is the top-level key in your YAML file (e.g., `flaresolverr`, `deluge-vpn`). It acts as a unique identifier for this specific service configuration within the `local.services` map.
* **`service_name`**: (Required) The human-readable name of the service, often used as the Docker container name.
* **`image_name`**: (Required) The Docker image to pull, including its tag (e.g., `binhex/arch-delugevpn:2.2`).
* **`ip_address`**: (Required) The static IPv4 address assigned to the container on its primary network interface (e.g., `br1`).
* **`service_port`**: (Optional) The port the service listens on *inside* the container. This is used by modules that need to construct URLs or proxy configurations.
* **`env`**: (Optional) A list of `KEY=VALUE` strings that will be set as environment variables inside the container.
* **`mounts`**: (Optional) A list of bind mount strings in the format `host_path:container_path[:ro]`.
* **`capabilities.add`**: (Optional) A list of Linux capabilities to add to the container (e.g., `NET_ADMIN`).
* **`icon`**: (Optional) A URL to an icon for the service, often used by UI tools like Unraid.
* **`web_ui`**: (Optional) A direct URL to the service's web interface. If not provided, some modules might attempt to construct one from `ip_address` and `service_port`.

### Special Cases

* **`authentik`**: This top-level key in `config/services.yaml` is not a Docker service definition. Instead, it holds global configuration parameters for your Authentik instance (e.g., `admin-user`) that are consumed by other Terraform modules.
141 changes: 141 additions & 0 deletions docs/stack-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@

## ⚙️ Stack Configuration Files

Stack configuration files define groups of related services and their specific settings. These files are written in YAML and are automatically discovered and loaded by Terraform.

### File Location

All stack configuration files must be placed in the `config/stacks/` directory. Each file should have a `.yaml` extension. Terraform will merge all `.yaml` files found in this directory into a single configuration object.

### Structure

Each stack configuration file should define a top-level key that represents the name of the stack (e.g., `karakeep`, `arr_services`). Under this key, you define global settings for the stack and a list of `services`.

```yaml
your_stack_name:
# Optional: Global environment variables for all services in this stack
env:
- "GLOBAL_ENV_VAR=value"
# Optional: Global bind mounts for all services in this stack
mounts:
- "/path/on/host:/path/in/container"
# Optional: Global Docker volumes for all services in this stack
volumes:
- "my_volume:/data"
# Optional: List of secret names to be generated by Terraform for this stack
generated_secrets:
- "API_KEY"
- "DATABASE_PASSWORD"
# Optional: DNS zone name for services in this stack (overrides global if set)
zone_name: "yourdomain.com"
# Optional: Custom Docker networks to be created for this stack
networks:
my_custom_network:
internal: true
driver: "bridge"
options: {} # Optional: Driver options for the network
# Required: A map of services to be deployed within this stack
services:
service_one_key: # Unique key for the service within this stack
# Required: Display name of the service
service_name: "My Awesome Service"
# Required: Docker image name and tag
image_name: "myrepo/my-service:latest"
# Optional: Description of the service
description: "A brief description of what this service does."
# Optional: URL to an icon for the service (e.g., for Unraid UI)
icon: "https://example.com/icon.png"
# Optional: Service-specific environment variables
env:
- "SERVICE_ENV_VAR=value"
- "SECRET_VAR=${API_KEY}" # References a generated secret
# Optional: Service-specific bind mounts
mounts:
- "/path/on/host:/path/in/container"
# Optional: Service-specific Docker volumes
volumes: [] # List of volume objects (see docker module variables for structure)
# Optional: Linux capabilities to add/drop for the container
capabilities:
add: ["NET_ADMIN"]
drop: ["SYS_ADMIN"]
# Optional: Network configuration for the service
network:
# Optional: Is this service internal-only (no external proxy)? Default: false
internal: false
# Optional: The port the service listens on inside the container
service_port: 8080
# Optional: Static IP address for the container on br0/br1
ip_address: "192.168.5.100"
# Optional: List of networks to attach the container to (e.g., "br1", "my_custom_network")
networks:
- "br1"
- "my_custom_network"
# Optional: DNS configuration for the service
dns:
# Optional: Enable DNS record and proxy host creation. Default: false
enabled: true
# Optional: Is this DNS record/proxy internal-only? Default: true
internal: false
# Optional: The domain name for this service (e.g., "service.yourdomain.com")
domain_name: "service.yourdomain.com"
# Optional: Nginx Proxy Manager Access List ID for this service
# If not set, defaults to "Internal Only" for internal services, "CloudFlare Only" for external.
access_list_id: "your_custom_access_list_id"
# Optional: Authentication configuration for the service
auth:
# Optional: Enable authentication for this service. Default: false
enabled: true
# Optional: Enable proxy authentication (e.g., Authentik forward auth). Default: false
proxy: true
# Optional: OAuth configuration for the service
oauth:
# Optional: Enable OAuth authentication. Default: false
enabled: true
# Optional: Authentik group for this application. Default: "Uncategorized"
group: "My App Group"
# Optional: Map of environment variable names to Authentik OAuth output keys.
# The key is the desired environment variable name (e.g., "OAUTH_CLIENT_ID").
# The value is the Authentik OAuth provider output attribute name (e.g., "client_id", "client_secret", "provider_info_url").
keys:
OAUTH_CLIENT_ID: "client_id"
OAUTH_CLIENT_SECRET: "client_secret"
OAUTH_WELL_KNOWN_URL: "provider_info_url"
# Optional: List of OAuth scopes to request.
scopes:
- "openid"
- "profile"
- "email"
# Optional: Additional redirect URIs for the OAuth provider.
# These are appended to the base domain redirect URI.
redirect_uris:
- "/oauth/callback"
- "/login/oauth/callback"

service_two_key: # Another service definition...
# ...
```

### Field Meanings and Usage

* **`env` (Stack/Service Level)**: A list of `KEY=VALUE` strings for environment variables. Service-level `env` is merged with stack-level `env`. Values like `${SECRET_NAME}` will be replaced by dynamically generated secrets.
* **`mounts` (Stack/Service Level)**: A list of bind mount strings in `host_path:container_path[:ro]`. Service-level `mounts` are merged with stack-level `mounts`.
* **`volumes` (Stack/Service Level)**: A list of Docker volume configurations.
* **`generated_secrets`**: A list of string names (e.g., `"API_KEY"`) for secrets that Terraform should generate using `openssl rand -base64 36` (or similar) and inject into environment variables.
* **`zone_name`**: The DNS zone (e.g., `yourdomain.com`) under which service domains will be created.
* **`networks` (Stack Level)**: Defines custom Docker networks to be created for this stack. These are separate from `br0` and `br1`.
* **`services`**: The core of the stack, defining individual Docker containers.
* **`service_name`**: The name of the Docker container and the base for Authentik application names.
* **`image_name`**: The Docker image to pull (e.g., `linuxserver/sonarr:latest`).
* **`network.internal`**: If `true`, the service is not exposed externally via Nginx Proxy Manager.
* **`network.service_port`**: The port the service listens on *inside* the container.
* **`network.ip_address`**: A static IP address to assign to the container on `br0` or `br1` if specified in `network.networks`.
* **`network.networks`**: A list of Docker networks to attach the container to. This can include `br0`, `br1`, or custom networks defined in `stack.networks`.
* **`dns.enabled`**: If `true`, Terraform will create a DNS record and an Nginx Proxy Manager host for this service.
* **`dns.domain_name`**: The full domain name for the service (e.g., `sonarr.yourdomain.com`).
* **`auth.enabled`**: If `true`, authentication is configured for this service.
* **`auth.proxy`**: If `true`, Nginx Proxy Manager will forward authentication requests to Authentik. The `service_port` for the DNS record will automatically point to Authentik's port.
* **`auth.oauth.enabled`**: If `true`, an Authentik OAuth2 provider and application are created for this service.
* **`auth.oauth.group`**: The Authentik group to associate with the OAuth application.
* **`auth.oauth.keys`**: Maps desired environment variable names (e.g., `OAUTH_CLIENT_ID`) to Authentik OAuth provider output attributes (e.g., `client_id`, `client_secret`, `provider_info_url`).
* **`auth.oauth.scopes`**: OAuth scopes to request from Authentik.
* **`auth.oauth.redirect_uris`**: Additional relative paths (e.g., `/oauth/callback`) that will be appended to the service's domain name to form valid OAuth redirect URIs.
1 change: 1 addition & 0 deletions terraform/modules/docker-stack/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ resource "random_password" "generated" {
special = true
}


module "custom_network" {
count = length(local.creatable_networks) > 0 ? 1 : 0
source = "../../modules/docker-network"
Expand Down