Compare commits

74 Commits

Author SHA1 Message Date
Jeroen Vijgen 83bd1d86e3 FIX: Disable caddy headers at this point 2026-05-08 14:49:55 +03:00
Jeroen Vijgen 987aabee7b FIX: Mount caddy folder into /etc/caddy 2026-05-08 14:47:14 +03:00
jjvijgen bd366947f8 FIX: Generate proper config hash by formatting all files appropariately 2026-05-08 11:38:22 +00:00
jjvijgen 08660757e6 FIX: Rename caddyfile generation to replace . with _ 2026-05-08 11:33:09 +00:00
jjvijgen d6e133b135 FIX: Remove additional ) in generate_caddyfile_content 2026-05-08 11:22:28 +00:00
jjvijgen 7177084e27 FEA: Allow alternate custom config for caddy to be loaded 2026-05-08 11:20:31 +00:00
jjvijgen 3041de4db1 HOTFIX: Remove Mods bind for Eco 2026-04-15 18:05:55 +00:00
jjvijgen 20d857cd78 HOTFIX: Auth Eco Server towards master server 2026-04-15 17:53:54 +00:00
jjvijgen c501316b10 HOTFIX: fix container tag 2026-04-15 17:45:34 +00:00
jjvijgen 5e768111f8 HOTFIX: Remove ENV vars from ECO server 2026-04-15 17:39:03 +00:00
jjvijgen f969b1728e FEATURE: Add ECO service for friends 2026-04-15 17:38:16 +00:00
jjvijgen 46271535f5 HOTFIX: Add ACCOUNT_EMAIL_VERIFICATION='none' to auto-assign users 2026-04-12 23:44:01 +00:00
jjvijgen e778a24f41 HOTFIX: Add Email-Based Account Matching for Tandoor 2026-04-12 23:35:40 +00:00
jjvijgen 342fd7a9a6 HOTFIX: Authentik authentication fix for tandoor 2026-04-12 21:58:38 +00:00
jjvijgen 1a626e4d44 hotfix: expose TCP and UDP listeners for satisfactory, don't pipe through caddy yet 2026-04-02 13:43:03 +00:00
jjvijgen 05a00cf937 hotfix: unset read-only from satisfactory config folder 2026-04-02 13:12:04 +00:00
jjvijgen 7c4ca2cdc6 feature: Add satisfactory to homelab 2026-04-02 13:05:15 +00:00
jjvijgen 1f22570f63 Push paths 2026-03-28 21:27:51 +00:00
jjvijgen 9f8c382840 Set different path for fs-quantum 2026-03-28 21:25:38 +00:00
jjvijgen c86dbcbbc2 Add names to paths for fs-quantum 2026-03-28 15:14:21 +00:00
jjvijgen c718b03ca3 Set Denybydefault unless a rule is created for /blue /black 2026-03-28 14:02:37 +00:00
jjvijgen 46ffab47e6 Remove config item that was marked wrongly in filesystem-quantum 2026-03-28 13:50:20 +00:00
jjvijgen 2b309cf288 Change docker image for filebrowser 2026-03-28 13:47:10 +00:00
jjvijgen 08eaaf1dbd Add fs manager 2026-03-28 13:20:20 +00:00
Jeroen Vijgen 84609f7861 Revert network changes 2026-03-27 23:14:36 +02:00
Jeroen Vijgen ed05c22732 Fix IP addressing range issues 2026-03-27 23:07:00 +02:00
jjvijgen a119b970e8 Remove services not in active use 2026-03-27 20:49:21 +00:00
jjvijgen 318562afaa Set trusted_proxies on default caddy deployment 2026-03-27 20:15:52 +00:00
jjvijgen d3d53c8094 Fix some issues with Wings-Service 2026-01-21 00:17:31 +00:00
jjvijgen 812702c6c3 Change volume mount 2026-01-20 15:17:41 +00:00
jjvijgen cdc0c89b7b Remove PGID and PUID from Penpot 2026-01-20 15:04:26 +00:00
jjvijgen ad97c5ec81 Manually declare PGID and PUID 2026-01-20 14:57:24 +00:00
jjvijgen e4442f489a Revert last change 2026-01-20 14:50:29 +00:00
jjvijgen 14a9ed8049 Appropriately pass USER_ID and GROUP_ID 2026-01-20 14:47:16 +00:00
jjvijgen 0b350f6486 Add PUID and PGID to containers 2026-01-20 14:42:55 +00:00
jjvijgen aa7f69c89d Apply correct image for penpot-exporter 2025-12-22 10:32:42 +00:00
jjvijgen 0c75a9450e Use mountpoint for temporary volume 2025-12-22 10:14:06 +00:00
jjvijgen 06e6ed0f0a Check if ID is enough to apply temp volume 2025-12-18 00:32:06 +00:00
jjvijgen ca2f21535b Simplify naming for docker-volumes generic component 2025-12-18 00:16:53 +00:00
jjvijgen 0612485245 Add temp volume for penpot 2025-12-18 00:15:34 +00:00
jjvijgen 612f02ed5b Properly propegate flags for Penpot 2025-11-12 23:09:12 +00:00
jjvijgen 750b9574bf Set flags to Penpot frontend 2025-11-12 22:49:10 +00:00
jjvijgen d67a7f4c6e Parse OIDC_CLIENT_SECRET through ENV Vars 2025-11-12 21:24:00 +00:00
jjvijgen 4287a90522 Remove pelican-wings from deployment 2025-11-12 21:19:51 +00:00
jjvijgen fd3dff1252 Fix networks in penpot-service 2025-11-12 19:22:23 +00:00
jjvijgen 64deb8d48a Fix penpot env_vars that are deprecated according to their changelog 2025-11-12 16:45:38 +00:00
jjvijgen 447dcfeb10 Remove TFSec as it fails with correct formatting 2025-11-12 15:38:32 +00:00
jjvijgen 8b8b437fa9 Add newline after host= 2025-11-12 15:36:48 +00:00
jjvijgen d403f4f3e8 Fix newline after argument according to Duck.ai 2025-11-12 15:35:10 +00:00
jjvijgen 8367f111ce Try and fix TFSec 2025-11-12 15:32:01 +00:00
jjvijgen c932b272d4 Add trailing whitespace in provider.tf 2025-11-12 15:29:34 +00:00
jjvijgen 2d8083ba60 Format Tofu files 2025-11-12 15:00:38 +00:00
jjvijgen 733ecdf498 Add penpot to active services 2025-11-12 14:10:18 +00:00
jjvijgen a7e442ff10 Add penpot, upgrade authentik and add new network to README.md 2025-11-12 14:08:34 +00:00
jjvijgen eb9d52445e Add wing to service definition 2025-10-18 13:34:46 +00:00
jjvijgen 598191f869 Add wings to modules 2025-10-18 13:33:04 +00:00
jjvijgen c030bd1beb Remove database mount and set proper .env settings 2025-10-18 12:03:09 +00:00
jjvijgen 96719f428b Set appkey through .env for Pelican 2025-10-18 11:40:50 +00:00
Jeroen Vijgen ab49677aa9 Fix typo in Pelican-service
subdomain -> subdomains
2025-10-08 01:33:53 +03:00
Jeroen Vijgen b985f9b000 Remove subdomain variable
Remove subdomain variable in Variables.tf in pelican-service
2025-10-08 01:22:20 +03:00
Jeroen Vijgen f83a9ca386 Update main.tf
Clean up subdomain variable.
2025-10-08 01:21:38 +03:00
Jeroen Vijgen 191c43f9b2 Remove problematic file mounts 2025-10-08 00:52:47 +03:00
Jeroen Vijgen b662307546 Revert 2025-10-07 23:50:19 +03:00
Jeroen Vijgen 466b434d73 Update main.tf
Fix set auto.
2025-10-07 23:48:17 +03:00
Jeroen Vijgen 5c4b73e433 Update main.tf
Try AUTO mapping.
2025-10-07 23:46:58 +03:00
Jeroen Vijgen dc2e5c2179 Set User and Group IDs 2025-10-07 23:31:39 +03:00
Jeroen Vijgen ac4a43ee37 Update main.tf
Remove keep-id for panel
2025-10-07 23:20:35 +03:00
Jeroen Vijgen 6f78e7c80e Simplifying keep-id for panel 2025-10-07 23:14:22 +03:00
Jeroen Vijgen 75b641f5c4 UserIDs set for pelican panel
Set keep-id so it can access my folder structure.
2025-10-07 23:04:41 +03:00
Jeroen Vijgen ea00b84f2e Update pelican image name
Fix issue using latest:latest as image
2025-10-07 22:43:52 +03:00
Jeroen Vijgen 0f077be700 Update main.tf 2025-10-07 22:33:43 +03:00
jjvijgen d87cf6efbe Hotfix pelican network 2025-10-07 19:29:58 +00:00
Jeroen Vijgen 6fd7dc7b71 Merge pull request #1 from BlackChaosNL/fea/add-pelican
Add pelican to infra stack for friendos
2025-10-07 22:23:12 +03:00
Jeroen Vijgen a922d72b97 Create tfsec.yml
Adds security scanning for Terraform files.
2025-10-07 22:23:01 +03:00
40 changed files with 1038 additions and 316 deletions
+2 -1
View File
@@ -61,7 +61,7 @@ You will get 253 usable host addresses per network. This network is mainly for c
For connecting pods to each other (For example: Postgres -> Project <- Redis ) I use the following IP range and Subnet:
- 172.16.0.0 - 172.16.0.254
- 172.17.0.0 - 172.17.0.254
- 255.255.255.248 (/29)
You get 6 usable host addresses per internal network, to find the usable addresses you can check [here](https://www.calculator.net/ip-subnet-calculator.html?cclass=b&csubnet=29&cip=172.16.0.0&ctype=ipv4&x=Calculate).
@@ -74,6 +74,7 @@ You get 6 usable host addresses per internal network, to find the usable address
| Pelican | .8 - .15 |
| Coder | .16 - .23 |
| Tandoor | .24 - .31 |
| Penpot | .32 - .39 |
## Configuration
+5 -5
View File
@@ -7,19 +7,19 @@ module "services" {
}
locals {
volume_host = "${module.system_globals.volume_host}"
volume_host = module.system_globals.volume_host
}
module "caddy" {
source = "./modules/01-networking/caddy-service"
volume_path = "${local.volume_host}"
domains = [
source = "./modules/01-networking/caddy-service"
volume_path = local.volume_host
domains = [
"blackchaosnl.myaddr.dev",
]
tls_email = "jjvijgen@gmail.com"
container_name = "caddy"
service_definitions = module.services.service_definitions
networks = [
networks = [
module.services.infrastructure_int.name
]
}
+40 -14
View File
@@ -35,15 +35,27 @@ locals {
])
caddyfile_default = <<-EOT
# !!!DO NOT EDIT!!!
# Automatically generated through OpenTofu, changes will not be persisted upon reapplication.
{
email ${var.tls_email}
servers {
trusted_proxies static 172.16.0.0/12 10.0.0.0/8 192.168.0.0/16 10.88.0.0/16 10.100.0.0/24
}
log {
format console
output stdout
}
}
import caddy/*.caddyfile
EOT
caddyfile_security = <<-EOT
# !!!DO NOT EDIT!!!
# Automatically generated through OpenTofu, changes will not be persisted upon reapplication.
(headers) {
header {
-server
@@ -54,18 +66,18 @@ locals {
X-Content-Type-Options "nosniff"
}
}
EOT
// Generate the main Caddyfile content
caddyfile_content = format("%s%s", local.caddyfile_default, join("\n\n", [
generate_caddyfile_content = join("\n\n", [
for site in local.caddy_site_configs :
// Use the custom Caddy config if provided
<<-EOT
# !!!DO NOT EDIT!!!
# Automatically generated through OpenTofu, changes will not be persisted upon reapplication.
${site.site_address} {
import headers
route {
%{ if site.is_route_protected }
%{if site.is_route_protected}
reverse_proxy /outpost.goauthentik.io/* http://authentik:9000
forward_auth http://authentik:9000 {
@@ -73,22 +85,22 @@ locals {
copy_headers X-Authentik-Username X-Authentik-Groups X-Authentik-Entitlements X-Authentik-Email X-Authentik-Name X-Authentik-Uid X-Authentik-Jwt X-Authentik-Meta-Jwks X-Authentik-Meta-Outpost X-Authentik-Meta-Provider X-Authentik-Meta-App X-Authentik-Meta-Version
trusted_proxies private_ranges
}
%{ endif }
%{ if site.has_custom_config }
%{endif}
%{if site.has_custom_config}
${site.custom_config}
%{ else }
%{else}
reverse_proxy ${site.endpoint} {
${join("\n ", [
for key, value in site.reverse_proxy_options :
"${key} ${value}"
])}
for key, value in site.reverse_proxy_options :
"${key} ${value}"
])}
}
%{ endif }
%{endif}
}
}
EOT
]))
])
}
resource "docker_volume" "caddy_config" {
@@ -97,10 +109,19 @@ resource "docker_volume" "caddy_config" {
// Create Caddyfile in the volume path
resource "local_file" "caddyfile" {
content = local.caddyfile_content
content = local.caddyfile_default
filename = "${var.volume_path}/${local.container_name}/Caddyfile"
}
resource "local_file" "security_caddyfile" {
content = local.caddyfile_security
filename = "${var.volume_path}/${local.container_name}/caddy/security.caddyfile"
}
resource "local_file" "generated_caddyfile" {
content = local.generate_caddyfile_content
filename = "${var.volume_path}/${local.container_name}/caddy/generated.caddyfile"
}
module "caddy" {
source = "../../10-generic/docker-service"
@@ -124,6 +145,11 @@ module "caddy" {
host_path = "${var.volume_path}/${local.container_name}/Caddyfile"
container_path = "/etc/caddy/Caddyfile"
read_only = true
},
{
host_path = "${var.volume_path}/${local.container_name}/caddy"
container_path = "/etc/caddy/caddy"
read_only = true
}
]
@@ -140,5 +166,5 @@ module "caddy" {
}
]
networks = var.networks
networks = var.networks
}
@@ -5,7 +5,7 @@ output "container_name" {
output "config_hash" {
description = "The SHA256 hash of the generated Caddyfile content"
value = sha256(local.caddyfile_content)
value = sha256(format("%s%s%s", local.caddyfile_default, local.caddyfile_security, local.generate_caddyfile_content))
}
output "service_sites" {
+1 -1
View File
@@ -61,7 +61,7 @@ resource "docker_container" "service_container" {
# Set the network mode (bridge, host, etc.)
network_mode = local.network_mode
# Add host mappings (entries for /etc/hosts)
dynamic "host" {
for_each = var.host_mappings
+10 -10
View File
@@ -49,7 +49,7 @@ variable "networks" {
}
variable "volumes" {
description = "List of volume mappings"
description = "List of volume mappings that are hosted on disk"
type = list(object({
host_path = string
container_path = string
@@ -181,20 +181,20 @@ variable "privileged" {
variable "security_opts" {
description = "Set's security options for container"
type = list(string)
default = null
type = list(string)
default = null
}
variable "userns_mode" {
description = "Set's the USERNS Mode"
type = string
default = null
type = string
default = null
}
variable "gpus" {
description = "Set the GPU passthrough"
type = string
default = null
type = string
default = null
}
// Logging options
@@ -207,8 +207,8 @@ variable "log_driver" {
variable "log_opts" {
description = "Log driver options"
type = map(string)
default = {
max-size = "10m"
max-file = "3"
default = {
max-size = "10m"
max-file = "3"
}
}
+19
View File
@@ -0,0 +1,19 @@
module "system_globals" {
source = "../../00-globals/system"
}
terraform {
required_providers {
docker = {
source = "kreuzwerker/docker"
}
}
}
locals {
name = var.name
}
resource "docker_volume" "shared_volume" {
name = local.name
}
@@ -0,0 +1,9 @@
output "name" {
description = "Name of the temporary volume"
value = docker_volume.shared_volume.name
}
output "host_path" {
description = "Host path of temporary volume mount"
value = docker_volume.shared_volume.mountpoint
}
@@ -0,0 +1,4 @@
variable "name" {
description = "Name of temporary volume"
type = string
}
@@ -7,25 +7,25 @@ terraform {
}
locals {
container_name = "calibre"
calibre_image = "docker.io/crocodilestick/calibre-web-automated"
calibre_tag = var.image_tag
calibre_internal_port = 8083
container_name = "calibre"
calibre_image = "docker.io/crocodilestick/calibre-web-automated"
calibre_tag = var.image_tag
calibre_internal_port = 8083
calibre_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/config"
container_path = "/config"
read_only = false
},{
}, {
host_path = "${var.volume_path}/${local.container_name}/book-ingest"
container_path = "/cwa-book-ingest"
read_only = false
},{
}, {
host_path = "${var.volume_path}/${local.container_name}/Calibre Library"
container_path = "/calibre-library"
read_only = false
},{
}, {
host_path = "${var.volume_path}/${local.container_name}/plugins"
container_path = "/config/.config/calibre/plugins"
read_only = false
@@ -33,9 +33,9 @@ locals {
]
calibre_env_vars = {
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
}
}
@@ -0,0 +1 @@
TOKEN=
@@ -0,0 +1,60 @@
terraform {
required_providers {
dotenv = {
source = "germanbrew/dotenv"
}
}
}
locals {
container_name = "eco"
eco_image = "docker.io/strangeloopgames/eco-game-server"
eco_tag = var.image_tag
env_file = "${path.module}/.env"
eco_internal_port = 3000
eco_token = provider::dotenv::get_by_key("TOKEN", local.env_file)
}
module "eco" {
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.eco_image
tag = local.eco_tag
networks = var.networks
restart_policy = "always"
command = [ "./EcoServer", "--nogui", "--userToken=${local.eco_token}" ]
ports = [
{
internal = 3000
external = 3000
protocol = "udp"
},
{
internal = 3001
external = 3001
protocol = "tcp"
}
]
volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/config"
container_path = "/app/Configs/"
read_only = false
},
{
host_path = "${var.volume_path}/${local.container_name}/data"
container_path = "/app/Storage/"
read_only = false
}
]
}
output "service_definition" {
description = "General service definition with optional ingress configuration"
value = {
name = local.container_name
primary_port = local.eco_internal_port
endpoint = "http://${local.container_name}:${local.eco_internal_port}"
}
}
@@ -0,0 +1,34 @@
variable "image_tag" {
description = "The tag for the Eco container image. Default: latest"
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 = []
}
variable "user_id" {
description = "User ID for container permissions"
type = string
default = "1000"
}
variable "group_id" {
description = "Group ID for container permissions"
type = string
default = "1000"
}
variable "timezone" {
description = "Timezone for the container"
type = string
default = "Europe/Helsinki"
}
@@ -7,12 +7,12 @@ terraform {
}
locals {
container_name = "jellyfin"
jellyfin_image = "docker.io/jellyfin/jellyfin"
jellyfin_tag = var.image_tag
env_file = "${path.module}/.env"
jellyfin_internal_port = 8096
gpus = "all"
container_name = "jellyfin"
jellyfin_image = "docker.io/jellyfin/jellyfin"
jellyfin_tag = var.image_tag
env_file = "${path.module}/.env"
jellyfin_internal_port = 8096
gpus = "all"
jellyfin_volumes = [
{
@@ -24,7 +24,7 @@ locals {
host_path = "${var.volume_path}/${local.container_name}/config"
container_path = "/config"
read_only = false
},{
}, {
host_path = "${var.volume_path}/${local.container_name}/cache"
container_path = "/cache"
read_only = false
@@ -32,9 +32,9 @@ locals {
]
jellyfin_env_vars = {
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
}
}
@@ -0,0 +1 @@
APP_KEY=
@@ -7,14 +7,11 @@ terraform {
}
locals {
container_name = "pelican"
wings_container_name = "pelican-wings"
pelican_image = "ghcr.io/pelican-dev/panel"
pelican_wings_image = "ghcr.io/pelican-dev/wings"
pelican_tag = var.image_tag
pelican_wings_tag = var.wings_image_tag
env_file = "${path.module}/.env"
pelican_internal_port = 80
container_name = "pelican"
pelican_image = "ghcr.io/pelican-dev/panel"
pelican_tag = var.image_tag
env_file = "${path.module}/.env"
pelican_internal_port = 8000
caddyfile_content = <<-EOT
{
@@ -23,7 +20,7 @@ locals {
email none@none.invalid
}
:80 {
:8000 {
root * /var/www/html/public
encode gzip
@@ -31,89 +28,65 @@ locals {
file_server
}
EOT
pelican_env_file = <<-EOT
APP_KEY=${provider::dotenv::get_by_key("APP_KEY", local.env_file)}
APP_INSTALLED=true
APP_NAME=Pelican
APP_URL="https://gpanel.blackchaosnl.myaddr.dev"
DB_CONNECTION=sqlite
DB_DATABASE="database.sqlite"
CACHE_STORE=file
QUEUE_CONNECTION=database
SESSION_DRIVER=file
EOT
}
resource "local_file" "pelican_caddy_config_file" {
content = local.caddyfile_content
filename = "${var.volume_path}/${local.container_name}/Caddyfile"
content = local.caddyfile_content
filename = "${var.volume_path}/${local.container_name}/Caddyfile"
}
module "pelican_network" {
source = "../../01-networking/network-service"
name = "authentik-network"
subnet = "172.16.0.8/29"
driver = "bridge"
options = {
"isolate": false
}
resource "local_file" "pelican_config_file" {
content = local.pelican_env_file
filename = "${var.volume_path}/${local.container_name}/.env"
}
module "pelican-panel" {
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.pelican_image
tag = local.pelican_tag
networks = concat([pelican_network], var.networks)
networks = var.networks
restart_policy = "always"
volumes = [
volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/data"
container_path = "/pelican-data"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/Caddyfile"
container_path = "/etc/caddy/Caddyfile"
read_only = true
},
{
host_path = "${var.volume_path}/${local.container_name}/logs"
container_path = "/var/www/html/storage/logs"
read_only = false
},
{
host_path = "${var.volume_path}/${local.container_name}/Caddyfile"
container_path = "/etc/caddy/Caddyfile"
read_only = true
}
]
env_vars = {
TZ = var.timezone
APP_TIMEZONE = var.timezone
APP_ENV = "production"
APP_URL = "${var.subdomain}.blackchaosnl.myaddr.dev"
ADMIN_EMAIL = "jjvijgen@gmail.com"
}
}
module "pelican-wings" {
source = "../../10-generic/docker-service"
container_name = local.pelican_wings_image
image = local.pelican_wings_tag
tag = local.pelican_wings_tag
networks = concat([pelican_network], var.networks)
restart_policy = "always"
volumes = [
{
host_path = "/run/user/1000/podman/podman.sock"
container_path = "/var/run/docker.sock"
read_only = false
},
{
host_path = "/home/jjvij/.local/share/containers"
container_path = "/var/lib/docker/containers/"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/.env"
container_path = "/pelican-data/.env"
read_only = true
}
]
env_vars = {
TZ = var.timezone
APP_TIMEZONE = var.timezone
WINGS_UID = var.user_id
WINGS_GID = var.group_id
WINGS_USERNAME = "pelican"
TZ = var.timezone
PUID = var.user_id
PGID = var.group_id
APP_TIMEZONE = var.timezone
APP_ENV = "production"
APP_URL = "https://gpanel.blackchaosnl.myaddr.dev"
ADMIN_EMAIL = "jjvijgen@gmail.com"
}
userns_mode = "keep-id:uid=1000,gid=1000"
labels = {
"run.oci.keep_original_groups" = "1"
}
security_opts = [
"label:type:container_runtype_t"
]
}
output "service_definition" {
@@ -122,6 +95,6 @@ output "service_definition" {
name = local.container_name
primary_port = local.pelican_internal_port
endpoint = "http://${local.container_name}:${local.pelican_internal_port}"
subdomain = [var.subdomain]
subdomains = ["gpanel"]
}
}
}
@@ -4,12 +4,6 @@ variable "image_tag" {
default = "latest"
}
variable "wings_image_tag" {
description = "The tag for the Pelican Wings container image. Default: latest"
type = string
default = "latest"
}
variable "volume_path" {
description = "Base directory for volumes"
type = string
@@ -38,9 +32,3 @@ variable "timezone" {
type = string
default = "Europe/Helsinki"
}
variable "subdomain" {
description = "Subdomain on which the panel is hosted"
type = string
default = "gpanel"
}
@@ -0,0 +1,4 @@
MAXPLAYERS=16
PGID=1000
PUID=1000
STEAMBETA=false
@@ -0,0 +1,65 @@
terraform {
required_providers {
dotenv = {
source = "germanbrew/dotenv"
}
}
}
locals {
container_name = "satisfactory"
satisfactory_image = "ghcr.io/wolveix/satisfactory-server"
satisfactory_tag = var.image_tag
env_file = "${path.module}/.env"
satisfactory_internal_port = 7777
}
module "satisfactory" {
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.satisfactory_image
tag = local.satisfactory_tag
networks = var.networks
restart_policy = "always"
memory_limit = 16000 // 16Gb
ports = [
{
internal = 7777
external = 7777
protocol = "tcp"
},
{
internal = 7777
external = 7777
protocol = "udp"
},
{
internal = 8888
external = 8888
protocol = "tcp"
}
]
volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/config"
container_path = "/config"
read_only = false
},
]
env_vars = {
MAXPLAYERS = provider::dotenv::get_by_key("MAXPLAYERS", local.env_file)
PUID = var.user_id
PGID = var.group_id
}
}
output "service_definition" {
description = "General service definition with optional ingress configuration"
value = {
name = local.container_name
primary_port = local.satisfactory_internal_port
endpoint = "http://${local.container_name}:${local.satisfactory_internal_port}"
}
}
@@ -0,0 +1,34 @@
variable "image_tag" {
description = "The tag for the Satisfactory container image. Default: latest"
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 = []
}
variable "user_id" {
description = "User ID for container permissions"
type = string
default = "1000"
}
variable "group_id" {
description = "Group ID for container permissions"
type = string
default = "1000"
}
variable "timezone" {
description = "Timezone for the container"
type = string
default = "Europe/Helsinki"
}
@@ -0,0 +1,3 @@
WINGS_0_UUID=
WINGS_0_TOKEN_ID=
WINGS_0_TOKEN=
@@ -0,0 +1,116 @@
terraform {
required_providers {
dotenv = {
source = "germanbrew/dotenv"
}
}
}
locals {
container_name = "pelican-wings"
wings_image = "ghcr.io/pelican-dev/wings"
wings_tag = var.image_tag
env_file = "${path.module}/.env"
internal_port = 8080
wing_0_config = <<-EOT
debug: false
uuid: ${provider::dotenv::get_by_key("WINGS_0_UUID", local.env_file)}
token_id: ${provider::dotenv::get_by_key("WINGS_0_TOKEN_ID", local.env_file)}
token: ${provider::dotenv::get_by_key("WINGS_0_TOKEN", local.env_file)}
api:
host: 0.0.0.0
port: 8080
ssl:
enabled: false
cert: /etc/letsencrypt/live/games.blackchaosnl.myaddr.dev/fullchain.pem
key: /etc/letsencrypt/live/games.blackchaosnl.myaddr.dev/privkey.pem
upload_limit: 256
system:
data: /var/lib/pelican/volumes
sftp:
bind_port: 2022
allowed_mounts: []
remote: 'https://gpanel.blackchaosnl.myaddr.dev'
EOT
}
resource "local_file" "wing_0_config_file" {
content = local.wing_0_config
filename = "${var.volume_path}/${local.container_name}/wing-0-config.yml"
}
module "wings_network" {
source = "../../../01-networking/docker-network"
name = "pelican-wings"
driver = "bridge"
attachable = true
subnet = "172.18.0.0/16"
options = {
"com.docker.network.bridge.name" = "pelican-wings"
}
}
module "pelican-wings" {
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.wings_image
tag = local.wings_tag
networks = concat([var.wings_network.name], var.networks)
restart_policy = "always"
ports = [
{
internal = 8080
external = 8080
protocol = "tcp"
},
{
internal = 2022
external = 2022
protocol = "tcp"
}
]
volumes = [
{
host_path = "/run/user/1000/podman/podman.sock"
container_path = "/var/run/docker.sock"
read_only = false
},
{
host_path = "/home/jjvij/.local/share/containers/"
container_path = "/var/lib/docker/containers/"
read_only = false
},
{
host_path = "${var.volume_path}/${local.container_name}/wing-0-config.yml"
container_path = "/etc/pelican/config.yml"
read_only = false
}
]
env_vars = {
TZ = var.timezone
APP_TIMEZONE = var.timezone
WINGS_UID = var.user_id
WINGS_GID = var.group_id
WINGS_USERNAME = "pelican"
}
userns_mode = "keep-id:uid=1000,gid=1000"
labels = {
"run.oci.keep_original_groups" = "1"
}
security_opts = [
"label:type:container_runtype_t"
]
}
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 = ["games"]
}
}
@@ -0,0 +1,34 @@
variable "image_tag" {
description = "The tag for the Pelican Wings container image. Default: latest"
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 = []
}
variable "user_id" {
description = "User ID for container permissions"
type = string
default = "1000"
}
variable "group_id" {
description = "Group ID for container permissions"
type = string
default = "1000"
}
variable "timezone" {
description = "Timezone for the container"
type = string
default = "Europe/Helsinki"
}
@@ -7,12 +7,12 @@ terraform {
}
locals {
container_name = "actualbudget"
image = "ghcr.io/actualbudget/actual"
image_tag = var.image_tag
env_file = "${path.module}/.env"
internal_port = 5006
container_name = "actualbudget"
image = "ghcr.io/actualbudget/actual"
image_tag = var.image_tag
env_file = "${path.module}/.env"
internal_port = 5006
default_volumes = [
{
host_path = "${var.volume_path}/data"
@@ -24,108 +24,108 @@ locals {
authentik_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/media"
container_path = "/media"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/media"
container_path = "/media"
read_only = false
},
{
host_path = "${var.volume_path}/${local.container_name}/custom-templates"
container_path = "/templates"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/custom-templates"
container_path = "/templates"
read_only = false
},
{
host_path = "${var.volume_path}/${local.container_name}/user_settings.py"
container_path = "/data/user_settings.py"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/user_settings.py"
container_path = "/data/user_settings.py"
read_only = false
}
]
redis_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/redis/data"
container_path = "/data"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/redis/data"
container_path = "/data"
read_only = false
},
]
postgres_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/postgres/data"
container_path = "/var/lib/postgresql/data"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/postgres/data"
container_path = "/var/lib/postgresql/data"
read_only = false
},
]
authentik_env_vars = {
AUTHENTIK_SECRET_KEY = provider::dotenv::get_by_key("AUTHENTIK_SECRET_KEY", local.env_file)
AUTHENTIK_REDIS__HOST = provider::dotenv::get_by_key("AUTHENTIK_REDIS__HOST", local.env_file)
AUTHENTIK_POSTGRESQL__HOST = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__HOST", local.env_file)
AUTHENTIK_POSTGRESQL__USER = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__USER", local.env_file)
AUTHENTIK_POSTGRESQL__NAME = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__NAME", local.env_file)
AUTHENTIK_POSTGRESQL__PASSWORD = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__PASSWORD", local.env_file)
AUTHENTIK_SECRET_KEY = provider::dotenv::get_by_key("AUTHENTIK_SECRET_KEY", local.env_file)
AUTHENTIK_REDIS__HOST = provider::dotenv::get_by_key("AUTHENTIK_REDIS__HOST", local.env_file)
AUTHENTIK_POSTGRESQL__HOST = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__HOST", local.env_file)
AUTHENTIK_POSTGRESQL__USER = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__USER", local.env_file)
AUTHENTIK_POSTGRESQL__NAME = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__NAME", local.env_file)
AUTHENTIK_POSTGRESQL__PASSWORD = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__PASSWORD", local.env_file)
}
postgres_env_vars = {
POSTGRES_PASSWORD = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__PASSWORD", local.env_file)
POSTGRES_USER = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__USER", local.env_file)
POSTGRES_DB = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__DB", local.env_file)
POSTGRES_PASSWORD = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__PASSWORD", local.env_file)
POSTGRES_USER = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__USER", local.env_file)
POSTGRES_DB = provider::dotenv::get_by_key("AUTHENTIK_POSTGRESQL__DB", local.env_file)
}
}
resource "local_file" "authentik_config_file" {
content = local.authentik_content
filename = "${var.volume_path}/${local.container_name}/user_settings.py"
}
resource "local_file" "authentik_config_file" {
content = local.authentik_content
filename = "${var.volume_path}/${local.container_name}/user_settings.py"
}
module "authentik_network" {
source = "../../01-networking/network-service"
name = "authentik-network"
subnet = "172.16.0.0/29"
subnet = "172.17.0.0/29"
driver = "bridge"
options = {
"isolate": false
"isolate" : false
}
}
module "authentik-postgres" {
source = "../../10-generic/docker-service"
container_name = local.postgres_container_name
image = local.postgres_image
tag = local.postgres_tag
volumes = local.postgres_volumes
env_vars = local.postgres_env_vars
networks = [module.authentik_network.name]
source = "../../10-generic/docker-service"
container_name = local.postgres_container_name
image = local.postgres_image
tag = local.postgres_tag
volumes = local.postgres_volumes
env_vars = local.postgres_env_vars
networks = [module.authentik_network.name]
}
module "authentik-redis" {
source = "../../10-generic/docker-service"
container_name = local.redis_container_name
image = local.redis_image
tag = local.redis_tag
volumes = local.redis_volumes
networks = [module.authentik_network.name]
source = "../../10-generic/docker-service"
container_name = local.redis_container_name
image = local.redis_image
tag = local.redis_tag
volumes = local.redis_volumes
networks = [module.authentik_network.name]
}
module "authentik-server" {
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.authentik_image
tag = local.authentik_tag
volumes = local.authentik_volumes
env_vars = local.authentik_env_vars
networks = concat([module.authentik_network.name], var.networks)
command = ["server"]
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.authentik_image
tag = local.authentik_tag
volumes = local.authentik_volumes
env_vars = local.authentik_env_vars
networks = concat([module.authentik_network.name], var.networks)
command = ["server"]
}
module "authentik-worker" {
source = "../../10-generic/docker-service"
container_name = "${local.container_name}-worker"
image = local.authentik_image
tag = local.authentik_tag
volumes = local.authentik_volumes
env_vars = local.authentik_env_vars
networks = [module.authentik_network.name]
command = ["worker"]
source = "../../10-generic/docker-service"
container_name = "${local.container_name}-worker"
image = local.authentik_image
tag = local.authentik_tag
volumes = local.authentik_volumes
env_vars = local.authentik_env_vars
networks = [module.authentik_network.name]
command = ["worker"]
}
output "service_definition" {
@@ -1,8 +1,8 @@
variable "image_tag" {
description = "The tag for the authentik container image. Default: 2025.8.1"
description = "The tag for the authentik container image. Default: 2025.10"
type = string
default = "2025.8.1"
default = "2025.10"
}
variable "redis_image_tag" {
@@ -7,14 +7,14 @@ terraform {
}
locals {
container_name = "coder"
postgres_container_name = "coder-postgres"
coder_image = "ghcr.io/coder/coder"
postgres_image = "docker.io/library/postgres"
coder_tag = var.image_tag
postgres_tag = var.postgres_image_tag
env_file = "${path.module}/.env"
coder_internal_port = 7080
container_name = "coder"
postgres_container_name = "coder-postgres"
coder_image = "ghcr.io/coder/coder"
postgres_image = "docker.io/library/postgres"
coder_tag = var.image_tag
postgres_tag = var.postgres_image_tag
env_file = "${path.module}/.env"
coder_internal_port = 7080
coder_volumes = [
{
@@ -33,19 +33,19 @@ locals {
]
coder_env_vars = {
CODER_PG_CONNECTION_URL = "postgresql://${provider::dotenv::get_by_key("POSTGRES_USER", local.env_file)}:${provider::dotenv::get_by_key("POSTGRES_PASSWORD", local.env_file)}@coder-postgres/${provider::dotenv::get_by_key("POSTGRES_DB", local.env_file)}?sslmode=disable"
CODER_HTTP_ADDRESS = provider::dotenv::get_by_key("CODER_HTTP_ADDRESS", local.env_file)
CODER_ACCESS_URL = provider::dotenv::get_by_key("CODER_ACCESS_URL", local.env_file)
CODER_PROXY_TRUSTED_HEADERS = provider::dotenv::get_by_key("CODER_PROXY_TRUSTED_HEADERS", local.env_file)
CODER_PROXY_TRUSTED_ORIGINS = provider::dotenv::get_by_key("CODER_PROXY_TRUSTED_ORIGINS", local.env_file)
CODER_DISABLE_PASSWORD_AUTH = provider::dotenv::get_by_key("CODER_DISABLE_PASSWORD_AUTH", local.env_file)
DOCKER_USER = provider::dotenv::get_by_key("DOCKER_USER", local.env_file)
CODER_PG_CONNECTION_URL = "postgresql://${provider::dotenv::get_by_key("POSTGRES_USER", local.env_file)}:${provider::dotenv::get_by_key("POSTGRES_PASSWORD", local.env_file)}@coder-postgres/${provider::dotenv::get_by_key("POSTGRES_DB", local.env_file)}?sslmode=disable"
CODER_HTTP_ADDRESS = provider::dotenv::get_by_key("CODER_HTTP_ADDRESS", local.env_file)
CODER_ACCESS_URL = provider::dotenv::get_by_key("CODER_ACCESS_URL", local.env_file)
CODER_PROXY_TRUSTED_HEADERS = provider::dotenv::get_by_key("CODER_PROXY_TRUSTED_HEADERS", local.env_file)
CODER_PROXY_TRUSTED_ORIGINS = provider::dotenv::get_by_key("CODER_PROXY_TRUSTED_ORIGINS", local.env_file)
CODER_DISABLE_PASSWORD_AUTH = provider::dotenv::get_by_key("CODER_DISABLE_PASSWORD_AUTH", local.env_file)
DOCKER_USER = provider::dotenv::get_by_key("DOCKER_USER", local.env_file)
}
postgres_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_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)
}
}
@@ -53,10 +53,10 @@ locals {
module "coder_network" {
source = "../../01-networking/network-service"
name = "coder-network"
subnet = "172.16.0.16/29"
subnet = "172.17.0.16/29"
driver = "bridge"
options = {
"isolate": false
"isolate" : false
}
}
@@ -82,10 +82,10 @@ module "coder" {
networks = concat([module.coder_network.name], var.networks)
restart_policy = "always"
userns_mode = "keep-id:uid=1000,gid=1000"
labels = {
labels = {
"run.oci.keep_original_groups" = "1"
}
security_opts = [
security_opts = [
"label:type:container_runtype_t"
]
}
@@ -0,0 +1,2 @@
FILEBROWSER_OIDC_CLIENT_ID=
FILEBROWSER_OIDC_CLIENT_SECRET=
@@ -0,0 +1,88 @@
terraform {
required_providers {
dotenv = {
source = "germanbrew/dotenv"
}
}
}
locals {
container_name = "fs-quantum"
fs_image = "docker.io/gtstef/filebrowser"
fs_tag = var.image_tag
env_file = "${path.module}/.env"
internal_port = 80
fs_env_vars = {
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
PORT = 80
FILEBROWSER_OIDC_CLIENT_ID = provider::dotenv::get_by_key("FILEBROWSER_OIDC_CLIENT_ID", local.env_file)
FILEBROWSER_OIDC_CLIENT_SECRET = provider::dotenv::get_by_key("FILEBROWSER_OIDC_CLIENT_SECRET", local.env_file)
}
fs_settings = <<-EOT
server:
sources:
- path: "/home/filebrowser/black"
name: "hdd"
- path: "/home/filebrowser/blue"
name: "ssd"
auth:
methods:
oidc:
enabled: true
issuerUrl: "https://authz.blackchaosnl.myaddr.dev/application/o/fs/"
scopes: "email openid profile groups"
userIdentifier: "preferred_username"
createUser: true
adminGroup: "admin"
groupsClaim: "groups"
password:
enabled: false
signup: false
EOT
}
resource "local_file" "fs_config_file" {
content = local.fs_settings
filename = "${var.volume_path}/${local.container_name}/config.yaml"
}
module "fs-quantum" {
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.fs_image
tag = local.fs_tag
volumes = [
{
host_path = "/mnt/storage"
container_path = "/home/filebrowser/black"
read_only = false
},
{
host_path = "/mnt/ssd"
container_path = "/home/filebrowser/blue"
read_only = false
},
{
host_path = "${var.volume_path}/${local.container_name}/config.yaml"
container_path = "/home/filebrowser/data/config.yaml"
read_only = true
}
]
env_vars = local.fs_env_vars
networks = concat(var.networks)
restart_policy = "always"
}
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 = ["fs"]
}
}
@@ -0,0 +1,34 @@
variable "image_tag" {
description = "The tag for the Filebrowser Quantum container image. Default: Latest"
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 = []
}
variable "user_id" {
description = "User ID for container permissions"
type = string
default = "1000"
}
variable "group_id" {
description = "Group ID for container permissions"
type = string
default = "1000"
}
variable "timezone" {
description = "Timezone for the container"
type = string
default = "Europe/Helsinki"
}
@@ -0,0 +1,8 @@
POSTGRES_USER=penpot
POSTGRES_PASSWORD=penpot
POSTGRES_DB=penpot
PENPOT_SECRET_KEY=
PENPOT_OIDC_CLIENT_ID=
PENPOT_OIDC_CLIENT_SECRET=
PENPOT_OIDC_BASE_URI=
PENPOT_OIDC_ROLES="admin user"
@@ -0,0 +1,156 @@
terraform {
required_providers {
dotenv = {
source = "germanbrew/dotenv"
}
}
}
locals {
container_name = "penpot"
penpot_backend_name = "penpot-backend"
penpot_exporter_name = "penpot-exporter"
postgres_container_name = "penpot-postgres"
valkey_container_name = "penpot-valkey"
penpot_frontend_image = "docker.io/penpotapp/frontend"
penpot_backend_image = "docker.io/penpotapp/backend"
penpot_exporter_image = "docker.io/penpotapp/exporter"
valkey_image = "docker.io/valkey/valkey"
postgres_image = "docker.io/library/postgres"
penpot_frontend_tag = var.image_tag
penpot_backend_tag = var.image_tag
penpot_exporter_tag = var.image_tag
valkey_tag = var.valkey_image_tag
postgres_tag = var.postgres_image_tag
env_file = "${path.module}/.env"
internal_port = 8080
penpot_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/assets"
container_path = "/opt/data/assets"
read_only = false
}
]
postgres_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/data"
container_path = "/var/lib/postgresql/data"
read_only = false
}
]
penpot_exporter_env_vars = {
PENPOT_SECRET_KEY = provider::dotenv::get_by_key("PENPOT_SECRET_KEY", local.env_file)
PENPOT_PUBLIC_URI = "http://${local.container_name}:${local.internal_port}"
PENPOT_REDIS_URI = "redis://${local.valkey_container_name}/0"
}
# Disable emails and enable OIDC since this is a private instanced managed with Authentik
penpot_frontend_env_vars = {
PENPOT_FLAGS = "disable-registration disable-email-verification disable-smtp enable-prepl-server enable-login-with-oidc"
}
penpot_backend_env_vars = {
PENPOT_SECRET_KEY = provider::dotenv::get_by_key("PENPOT_SECRET_KEY", local.env_file)
PENPOT_PREPL_HOST = "0.0.0.0"
PENPOT_DATABASE_URI = "postgresql://${local.postgres_container_name}/${try(provider::dotenv::get_by_key("POSTGRES_DB", local.env_file), "penpot")}"
PENPOT_DATABASE_USERNAME = provider::dotenv::get_by_key("POSTGRES_USER", local.env_file)
PENPOT_DATABASE_PASSWORD = provider::dotenv::get_by_key("POSTGRES_PASSWORD", local.env_file)
PENPOT_REDIS_URI = "redis://${local.valkey_container_name}/0"
PENPOT_OBJECTS_STORAGE_BACKEND = "fs"
PENPOT_OBJECTS_STORAGE_FS_DIRECTORY = "/opt/data/assets"
PENPOT_TELEMETRY_ENABLED = false
PENPOT_TELEMETRY_REFERER = ""
PENPOT_OIDC_CLIENT_ID = provider::dotenv::get_by_key("PENPOT_OIDC_CLIENT_ID", local.env_file)
PENPOT_OIDC_CLIENT_SECRET = provider::dotenv::get_by_key("PENPOT_OIDC_CLIENT_SECRET", local.env_file)
PENPOT_OIDC_BASE_URI = provider::dotenv::get_by_key("PENPOT_OIDC_BASE_URI", local.env_file)
PENPOT_OIDC_ROLES = provider::dotenv::get_by_key("PENPOT_OIDC_ROLES", local.env_file)
}
postgres_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)
}
}
module "penpot_network" {
source = "../../01-networking/network-service"
name = "penpot-network"
subnet = "172.17.0.32/29"
driver = "bridge"
options = {
"isolate" : false
}
}
module "penpot-postgres" {
source = "../../10-generic/docker-service"
container_name = local.postgres_container_name
image = local.postgres_image
tag = local.postgres_tag
volumes = local.postgres_volumes
env_vars = local.postgres_env_vars
networks = [module.penpot_network.name]
restart_policy = "always"
}
module "penpot-valkey" {
source = "../../10-generic/docker-service"
container_name = local.valkey_container_name
image = local.valkey_image
tag = local.valkey_tag
networks = [module.penpot_network.name]
restart_policy = "always"
}
module "penpot-exporter" {
source = "../../10-generic/docker-service"
container_name = local.penpot_exporter_name
image = local.penpot_exporter_image
tag = local.penpot_backend_tag
env_vars = local.penpot_exporter_env_vars
networks = [module.penpot_network.name]
restart_policy = "always"
}
module "penpot-backend" {
source = "../../10-generic/docker-service"
container_name = local.penpot_backend_name
image = local.penpot_backend_image
tag = local.penpot_backend_tag
volumes = local.penpot_volumes
env_vars = merge(local.penpot_frontend_env_vars, local.penpot_backend_env_vars)
networks = [module.penpot_network.name]
restart_policy = "always"
}
module "penpot" {
source = "../../10-generic/docker-service"
container_name = local.container_name
image = local.penpot_frontend_image
tag = local.penpot_frontend_tag
volumes = local.penpot_volumes
env_vars = local.penpot_frontend_env_vars
networks = concat([module.penpot_network.name], var.networks)
restart_policy = "always"
}
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 = ["penpot"]
}
}
@@ -0,0 +1,46 @@
variable "image_tag" {
description = "The tag for the coder container image. Default: Latest"
type = string
default = "latest"
}
variable "postgres_image_tag" {
description = "The tag for the postgres container image. Default: Latest"
type = string
default = "17-alpine"
}
variable "valkey_image_tag" {
description = "Valkey K/V store container image. Default: 8.1"
type = string
default = "8.1"
}
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 = []
}
variable "user_id" {
description = "User ID for container permissions"
type = string
default = "1000"
}
variable "group_id" {
description = "Group ID for container permissions"
type = string
default = "1000"
}
variable "timezone" {
description = "Timezone for the container"
type = string
default = "Europe/Helsinki"
}
@@ -7,11 +7,11 @@ terraform {
}
locals {
container_name = "qbittorrent"
qbittorrent_image = "lscr.io/linuxserver/qbittorrent"
qbittorrent_tag = var.image_tag
env_file = "${path.module}/.env"
qbittorrent_internal_port = 9080
container_name = "qbittorrent"
qbittorrent_image = "lscr.io/linuxserver/qbittorrent"
qbittorrent_tag = var.image_tag
env_file = "${path.module}/.env"
qbittorrent_internal_port = 9080
qbittorrent_volumes = [
{
@@ -27,11 +27,11 @@ locals {
]
qbittorrent_env_vars = {
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
WEBUI_PORT = provider::dotenv::get_by_key("WEBUI_PORT", local.env_file)
TORRENTING_PORT = provider::dotenv::get_by_key("TORRENTING_PORT", local.env_file)
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
WEBUI_PORT = provider::dotenv::get_by_key("WEBUI_PORT", local.env_file)
TORRENTING_PORT = provider::dotenv::get_by_key("TORRENTING_PORT", local.env_file)
}
}
@@ -4,8 +4,15 @@
# ---------------------------------------------------------------------------
# Setup OpenID
SOCIAL_PROVIDERS=
SOCIALACCOUNT_PROVIDERS=
SOCIAL_PROVIDERS=allauth.socialaccount.providers.openid_connect
SOCIALACCOUNT_PROVIDERS='{"openid_connect":{"APPS":[{"provider_id":"myidp","name":"My Provider","client_id":"...","secret":"...","settings":{"server_url":"https://idp.example.com/.well-known/openid-configuration"}}]}}'
ALLAUTH_TRUSTED_PROXY_COUNT=2
SOCIALACCOUNT_ONLY=1 # Fully disable local auth, we have no need for local auth.
SOCIALACCOUNT_LOGIN_ON_GET=1
SOCIALACCOUNT_AUTO_SIGNUP=1
SOCIALACCOUNT_EMAIL_AUTHENTICATION=1
SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT=1
ACCOUNT_EMAIL_VERIFICATION='none'
MEDIA_URL="<Your URL>/media/"
@@ -7,65 +7,72 @@ terraform {
}
locals {
container_name = "tandoor"
postgres_name = "tandoor-postgres"
tandoor_image = "docker.io/vabene1111/recipes"
postgres_image = "docker.io/library/postgres"
tandoor_tag = var.image_tag
postgres_tag = var.postgres_image_tag
env_file = "${path.module}/.env"
tandoor_internal_port = 80
container_name = "tandoor"
postgres_name = "tandoor-postgres"
tandoor_image = "docker.io/vabene1111/recipes"
postgres_image = "docker.io/library/postgres"
tandoor_tag = var.image_tag
postgres_tag = var.postgres_image_tag
env_file = "${path.module}/.env"
tandoor_internal_port = 80
tandoor_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/config"
container_path = "/config"
read_only = false
},{
}, {
host_path = "${var.volume_path}/${local.container_name}/cache"
container_path = "/cache"
read_only = false
},
]
postgres_volumes = [
postgres_volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/postgres/data"
container_path = "/var/lib/postgresql/data"
read_only = false
host_path = "${var.volume_path}/${local.container_name}/postgres/data"
container_path = "/var/lib/postgresql/data"
read_only = false
},
]
tandoor_env_vars = {
SOCIAL_PROVIDERS = provider::dotenv::get_by_key("SOCIAL_PROVIDERS", local.env_file)
SOCIALACCOUNT_PROVIDERS = provider::dotenv::get_by_key("SOCIALACCOUNT_PROVIDERS", local.env_file)
ENABLE_SIGNUP = provider::dotenv::get_by_key("ENABLE_SIGNUP", local.env_file)
MEDIA_URL = provider::dotenv::get_by_key("MEDIA_URL", local.env_file)
SECRET_KEY = provider::dotenv::get_by_key("SECRET_KEY", local.env_file)
DEBUG = provider::dotenv::get_by_key("DEBUG", local.env_file)
ALLOWED_HOSTS = provider::dotenv::get_by_key("ALLOWED_HOSTS", local.env_file)
DB_ENGINE = provider::dotenv::get_by_key("DB_ENGINE", local.env_file)
POSTGRES_HOST = provider::dotenv::get_by_key("POSTGRES_HOST", local.env_file)
POSTGRES_DB = provider::dotenv::get_by_key("POSTGRES_DB", local.env_file)
POSTGRES_PORT = provider::dotenv::get_by_key("POSTGRES_PORT", local.env_file)
POSTGRES_USER = provider::dotenv::get_by_key("POSTGRES_USER", local.env_file)
POSTGRES_PASSWORD = provider::dotenv::get_by_key("POSTGRES_PASSWORD", local.env_file)
ALLAUTH_TRUSTED_PROXY_COUNT = provider::dotenv::get_by_key("ALLAUTH_TRUSTED_PROXY_COUNT", local.env_file)
SOCIAL_PROVIDERS = provider::dotenv::get_by_key("SOCIAL_PROVIDERS", local.env_file)
SOCIALACCOUNT_PROVIDERS = provider::dotenv::get_by_key("SOCIALACCOUNT_PROVIDERS", local.env_file)
SOCIALACCOUNT_ONLY = provider::dotenv::get_by_key("SOCIALACCOUNT_ONLY", local.env_file)
SOCIALACCOUNT_LOGIN_ON_GET = provider::dotenv::get_by_key("SOCIALACCOUNT_LOGIN_ON_GET", local.env_file)
SOCIALACCOUNT_AUTO_SIGNUP = provider::dotenv::get_by_key("SOCIALACCOUNT_AUTO_SIGNUP", local.env_file)
SOCIALACCOUNT_EMAIL_AUTHENTICATION = provider::dotenv::get_by_key("SOCIALACCOUNT_EMAIL_AUTHENTICATION", local.env_file)
SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT = provider::dotenv::get_by_key("SOCIALACCOUNT_EMAIL_AUTHENTICATION_AUTO_CONNECT", local.env_file)
ACCOUNT_EMAIL_VERIFICATION = provider::dotenv::get_by_key("ACCOUNT_EMAIL_VERIFICATION", local.env_file)
ENABLE_SIGNUP = provider::dotenv::get_by_key("ENABLE_SIGNUP", local.env_file)
MEDIA_URL = provider::dotenv::get_by_key("MEDIA_URL", local.env_file)
SECRET_KEY = provider::dotenv::get_by_key("SECRET_KEY", local.env_file)
DEBUG = provider::dotenv::get_by_key("DEBUG", local.env_file)
ALLOWED_HOSTS = provider::dotenv::get_by_key("ALLOWED_HOSTS", local.env_file)
DB_ENGINE = provider::dotenv::get_by_key("DB_ENGINE", local.env_file)
POSTGRES_HOST = provider::dotenv::get_by_key("POSTGRES_HOST", local.env_file)
POSTGRES_DB = provider::dotenv::get_by_key("POSTGRES_DB", local.env_file)
POSTGRES_PORT = provider::dotenv::get_by_key("POSTGRES_PORT", local.env_file)
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_env_vars = {
POSTGRES_PASSWORD = provider::dotenv::get_by_key("POSTGRES_PASSWORD", local.env_file)
POSTGRES_USER = provider::dotenv::get_by_key("POSTGRES_USER", local.env_file)
POSTGRES_DB = provider::dotenv::get_by_key("POSTGRES_DB", local.env_file)
POSTGRES_PASSWORD = provider::dotenv::get_by_key("POSTGRES_PASSWORD", local.env_file)
POSTGRES_USER = provider::dotenv::get_by_key("POSTGRES_USER", local.env_file)
POSTGRES_DB = provider::dotenv::get_by_key("POSTGRES_DB", local.env_file)
}
}
module "tandoor_network" {
source = "../../01-networking/network-service"
name = "tandoor-network"
subnet = "172.16.0.24/29"
subnet = "172.17.0.24/29"
driver = "bridge"
options = {
"isolate": false
"isolate" : false
}
}
@@ -7,16 +7,16 @@ terraform {
}
locals {
container_name = "traccar"
traccar_image = "docker.io/traccar/traccar"
traccar_tag = var.image_tag
env_file = "${path.module}/.env"
traccar_internal_port = 8082
container_name = "traccar"
traccar_image = "docker.io/traccar/traccar"
traccar_tag = var.image_tag
env_file = "${path.module}/.env"
traccar_internal_port = 8082
traccar_env_vars = {
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
PUID = var.user_id
PGID = var.group_id
TZ = var.timezone
}
traccar_content = <<-EOT
@@ -52,16 +52,16 @@ module "traccar" {
container_name = local.container_name
image = local.traccar_image
tag = local.traccar_tag
volumes = [
volumes = [
{
host_path = "${var.volume_path}/${local.container_name}/logs"
container_path = "/opt/traccar/logs"
read_only = false
},{
}, {
host_path = "${var.volume_path}/${local.container_name}/data"
container_path = "/opt/traccar/data"
read_only = false
},{
}, {
host_path = "${var.volume_path}/${local.container_name}/traccar.xml"
container_path = "/opt/traccar/conf/traccar.xml"
read_only = true
+3 -1
View File
@@ -4,10 +4,12 @@ terraform {
source = "kreuzwerker/docker"
version = "~> 3.6.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.5.1"
}
dotenv = {
source = "germanbrew/dotenv"
version = "1.2.5"
@@ -17,4 +19,4 @@ terraform {
provider "docker" {
host = provider::dotenv::get_by_key("DOCKER_SOCK", "${path.module}/.env")
}
}
+29 -29
View File
@@ -15,61 +15,61 @@ module "infrastructure_int" {
driver = "bridge"
attachable = true
options = {
"isolate": false
"isolate" : false
}
}
module "jellyfin" {
source = "${local.module_dir}/20-services-entertainment/jellyfin-service"
source = "${local.module_dir}/20-services-entertainment/jellyfin-service"
volume_path = "${local.root_volume}/jellyfin"
networks = [module.infrastructure_int.name]
networks = [module.infrastructure_int.name]
}
module "calibre" {
source = "${local.module_dir}/20-services-entertainment/calibre-service"
volume_path = "${local.root_volume}/calibre"
networks = [module.infrastructure_int.name]
module "satisfactory" {
source = "${local.module_dir}/20-services-entertainment/satisfactory-service"
volume_path = "${local.root_volume}/satisfactory"
networks = [module.infrastructure_int.name]
}
module "pelican" {
source = "${local.module_dir}/20-services-entertainment/pelican-service"
volume_path = "${local.root_volume}/pelican"
networks = [module.infrastructure_int.name]
module "eco" {
source = "${local.module_dir}/20-services-entertainment/eco-service"
volume_path = "${local.root_volume}/eco"
networks = [module.infrastructure_int.name]
}
module "authentik" {
source = "${local.module_dir}/30-services-software/authentik-service"
source = "${local.module_dir}/30-services-software/authentik-service"
volume_path = "${local.root_volume}/authentik"
networks = [module.infrastructure_int.name]
networks = [module.infrastructure_int.name]
}
module "traccar" {
source = "${local.module_dir}/30-services-software/traccar-service"
source = "${local.module_dir}/30-services-software/traccar-service"
volume_path = "${local.root_volume}/traccar"
networks = [module.infrastructure_int.name]
networks = [module.infrastructure_int.name]
}
module "tandoor" {
source = "${local.module_dir}/30-services-software/tandoor-service"
source = "${local.module_dir}/30-services-software/tandoor-service"
volume_path = "${local.root_volume}/tandoor"
networks = [module.infrastructure_int.name]
}
module "qbittorrent" {
source = "${local.module_dir}/30-services-software/qbittorrent-service"
volume_path = "${local.root_volume}/qbittorrent"
networks = [module.infrastructure_int.name]
networks = [module.infrastructure_int.name]
}
module "coder" {
source = "${local.module_dir}/30-services-software/coder-service"
source = "${local.module_dir}/30-services-software/coder-service"
volume_path = "${local.root_volume}/coder"
networks = [module.infrastructure_int.name]
networks = [module.infrastructure_int.name]
}
module "actualbudget" {
source = "${local.module_dir}/30-services-software/actualbudget-service"
volume_path = "${local.root_volume}/actualbudget"
networks = [module.infrastructure_int.name]
module "penpot" {
source = "${local.module_dir}/30-services-software/penpot-service"
volume_path = "${local.root_volume}/penpot"
networks = [module.infrastructure_int.name]
}
module "fs-quantum" {
source = "${local.module_dir}/30-services-software/filesystem-service"
volume_path = "${local.root_volume}/fs-quantum"
networks = [module.infrastructure_int.name]
}
+5 -5
View File
@@ -2,18 +2,18 @@ output "service_definitions" {
description = "Service definitions for all services"
value = [
module.jellyfin.service_definition,
module.calibre.service_definition,
module.pelican.service_definition,
module.satisfactory.service_definition,
module.eco.service_definition,
module.authentik.service_definition,
module.traccar.service_definition,
module.tandoor.service_definition,
module.qbittorrent.service_definition,
module.coder.service_definition,
module.actualbudget.service_definition,
module.penpot.service_definition,
module.fs-quantum.service_definition,
]
}
output "infrastructure_int" {
description = "The internal infrastructure network"
value = module.infrastructure_int
value = module.infrastructure_int
}