Kieran's opinionated (and probably slightly dumb) nix config
at main 101 lines 4.1 kB view raw view rendered
1# mkService 2 3`modules/lib/mkService.nix` is the service factory used by most atelier services. It takes a set of parameters and returns a NixOS module with standardized options, systemd service, Caddy reverse proxy, and backup integration. 4 5## Factory parameters 6 7| Parameter | Type | Default | Description | 8|-----------|------|---------|-------------| 9| `name` | string | *required* | Service identity — used for user, group, systemd unit, and option namespace | 10| `description` | string | `"<name> service"` | Human-readable description | 11| `defaultPort` | int | `3000` | Default port if not overridden in config | 12| `runtime` | string | `"bun"` | `"bun"`, `"node"`, or `"custom"` | 13| `entryPoint` | string | `"src/index.ts"` | Script to run (ignored if `startCommand` is set) | 14| `startCommand` | string | `null` | Override the full start command | 15| `extraOptions` | attrset | `{}` | Additional NixOS options for this service | 16| `extraConfig` | function | `cfg: {}` | Additional NixOS config when enabled (receives the service config) | 17 18## Options 19 20Every mkService module creates options under `atelier.services.<name>`: 21 22### Core 23 24| Option | Type | Default | Description | 25|--------|------|---------|-------------| 26| `enable` | bool | `false` | Enable the service | 27| `domain` | string | *required* | Domain for Caddy reverse proxy | 28| `port` | port | `defaultPort` | Port the service listens on | 29| `dataDir` | path | `"/var/lib/<name>"` | Data storage directory | 30| `secretsFile` | path or null | `null` | Agenix secrets environment file | 31| `repository` | string or null | `null` | Git repo URL — cloned once on first start | 32| `healthUrl` | string or null | `null` | Health check URL for monitoring | 33| `environment` | attrset | `{}` | Additional environment variables | 34 35### Data declarations 36 37Used by the backup system to automatically discover what to back up. 38 39| Option | Type | Default | Description | 40|--------|------|---------|-------------| 41| `data.sqlite` | string or null | `null` | SQLite database path (WAL checkpoint + stop/start during backup) | 42| `data.postgres` | string or null | `null` | PostgreSQL database name (pg_dump during backup) | 43| `data.files` | list of strings | `[]` | Additional file paths to back up | 44| `data.exclude` | list of strings | `["*.log", "node_modules", ...]` | Glob patterns to exclude | 45 46### Caddy 47 48| Option | Type | Default | Description | 49|--------|------|---------|-------------| 50| `caddy.enable` | bool | `true` | Enable Caddy reverse proxy | 51| `caddy.extraConfig` | string | `""` | Additional Caddy directives | 52| `caddy.rateLimit.enable` | bool | `false` | Enable rate limiting | 53| `caddy.rateLimit.events` | int | `60` | Requests per window | 54| `caddy.rateLimit.window` | string | `"1m"` | Rate limit time window | 55 56## What it sets up 57 58- **System user and group** — dedicated user in the `services` group with sudo for `systemctl restart/stop/start/status` 59- **Systemd service** — `ExecStartPre` creates dirs as root, `preStart` clones repo and installs deps, `ExecStart` runs the application 60- **Caddy virtual host** — TLS via Cloudflare DNS challenge, reverse proxy to localhost port 61- **Port conflict detection** — assertions prevent two services from binding the same port 62- **Security hardening** — `NoNewPrivileges`, `ProtectSystem=strict`, `ProtectHome`, `PrivateTmp` 63 64## Example 65 66Minimal service module: 67 68```nix 69let 70 mkService = import ../../lib/mkService.nix; 71in 72mkService { 73 name = "myapp"; 74 description = "My application"; 75 defaultPort = 3000; 76 runtime = "bun"; 77 entryPoint = "src/index.ts"; 78 79 extraConfig = cfg: { 80 systemd.services.myapp.serviceConfig.Environment = [ 81 "DATABASE_PATH=${cfg.dataDir}/data/app.db" 82 ]; 83 84 atelier.services.myapp.data = { 85 sqlite = "${cfg.dataDir}/data/app.db"; 86 }; 87 }; 88} 89``` 90 91Then enable in the machine config: 92 93```nix 94atelier.services.myapp = { 95 enable = true; 96 domain = "myapp.dunkirk.sh"; 97 repository = "https://github.com/taciturnaxolotl/myapp"; 98 secretsFile = config.age.secrets.myapp.path; 99 healthUrl = "https://myapp.dunkirk.sh/health"; 100}; 101```