From c6b3a16ea460d716a9536b090405957c5c27ec34 Mon Sep 17 00:00:00 2001 From: Yuris Cakranegara Date: Sun, 8 Jun 2025 18:56:45 +1000 Subject: [PATCH] feat: add n8n --- modules/20-services-apps/n8n/.env.example | 10 ++ modules/20-services-apps/n8n/init-data.sh | 13 +++ modules/20-services-apps/n8n/main.tf | 127 ++++++++++++++++++++++ services/main.tf | 6 + services/outputs.tf | 3 +- 5 files changed, 158 insertions(+), 1 deletion(-) create mode 100644 modules/20-services-apps/n8n/.env.example create mode 100644 modules/20-services-apps/n8n/init-data.sh create mode 100644 modules/20-services-apps/n8n/main.tf diff --git a/modules/20-services-apps/n8n/.env.example b/modules/20-services-apps/n8n/.env.example new file mode 100644 index 0000000..18d8c6a --- /dev/null +++ b/modules/20-services-apps/n8n/.env.example @@ -0,0 +1,10 @@ +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=* diff --git a/modules/20-services-apps/n8n/init-data.sh b/modules/20-services-apps/n8n/init-data.sh new file mode 100644 index 0000000..b0e85d0 --- /dev/null +++ b/modules/20-services-apps/n8n/init-data.sh @@ -0,0 +1,13 @@ +#!/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 new file mode 100644 index 0000000..5f915fd --- /dev/null +++ b/modules/20-services-apps/n8n/main.tf @@ -0,0 +1,127 @@ +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 = [] +} + +locals { + container_name = "n8n" + database_name = "n8n-postgres" + n8n_image = "docker.n8n.io/n8nio/n8n" + database_image = "postgres" + n8n_tag = var.image_tag != "" ? var.image_tag : "latest" + database_tag = "16" + 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) + } + + # 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" + } +} + +# 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 + env_vars = local.database_env_vars + networks = var.networks + monitoring = local.monitoring + healthcheck = local.database_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 + env_vars = local.n8n_env_vars + networks = 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.n8n_internal_port + endpoint = "http://${local.container_name}:${local.n8n_internal_port}" + subdomains = ["n8n"] + } +} diff --git a/services/main.tf b/services/main.tf index 59aef35..ddac48b 100644 --- a/services/main.tf +++ b/services/main.tf @@ -52,3 +52,9 @@ module "pterodactyl_wings" { volume_path = "${local.volume_host}/pterodactyl/wings" 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] +} diff --git a/services/outputs.tf b/services/outputs.tf index 3209f8e..85ddcd6 100644 --- a/services/outputs.tf +++ b/services/outputs.tf @@ -9,7 +9,8 @@ output "service_definitions" { module.linkwarden.service_definition, module.ntfy.service_definition, module.pterodactyl_wings.service_definition, - module.pterodactyl_panel.service_definition + module.pterodactyl_panel.service_definition, + module.n8n.service_definition ] }