Best practices for templating K8 configs



I am switching codebase from using K8 Kustomize to Dhall.
I’m using for this, however, I’m having an issue with overriding env variables deep in K8 Deployment.
Basically, I cannot use with to override env variables for a deployment, because deployment.spec is Optional. If I try using with, I get: Error: ❰with❱ only works on records Only working solution is to convert everything to lambda:

let mkContainer = \(envVars : List k8.EnvVar.Type) -> k8.Container :: {
    name = "service_name",
    image = Some "image_name",
    command = Some ["python", "service_path"],
    env = Some envVars,
let mkPodSpec = \(envVars : List k8.EnvVar.Type) -> k8.PodSpec :: {
    serviceAccountName = Some "service_account",
    containers = [mkContainer envVars]
let mkPodTemplateSpec =  \(envVars : List k8.EnvVar.Type) -> k8.PodTemplateSpec::{
    spec = Some (mkPodSpec envVars)
let selector = k8.LabelSelector :: {
    matchLabels = Some [{
            mapKey = "tier",
            mapValue = "backend"
let makeDeployment = \(envVars : List k8.EnvVar.Type) -> k8.Deployment :: {
    metadata = k8.ObjectMeta::{
        name = Some "serice_name",
        labels = Some [{
    spec = Some k8.DeploymentSpec::{
        replicas = Some +1,
        strategy = Some strategy,
        selector = selector,
        template = mkPodTemplateSpec envVars,
in makeDeployment

However, this does not scale well, I am not sure what part of “default” deployment will need to be adjusted in the future.

Are there any best practices for updating such nested Optionals and Lists?


Yeah, this is a known limitation of the language. I recommend checking out Roadmap for improved Kubernetes support which mentions that the most likely way to address this is to change the language to improve the with keyword to allow working inside of Optionals and Lists. In other words, you would eventually be able to write something like config with deployment.spec.?.foo to make a change inside of an Optional value.

Until then, there isn’t really an ergonomic way to do this other than writing a bunch of transformation functions like you were already doing.


Thanks for advice!
Just wanted to share a final solution, I create a separate Params type on top of template for K8 object, like so:

let Params = { image : Text, envVars : List k8.EnvVar.Type, namespace : Text }

let makeDeployment =
      \(params : Params) ->

And than it’s very clear for consumer which params to provide, and each lambda takes just a single parameter inside a template.