+ portUtils +metabase

This commit is contained in:
m3tam3re 2025-09-29 15:44:25 +02:00
parent 7cf5f7d06f
commit c11847206f
18 changed files with 317 additions and 109 deletions

View File

@ -16,6 +16,9 @@
litellm-env = {
file = ../../secrets/litellm-env.age;
};
metabase-env = {
file = ../../secrets/metabase-env.age;
};
n8n-env = {
file = ../../secrets/n8n-env.age;
};

View File

@ -1,25 +1,34 @@
{config, ...}: {
virtualisation.oci-containers.containers."baserow" = {
{
config,
lib,
...
}: let
serviceName = "baserow";
portUtils = import ../../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
virtualisation.oci-containers.containers.${serviceName} = {
image = "docker.io/baserow/baserow:1.34.5";
environmentFiles = [config.age.secrets.baserow-env.path];
ports = ["127.0.0.1:3050:80"];
ports = ["127.0.0.1:${toString servicePort}:80"];
volumes = ["baserow_data:/baserow/data"];
extraOptions = ["--add-host=postgres:10.89.0.1" "--ip=10.89.0.10" "--network=web"];
};
# Traefik configuration specific to baserow
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.baserow.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:3050/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.baserow = {
routers.${serviceName} = {
rule = "Host(`br.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "baserow";
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -1,8 +1,13 @@
{
config,
lib,
pkgs,
...
}: let
serviceName = "librechat-dev";
portUtils = import ../../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
ragApiDevPort = portUtils.getPort "rag-api-dev" "AZ-CLD-1";
envFileDev = config.age.secrets.librechat-env-dev.path;
in {
virtualisation.oci-containers = {
@ -29,7 +34,7 @@ in {
environmentFiles = [envFileDev];
dependsOn = ["meilisearch-dev"];
extraOptions = ["--add-host=postgres:10.89.1.1" "--ip=10.89.1.21" "--network=web-dev"];
ports = ["127.0.0.1:8100:8000"];
ports = ["127.0.0.1:${toString ragApiDevPort}:8000"];
};
containers.mongodb-dev = {
@ -42,10 +47,10 @@ in {
extraOptions = ["--ip=10.89.1.22" "--network=web-dev"];
};
containers.librechat-dev = {
containers.${serviceName} = {
image = "ghcr.io/danny-avila/librechat-dev-api:latest";
autoStart = false;
ports = ["127.0.0.1:3141:3080"];
ports = ["127.0.0.1:${toString servicePort}:3080"];
dependsOn = ["mongodb-dev" "rag_api-dev" "meilisearch-dev"];
environment = {
HOST = "0.0.0.0";
@ -66,15 +71,24 @@ in {
};
};
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.librechat-dev.loadBalancer.servers = [{url = "http://localhost:3141/";}];
routers.librechat-dev = {
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:${toString servicePort}/";
}
];
routers.${serviceName} = {
rule = "Host(`chat-dev.az-gruppe.com`)";
tls.certResolver = "ionos";
service = "librechat-dev";
tls = {
certResolver = "ionos";
};
service = serviceName;
entrypoints = "websecure";
};
};
environment.systemPackages = [
(pkgs.writeShellScriptBin "librechat-dev" ''
#!/usr/bin/env bash

View File

@ -1,8 +1,13 @@
{
config,
lib,
pkgs,
...
}: let
serviceName = "librechat";
portUtils = import ../../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
ragApiPort = portUtils.getPort "rag-api" "AZ-CLD-1";
envFile = config.age.secrets.librechat-env.path;
in {
virtualisation.oci-containers = {
@ -29,7 +34,7 @@ in {
environmentFiles = [envFile];
dependsOn = ["meilisearch"];
extraOptions = ["--add-host=postgres:10.89.0.1" "--ip=10.89.0.21" "--network=web"];
ports = ["127.0.0.1:8000:8000"]; # optional: expose to host for debugging
ports = ["127.0.0.1:${toString ragApiPort}:8000"];
};
containers.mongodb = {
@ -44,10 +49,10 @@ in {
extraOptions = ["--ip=10.89.0.22" "--network=web"];
};
containers.librechat = {
containers.${serviceName} = {
image = "ghcr.io/danny-avila/librechat-dev-api:latest";
autoStart = true;
ports = ["127.0.0.1:3040:3080"];
ports = ["127.0.0.1:${toString servicePort}:3080"];
dependsOn = ["mongodb" "rag_api" "meilisearch"];
environment = {
HOST = "0.0.0.0";
@ -93,12 +98,20 @@ in {
timerConfig.RandomizedDelaySec = "15m";
};
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.librechat.loadBalancer.servers = [{url = "http://localhost:3040/";}];
routers.librechat = {
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:${toString servicePort}/";
}
];
routers.${serviceName} = {
rule = "Host(`chat.az-gruppe.com`)";
tls.certResolver = "ionos";
service = "librechat";
tls = {
certResolver = "ionos";
};
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -1,11 +1,15 @@
{
config,
pkgs,
lib,
...
}: {
virtualisation.oci-containers.containers.litellm = {
}: let
serviceName = "litellm";
portUtils = import ../../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
virtualisation.oci-containers.containers.${serviceName} = {
image = "ghcr.io/berriai/litellm:main-stable";
ports = ["127.0.0.1:4000:4000"];
ports = ["127.0.0.1:${toString servicePort}:4000"];
environmentFiles = [config.age.secrets.litellm-env.path];
environment = {
ANONYMIZED_TELEMETRY = "False";
@ -18,11 +22,18 @@
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.litellm.loadBalancer.servers = [{url = "http://127.0.0.1:4000/";}];
routers.litellm = {
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:${toString servicePort}/";
}
];
routers.${serviceName} = {
rule = "Host(`llm.az-gruppe.com`)";
tls.certResolver = "ionos";
service = "litellm";
tls = {
certResolver = "ionos";
};
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -1,27 +1,32 @@
{
virtualisation.oci-containers.containers.portainer = {
{lib, ...}: let
serviceName = "portainer";
portUtils = import ../../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
virtualisation.oci-containers.containers.${serviceName} = {
image = "docker.io/portainer/portainer-ce:latest";
ports = ["127.0.0.1:9000:9000"];
ports = ["127.0.0.1:${toString servicePort}:9000"];
volumes = [
"/etc/localtime:/etc/localtime:ro"
"/run/podman/podman.sock:/var/run/docker.sock:ro"
"portainer_data:/data"
];
};
# Traefik configuration specific to baserow
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.portainer.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:9000/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.portainer = {
routers.${serviceName} = {
rule = "Host(`pt.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "portainer";
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -1,10 +1,14 @@
{
services.gitea = {
{lib, ...}: let
serviceName = "gitea";
portUtils = import ../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
services.${serviceName} = {
enable = true;
settings = {
server = {
ROOT_URL = "https://git.az-gruppe.com";
HTTP_PORT = 3030;
HTTP_PORT = servicePort;
};
mailer.SENDMAIL_PATH = "/run/wrappers/bin/sendmail";
service.DISABLE_REGISTRATION = true;
@ -17,20 +21,21 @@
backupDir = "/var/backup/gitea";
};
};
# Traefik configuration specific to gitea
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.gitea.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:3030/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.gitea = {
routers.${serviceName} = {
rule = "Host(`git.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "gitea";
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -0,0 +1,36 @@
{
config,
lib,
...
}: let
serviceName = "metabase";
portUtils = import ../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
services.${serviceName} = {
enable = true;
listen.port = servicePort;
};
systemd.services.${serviceName}.serviceConfig = {
EnvironmentFile = config.age.secrets.metabase-env.path;
};
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:${toString servicePort}/";
}
];
routers.${serviceName} = {
rule = "Host(`kpi.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = serviceName;
entrypoints = "websecure";
};
};
}

View File

@ -1,25 +1,35 @@
{config, ...}: {
services.n8n = {
{
config,
lib,
...
}: let
serviceName = "n8n";
portUtils = import ../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
services.${serviceName} = {
enable = true;
webhookUrl = "https://wf.az-group.com";
webhookUrl = "https://wf.az-gruppe.com";
};
systemd.services.n8n.serviceConfig = {
EnvironmentFile = ["${config.age.secrets.n8n-env.path}"];
systemd.services.${serviceName}.serviceConfig = {
EnvironmentFile = config.age.secrets.n8n-env.path;
};
# Traefik configuration specific to n8n
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.n8n.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:5678/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.n8n = {
routers.${serviceName} = {
rule = "Host(`wf.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "n8n";
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -1,28 +1,36 @@
{config, ...}: {
services.ntfy-sh = {
enable = true;
settings.base-url = "https://ping.az-gruppe.com";
settings = {
listen-http = ":3033";
auth-file = "/var/lib/ntfy-sh/user.db";
auth-default-access = "deny-all";
};
{
config,
lib,
...
}: let
serviceName = "ntfy-sh";
portUtils = import ../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort "ntfy-sh" "AZ-CLD-1";
in {
services.${serviceName} = {
enable = true;
settings = {
base-url = "https://ping.az-gruppe.com";
listen-http = ":${toString servicePort}";
auth-file = "/var/lib/ntfy-sh/user.db";
auth-default-access = "deny-all";
};
};
# Traefik configuration specific to littlelink
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.ntfy-sh.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:3033/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.ntfy-sh = {
routers.${serviceName} = {
rule = "Host(`ping.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "ntfy-sh";
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -1,7 +1,15 @@
{config, ...}: {
services.outline = {
{
config,
lib,
...
}: let
serviceName = "outline";
portUtils = import ../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
services.${serviceName} = {
enable = true;
port = 3031;
port = servicePort;
publicUrl = "https://wiki.az-gruppe.com";
databaseUrl = "postgresql://outline:outline@127.0.0.1:5432/outline";
storage = {
@ -13,23 +21,25 @@
accessKey = "CRT7V4HR5CG9NHICD2WW";
};
};
systemd.services.outline.serviceConfig = {
EnvironmentFile = ["${config.age.secrets.outline-env.path}"];
systemd.services.${serviceName}.serviceConfig = {
EnvironmentFile = config.age.secrets.outline-env.path;
};
# Traefik configuration specific to littlelink
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.outline.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:3031/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.outline = {
routers.${serviceName} = {
rule = "Host(`wiki.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "outline";
service = serviceName;
entrypoints = "websecure";
};
};

View File

@ -45,6 +45,7 @@
authentication = pkgs.lib.mkOverride 10 ''
# Local connections (Unix socket)
local all postgres peer
local metabase metabase scram-sha-256
local n8n n8n scram-sha-256
local outline outline scram-sha-256
local vaultwarden vaultwarden scram-sha-256
@ -57,6 +58,9 @@
host outline outline 127.0.0.1/32 scram-sha-256
host outline outline ::1/128 scram-sha-256
host metabase metabase 127.0.0.1/32 scram-sha-256
host metabase metabase ::1/128 scram-sha-256
host n8n n8n 127.0.0.1/32 scram-sha-256
host n8n n8n ::1/128 scram-sha-256
@ -82,7 +86,7 @@
services.postgresqlBackup = {
enable = true;
startAt = "03:10:00";
databases = ["baserow" "kestra" "librechat_rag" "n8n" "outline" "vaultwarden" "zammad"];
databases = ["baserow" "kestra" "librechat_rag" "metabase" "n8n" "outline" "vaultwarden" "zammad"];
};
services.pgadmin = {
enable = true;

View File

@ -1,30 +1,37 @@
{config, ...}: {
services.vaultwarden = {
{
config,
lib,
...
}: let
serviceName = "vaultwarden";
portUtils = import ../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
services.${serviceName} = {
enable = true;
dbBackend = "postgresql";
config = {
ROCKET_ADDRESS = "127.0.0.1";
ROCKET_PORT = 3032;
ROCKET_PORT = servicePort;
};
environmentFile = "${config.age.secrets.vaultwarden-env.path}";
environmentFile = config.age.secrets.vaultwarden-env.path;
};
# Traefik configuration for headscale
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.vaultwarden.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:3032/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.vaultwarden = {
routers.${serviceName} = {
rule = "Host(`pw.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "vaultwarden";
service = serviceName;
entrypoints = "websecure";
};
};
}

View File

@ -1,33 +1,39 @@
{config, ...}:{
services = {
zammad = {
enable = true;
openPorts = false;
port = 3034;
secretKeyBaseFile = "${config.age.secrets.zammad-secret.path}";
database = {
createLocally = false;
port = 5432;
host = "127.0.0.1";
passwordFile = "${config.age.secrets.zammad-pw.path}";
};
{
config,
lib,
...
}: let
serviceName = "zammad";
portUtils = import ../../../lib/port-utils.nix {inherit lib;};
servicePort = portUtils.getPort serviceName "AZ-CLD-1";
in {
services.${serviceName} = {
enable = true;
openPorts = false;
port = servicePort;
secretKeyBaseFile = config.age.secrets.zammad-secret.path;
database = {
createLocally = false;
port = 5432;
host = "127.0.0.1";
passwordFile = config.age.secrets.zammad-pw.path;
};
};
# Traefik configuration specific to littlelink
# Traefik configuration
services.traefik.dynamicConfigOptions.http = {
services.zammad.loadBalancer.servers = [
services.${serviceName}.loadBalancer.servers = [
{
url = "http://localhost:3034/";
url = "http://localhost:${toString servicePort}/";
}
];
routers.zammad = {
routers.${serviceName} = {
rule = "Host(`help.az-gruppe.com`)";
tls = {
certResolver = "ionos";
};
service = "zammad";
service = serviceName;
entrypoints = "websecure";
};
};

11
lib/port-utils.nix Normal file
View File

@ -0,0 +1,11 @@
{lib}: let
ports = import ./ports.nix;
in {
# Get port for a service, with optional host-specific override
getPort = service: host:
ports.hostPorts.${host}.${service} or ports.ports.${service};
# Get all ports for a specific host
getHostPorts = host:
lib.mapAttrs (_: port: port) (ports.ports // (ports.hostPorts.${host} or {}));
}

48
lib/ports.nix Normal file
View File

@ -0,0 +1,48 @@
{
ports = {
# Infrastructure
traefik = {
http = 80;
https = 443;
};
# Core services (3000-3099 range)
gitea = 3030;
outline = 3031;
vaultwarden = 3032;
baserow = 3050;
zammad = 3034;
metabase = 3013;
ntfy-sh = 3033;
# Docker services (3100-3199 range)
librechat = 3040;
librechat-dev = 3141;
rag-api = 8000;
rag-api-dev = 8100;
litellm = 4000;
# Workflow/automation (5000-5999 range)
n8n = 5678;
kestra = 5080;
# Management tools (9000-9999 range)
portainer = 9000;
};
# Host-specific port allocations
hostPorts = {
AZ-CLD-1 = {
# Development environment gets higher port ranges
baserow = 3050;
librechat-dev = 3141;
rag-api-dev = 8100;
};
AZ-PRM-1 = {
# Production gets clean base ports
baserow = 3051; # Changed to avoid conflict
kestra = 5080; # Changed to avoid conflict
};
};
}

View File

@ -18,6 +18,7 @@ in {
"secrets/librechat-env.age".publicKeys = systems ++ users;
"secrets/librechat-env-dev.age".publicKeys = systems ++ users;
"secrets/litellm-env.age".publicKeys = systems ++ users;
"secrets/metabase-env.age".publicKeys = systems ++ users;
"secrets/n8n-env.age".publicKeys = systems ++ users;
"secrets/n8n-db.age".publicKeys = systems ++ users;
"secrets/outline-env.age".publicKeys = systems ++ users;

7
secrets/metabase-env.age Normal file
View File

@ -0,0 +1,7 @@
age-encryption.org/v1
-> ssh-ed25519 ZhVseg E0jqjOUyh9gHafLVG7jbvQ/oJUZbpzdjwzC7qzhqoUY
9ChTLvwmesarLHW8gZLFhWwToqFeM4uJliNmeBjrjQI
-> ssh-ed25519 CSMyhg i1pnf2ajQTO5dI+5z1wVeHEz8sEB6QeBj4dLlN2t5lQ
7KPBw1oiFQdiXJZRp0uj48cinuxQN5CFOZcMrlPUybc
--- MZwSQLl95AoXx1q1q/1yXNZ4uNT5OP7r5J354a3F440
ÉÞ'ˆr㎘# óBÕbÉÛ×!°‘¨¡¥ 90+צËâ B3½ã["Õ|í÷þ¢½Kìoˆ¬üËܪµÌ¢‡Ìó]d¢_>Ÿæ#åâ¸)F©ÙÈшŒW±S8VHïKWÛʲ¸ªé§ì’®-­É›™~ïqG>m(öÈØmü4ð±3·Ô2Åk±_ÔIÏV`:à;ú4uWðê=ŠÆ<C5A0>g°ÙN