Skip to main content

Overview

Nixidy is a declarative Kubernetes manifest generator that uses Nix to define applications and Helm charts. The microservices-app uses Nixidy to:
  • Define all Kubernetes resources in Nix
  • Manage Helm chart values declaratively
  • Generate ArgoCD Applications for GitOps
  • Ensure reproducible manifests

Architecture

Nixidy modules are organized as follows:
deploy/
├── nixidy/
│   └── env/
│       ├── local.nix         # Main environment config
│       └── traefik.nix       # Traefik Helm chart config
├── k8s/
│   ├── greeter.nix           # Greeter service deployment
│   ├── caller.nix            # Caller service deployment
│   ├── gateway.nix           # Gateway service deployment
│   ├── auth-service.nix      # Auth service deployment
│   ├── custom-lang-service.nix
│   └── frontend.nix          # Frontend deployment
└── manifests/
    ├── greeter-service/      # Generated manifests
    ├── caller-service/
    ├── gateway-service/
    ├── traefik/
    └── apps/                 # ArgoCD Applications

Environment Configuration

The main environment file at deploy/nixidy/env/local.nix imports all service modules:
{ ... }:
{
  imports = [
    ../../k8s/greeter.nix
    ../../k8s/caller.nix
    ../../k8s/gateway.nix
    ../../k8s/custom-lang-service.nix
    ../../k8s/auth-service.nix
    ../../k8s/frontend.nix
    ./traefik.nix
  ];

  nixidy = {
    target = {
      repository = "https://github.com/hackz-megalo-cup/microservices-app";
      branch = "main";
      rootPath = "./deploy/manifests";
    };

    defaults = {
      destination.server = "https://kubernetes.default.svc";

      syncPolicy = {
        autoSync = {
          enable = true;
          prune = true;
          selfHeal = true;
        };
      };
    };

    appOfApps = {
      name = "apps";
      namespace = "argocd";
    };
  };
}

Key Settings

  • target.repository: Git repository for ArgoCD sync
  • target.rootPath: Where manifests are stored
  • autoSync: Automatically sync changes from Git
  • prune: Delete resources not in Git
  • selfHeal: Reconcile manual changes

Service Module Example

Example from deploy/k8s/greeter.nix:
_:
let
  labels = {
    "app.kubernetes.io/name" = "greeter";
    "app.kubernetes.io/version" = "0.1.0";
  };
in
{
  applications.greeter-service = {
    namespace = "microservices";
    createNamespace = true;

    resources = {
      deployments.greeter-service.spec = {
        replicas = 1;
        selector.matchLabels = labels;
        template = {
          metadata.labels = labels;
          spec.containers.greeter-service = {
            image = "greeter:latest";
            imagePullPolicy = "Never";
            ports.http.containerPort = 8080;

            env = {
              OTEL_EXPORTER_OTLP_ENDPOINT.value = "http://otel-collector.observability:4317";
              OTEL_SERVICE_NAME.value = "greeter-service";
              PORT.value = "8080";
              DATABASE_URL.value = "postgresql://devuser:[email protected]:5432/greeter_db";
            };

            livenessProbe = {
              httpGet = { path = "/healthz"; port = 8080; };
              initialDelaySeconds = 5;
              periodSeconds = 10;
            };

            readinessProbe = {
              httpGet = { path = "/healthz"; port = 8080; };
              initialDelaySeconds = 3;
              periodSeconds = 5;
            };

            resources = {
              requests = { cpu = "50m"; memory = "64Mi"; };
              limits = { cpu = "200m"; memory = "192Mi"; };
            };
          };
        };
      };

      services.greeter-service.spec = {
        selector = labels;
        ports.http = {
          port = 80;
          targetPort = 8080;
          protocol = "TCP";
        };
      };
    };
  };
}

Key Features

  • Type safety: Nix validates structure at evaluation time
  • Reusable labels: Define labels once, reference everywhere
  • Environment variables: Declarative configuration
  • Health probes: Kubernetes liveness and readiness checks
  • Resource limits: CPU and memory constraints

