- Update package description and fix mainProgram typo - Rewrite documentation to describe project switching, not process viewing - Add PROJECT_FOLDERS configuration and usage examples - Update all references across docs (README, guides, module overviews)
8.7 KiB
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
{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
{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:
{}
definitions = {
nginx = 80;
grafana = 3000;
prometheus = 9090;
};
m3ta.ports.hostOverrides
Host-specific port overrides.
- Type:
attrsOf (attrsOf int) - Default:
{}
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
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=8080PORT_GRAFANA=3000
Functions
config.m3ta.ports.get "service"
Get port for a service with host-specific override.
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.
# 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.
allServices = config.m3ta.ports.listServices;
# Returns: ["nginx" "grafana" "prometheus" "homepage"]
Examples
NixOS Configuration
{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
{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):
{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:
{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
{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
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
{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
# Good
definitions = {
nginx = 80;
grafana = 3000;
prometheus-ui = 9090;
prometheus-push = 9091;
};
# Avoid
definitions = {
p1 = 80;
p2 = 3000;
p3 = 9090;
};
Group Related Services
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
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
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:
definitions = {
foo = 8080;
};
Current Host Not Set
Error: currentHost not set
Solution: Set currentHost:
currentHost = config.networking.hostName;
Port Conflict
Issue: Two services trying to use same port.
Solution: Define both in port management:
definitions = {
service1 = 8080;
service2 = 8081; # Different port
};
Migration from Hardcoded Ports
Before
services.nginx = {
enable = true;
httpConfig = ''
server {
listen 80;
}
'';
};
services.grafana = {
enable = true;
settings.server.http_port = 3000;
};
After
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 - Understanding the library functions
- Using Modules - Using modules with port management
- Contributing - Code style and guidelines