# Port Management Guide Managing service ports across multiple hosts with the `m3ta.ports` module. ## Overview The port management module provides a centralized way to define service ports that can have host-specific overrides. This prevents port conflicts and makes it easy to manage services across multiple machines. ## Basic Usage ### Enable Port Management ```nix {config, ...}: { m3ta.ports = { enable = true; # Define default ports definitions = { nginx = 80; grafana = 3000; prometheus = 9090; homepage = 8080; }; # Define host-specific overrides hostOverrides = { laptop = { nginx = 8080; # Override on laptop homepage = 3001; # Override on laptop }; server = { homepage = 3002; # Override on server }; }; # Set current host (determines which overrides to use) currentHost = config.networking.hostName; }; } ``` ### Using Ports ```nix {config, ...}: { services.nginx = { enable = true; httpConfig = '' server { listen ${toString (config.m3ta.ports.get "nginx")}; root /var/www; } ''; }; services.grafana = { enable = true; settings.server.http_port = config.m3ta.ports.get "grafana"; }; } ``` ## Module Options ### `m3ta.ports.enable` Enable port management module. - Type: `boolean` - Default: `false` ### `m3ta.ports.definitions` Default port definitions. - Type: `attrsOf int` - Default: `{}` ```nix definitions = { nginx = 80; grafana = 3000; prometheus = 9090; }; ``` ### `m3ta.ports.hostOverrides` Host-specific port overrides. - Type: `attrsOf (attrsOf int)` - Default: `{}` ```nix hostOverrides = { laptop = { nginx = 8080; grafana = 3001; }; server = { grafana = 3002; }; }; ``` ### `m3ta.ports.currentHost` Current hostname. Determines which overrides to apply. - Type: `string` - Example: `config.networking.hostName` ```nix currentHost = "laptop"; # Use laptop overrides ``` ### `m3ta.ports.generateEnvVars` (Home Manager only) Generate environment variables from ports. - Type: `boolean` - Default: `false` (Home Manager) - NixOS: Not available When enabled, generates environment variables like: - `PORT_NGINX=8080` - `PORT_GRAFANA=3000` ## Functions ### `config.m3ta.ports.get "service"` Get port for a service with host-specific override. ```nix services.nginx = { port = config.m3ta.ports.get "nginx"; }; ``` If current host is `laptop` and `hostOverrides.laptop.nginx = 8080`, returns `8080`. If no override, returns default `80`. ### `config.m3ta.ports.getHostPorts "hostname"` Get all ports for a specific host. ```nix # Get all ports for laptop laptopPorts = config.m3ta.ports.getHostPorts "laptop"; # Returns: { nginx = 8080; grafana = 3000; ... } ``` ### `config.m3ta.ports.listServices` List all defined service names. ```nix allServices = config.m3ta.ports.listServices; # Returns: ["nginx" "grafana" "prometheus" "homepage"] ``` ## Examples ### NixOS Configuration ```nix {config, ...}: { # Define ports m3ta.ports = { enable = true; definitions = { nginx = 80; grafana = 3000; prometheus = 9090; loki = 3100; promtail = 9080; }; hostOverrides.laptop = { nginx = 8080; grafana = 3001; }; currentHost = config.networking.hostName; }; # Use ports services.nginx = { enable = true; httpConfig = '' server { listen ${toString (config.m3ta.ports.get "nginx")}; root /var/www; } ''; }; services.grafana = { enable = true; settings.server.http_port = config.m3ta.ports.get "grafana"; }; services.prometheus = { enable = true; port = config.m3ta.ports.get "prometheus"; }; services.loki = { enable = true; configuration.http_listen_port = config.m3ta.ports.get "loki"; }; } ``` ### Home Manager Configuration ```nix {config, ...}: { # Define ports m3ta.ports = { enable = true; definitions = { dev-server = 3000; nextjs = 3001; vite = 5173; }; hostOverrides.desktop = { vite = 5174; }; currentHost = "desktop"; generateEnvVars = true; # Generate env vars }; # Ports are now available as env vars # PORT_DEV_SERVER=3000 # PORT_NEXTJS=3001 # PORT_VITE=5174 home.sessionVariables = { DEV_PORT = toString (config.m3ta.ports.get "dev-server"); }; } ``` ### With Custom Modules Using ports with custom modules (e.g., `m3ta.mem0`): ```nix {config, ...}: { # Define ports m3ta.ports = { enable = true; definitions = { mem0 = 8000; qdrant = 6333; }; hostOverrides.laptop = { mem0 = 8080; }; currentHost = config.networking.hostName; }; # Use with mem0 module m3ta.mem0 = { enable = true; port = config.m3ta.ports.get "mem0"; # 8000 or 8080 on laptop }; # Use with qdrant service services.qdrant = { enable = true; port = config.m3ta.ports.get "qdrant"; }; } ``` ### Port File Generation Generate a JSON file with all ports: ```nix {config, pkgs, ...}: { m3ta.ports = { enable = true; definitions = { service1 = 80; service2 = 443; }; currentHost = config.networking.hostName; }; # Generate port file environment.etc."m3ta/ports.json".text = builtins.toJSON ( config.m3ta.ports.getHostPorts config.networking.hostName ); } ``` ## Advanced Usage ### Conditional Configuration ```nix {config, ...}: { services.nginx = { enable = true; # Only open firewall if binding to non-localhost httpConfig = let port = config.m3ta.ports.get "nginx"; in '' server { listen ${toString port}; } ''; }; networking.firewall.allowedTCPPorts = if config.m3ta.ports.get "nginx" == 80 then [80] else []; } ``` ### Port Ranges ```nix definitions = { service-start = 8000; service-end = 8999; }; # Use in config services.my-app = { portRange = [ config.m3ta.ports.get "service-start" config.m3ta.ports.get "service-end" ]; }; ``` ### Dynamic Port Allocation ```nix {config, ...}: { m3ta.ports = { enable = true; definitions = { # Reserve port ranges app-range-start = 9000; app-range-end = 9999; }; currentHost = config.networking.hostName; }; # Calculate next available port services.my-app = { port = config.m3ta.ports.get "app-range-start" + 0; }; services.my-other-app = { port = config.m3ta.ports.get "app-range-start" + 1; }; } ``` ## Best Practices ### Use Descriptive Service Names ```nix # Good definitions = { nginx = 80; grafana = 3000; prometheus-ui = 9090; prometheus-push = 9091; }; # Avoid definitions = { p1 = 80; p2 = 3000; p3 = 9090; }; ``` ### Group Related Services ```nix definitions = { # Monitoring stack grafana = 3000; prometheus = 9090; loki = 3100; promtail = 9080; # Web services nginx = 80; homepage = 8080; # Databases postgres = 5432; redis = 6379; qdrant = 6333; }; ``` ### Document Overrides ```nix hostOverrides = { # Laptop: Running multiple dev servers, use higher ports laptop = { nginx = 8080; dev-server = 3000; }; # Server: Production, use standard ports server = { nginx = 80; dev-server = null; # Disable on server }; }; ``` ### Handle Missing Ports ```nix services.some-service = { enable = true; port = config.m3ta.ports.get "some-service" or 8080; }; ``` ## Troubleshooting ### Service Not Found Error: `Service "foo" not defined` Solution: Add service to `definitions`: ```nix definitions = { foo = 8080; }; ``` ### Current Host Not Set Error: `currentHost not set` Solution: Set `currentHost`: ```nix currentHost = config.networking.hostName; ``` ### Port Conflict Issue: Two services trying to use same port. Solution: Define both in port management: ```nix definitions = { service1 = 8080; service2 = 8081; # Different port }; ``` ## Migration from Hardcoded Ports ### Before ```nix services.nginx = { enable = true; httpConfig = '' server { listen 80; } ''; }; services.grafana = { enable = true; settings.server.http_port = 3000; }; ``` ### After ```nix m3ta.ports = { enable = true; definitions = { nginx = 80; grafana = 3000; }; currentHost = config.networking.hostName; }; services.nginx = { enable = true; httpConfig = '' server { listen ${toString (config.m3ta.ports.get "nginx")}; } ''; }; services.grafana = { enable = true; settings.server.http_port = config.m3ta.ports.get "grafana"; }; ``` ## Next Steps - [Architecture](../ARCHITECTURE.md) - Understanding the library functions - [Using Modules](./using-modules.md) - Using modules with port management - [Contributing](../CONTRIBUTING.md) - Code style and guidelines