Tutorial 09
Typed package options
In earlier tutorials, you passed config = { message = "..."; }; to packages.echo-server to customize the HTTP response body.
In this tutorial, you will look at the package author’s side of that contract. Using tutorials/09-typed-options, you will add typed options to the local reverse-proxy package (proxyPath and maxBodySize), use those values inside build, override them in cluster.nix, and see how lib.types catches invalid configuration at evaluation time.
Prerequisites
Section titled “Prerequisites”- Finish the cross-namespace dependency tutorial.
- Keep the same
kix-examplescheckout.
Declare Typed options In The Package
Section titled “Declare Typed options In The Package”Open tutorials/09-typed-options/reverse-proxy-package.nix. Unlike the tutorial 06 version of the reverse proxy, this package is written as a function { scope, lib, kix }: { ... } so it can use lib.mkOption and lib.types to declare an options section:
34 options = {35 proxyPath = lib.mkOption {36 # `lib.types.strMatching` runs the regex at module-merge time;37 # if `proxyPath = "api"` (no leading slash) is set, you get a38 # type error from the cluster build, not a nginx 404 at runtime.39 type = lib.types.strMatching "^/.*$";40 default = "/";41 description = "URL path this proxy forwards. Must start with `/`.";42 example = "/api/";43 };44
45 maxBodySize = lib.mkOption {46 # nginx body-size format: number with optional k/K/m/M/g/G suffix.47 type = lib.types.strMatching "^[0-9]+[kKmMgG]?$";48 default = "1m";49 description = ''50 Max request body size, in nginx format (e.g. "1m", "100k", "50M").51 Sets `client_max_body_size`.52 '';53 example = "10m";54 };55 };Each option defines:
type: A NixOS module type fromlib.types. Here,lib.types.strMatching "^/.*$"requiresproxyPathto start with/, andlib.types.strMatching "^[0-9]+[kKmMgG]?$"requiresmaxBodySizeto match nginx’s size syntax (1m,100k,50M).default: The value used when a cluster instance omits the option fromconfig.descriptionandexample: Human-readable documentation for the option, used in generated package reference pages and evaluation diagnostics.
Why validate proxyPath and maxBodySize with lib.types.strMatching? If someone configures proxyPath = "api" (missing the leading slash) or maxBodySize = "10mb" (invalid mb suffix for nginx), raw templating would generate a broken default.conf that only fails when the nginx container starts in the cluster. With lib.types.strMatching, kix check and kix build catch the mistake immediately on your workstation.
Consume config In build
Section titled “Consume config In build”Still in tutorials/09-typed-options/reverse-proxy-package.nix, look at how build receives config and uses config.maxBodySize and config.proxyPath inside the nginx ConfigMap:
build = { self, scope, kix, backend, config, ... }: let name = scope.instanceName; in { configmap = scope.mkResource { apiVersion = "v1"; kind = "ConfigMap"; inherit name; data."default.conf" = '' server { listen 80; client_max_body_size ${config.maxBodySize};
location /healthz { default_type text/plain; return 200 'ok\n'; } location ${config.proxyPath} { proxy_pass http://${backend.out.fqdn}:${backend.out.port}/; } } ''; };
# ... };When Kix evaluates an instance of reverse-proxy, it merges the package’s options declarations with the instance’s config = { ... } overrides and passes the validated result into build as config.
Configure The Instance In cluster.nix
Section titled “Configure The Instance In cluster.nix”Open tutorials/09-typed-options/cluster.nix to see how the gateway instance sets those options:
39 gateway = {40 package = reverseProxyPackage;41
42 # Both options are typed in the package. Try a43 # `proxyPath` that does not start with `/`, or a44 # `maxBodySize` such as "1mb" (wrong suffix), and the45 # build will fail with a type error.46 config = {47 proxyPath = "/";48 maxBodySize = "10m";49 };50
51 deps.backend = ref.tutorial-09.backend;52 };Check And Build The Cluster
Section titled “Check And Build The Cluster”Run kix check and kix build on 09-typed-options:
❱ kix check 09-typed-options❱ kix build 09-typed-options In the rendered ConfigMap/gateway manifest, confirm that both options were interpolated into default.conf:
1server {2 listen 80;3 client_max_body_size 10m;4 ...5 location / {6 proxy_pass http://backend.tutorial-09.svc.cluster.local:80/;7 }8}Try Passing An Invalid Option Value
Section titled “Try Passing An Invalid Option Value”Open tutorials/09-typed-options/cluster.nix and change proxyPath to "api/" (without a leading slash):
1gateway = {2 package = reverseProxyPackage;3 config = {4 proxyPath = "api/";5 maxBodySize = "10m";6 };7 deps.backend = ref.tutorial-09.backend;8};Run kix check 09-typed-options:
❱ kix check 09-typed-options Evaluation fails with a type error explaining that "api/" does not match the pattern "^/.*$" required by proxyPath.
Also try adding an unknown option key, such as timeout = 30;, inside gateway.config. Kix rejects unknown options immediately rather than ignoring typos.
Change proxyPath back to "/" and remove any extra test keys before moving on.
What You Learned
Section titled “What You Learned”You turned hardcoded package strings into a typed configuration contract:
- packages declare their configuration schema in
optionsusinglib.mkOptionandlib.types.* -
lib.types.strMatching,lib.types.ints.positive,lib.types.enum, and other types validateconfigvalues at evaluation time -
buildreceives the merged, validatedconfigattribute set - invalid values or misspelled option names fail
kix checkandkix buildbefore any manifests reach a cluster
In the next tutorial, you will instantiate the same package multiple times in a single cluster and see how scope keeps each instance’s resources isolated.