Helm Chart Integration

The deploy/nixidy/env/traefik.nix shows Helm chart usage:
{ charts, ... }:
{
  applications.traefik = {
    namespace = "edge";
    createNamespace = true;

    helm.releases.traefik = {
      chart = charts.traefik.traefik;
      values = {
        image.tag = "v3.2.0";
        service.type = "NodePort";
        ports = {
          web.nodePort = 30081;
          websecure.nodePort = 30444;
        };
        providers = {
          kubernetesCRD.enabled = true;
          kubernetesIngress.enabled = true;
        };
        tracing.otlp.grpc = {
          enabled = true;
          endpoint = "otel-collector.observability:4317";
        };
        extraObjects = [
          # Traefik Middleware and IngressRoute CRDs
        ];
      };
    };
  };
}
The charts attribute provides access to Helm charts defined in flake.nix.

Generating Manifests

The gen-manifests command builds and copies manifests:
gen-manifests
This runs scripts/gen-manifests.sh:
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
SYSTEM="$(nix eval --raw --impure --expr 'builtins.currentSystem')"

echo "==> Building nixidy manifests..."
nix build "${REPO_ROOT}#legacyPackages.${SYSTEM}.nixidyEnvs.local.environmentPackage" \
  -o "${REPO_ROOT}/manifests-result"

echo "==> Copying to deploy/manifests/..."
rm -rf "${REPO_ROOT}/deploy/manifests"
cp -rL "${REPO_ROOT}/manifests-result" "${REPO_ROOT}/deploy/manifests"
chmod -R u+w "${REPO_ROOT}/deploy/manifests"

rm -f "${REPO_ROOT}/deploy/manifests/apps/Application-argocd.yaml"

echo "==> Done. deploy/manifests/ updated."
git -C "${REPO_ROOT}" diff --stat -- deploy/manifests/

Build Process

  1. Nix evaluation: Evaluates flake.nix#nixidyEnvs.local
  2. Build: Generates all YAML manifests
  3. Copy: Copies from Nix store to deploy/manifests/
  4. Cleanup: Removes ArgoCD bootstrap Application (deployed separately)
  5. Diff: Shows changes to commit

Auto-Regeneration

Tilt watches Nixidy files and auto-regenerates manifests:
local_resource(
    'gen-manifests',
    cmd='bash scripts/gen-manifests.sh',
    deps=[
        'flake.nix',
        'deploy/nixidy/env/local.nix',
        'deploy/nixidy/env/traefik.nix',
        'deploy/k8s/greeter.nix',
        'deploy/k8s/caller.nix',
        # ...
    ],
    trigger_mode=TRIGGER_MODE_AUTO,
)
Changes to .nix files trigger automatic manifest regeneration and kubectl apply.

Watching Manifests

Monitor Nixidy changes and apply automatically:
watch-manifests
This watches .nix files and runs gen-manifests + kubectl apply on changes.

Adding a New Service

When adding a new service, update Nixidy:
  1. Create module: deploy/k8s/my-service.nix
  2. Import in environment: Add to deploy/nixidy/env/local.nix imports
  3. Add to Tiltfile: Include in gen-manifests deps
  4. Stage files: Run git add deploy/k8s/my-service.nix (Nix requires tracked files)
  5. Generate: Run gen-manifests
See Adding a Go Service or Adding a Node.js Service for details.

Fixing Chart Hashes

If you see a chartHash error:
fix-chart-hash
This parses the Nix build error and automatically updates the hash in your .nix files.

Troubleshooting

Nix Evaluation Errors

nix-check
Validates Nix expressions without building.

Git-Tracked Files

Nixidy uses builtins.path which only reads Git-tracked files. Always git add new .nix files before running gen-manifests.

Manifest Diff

Review changes before committing:
git diff deploy/manifests/

Next Steps

Build docs developers (and LLMs) love