terranix and flake modules

flake-parts is a framework for Nix flakes built on the module system. As flake.parts puts it:

flakes are configuration, and the module system lets you refactor that configuration into reusable pieces — reducing the proliferation of custom Nix glue code.

terranix ships a flake-module that plugs into flake-parts. It gives you a declarative, options-based way to use terranix instead of calling lib.terranixConfiguration manually and wiring up your own flake outputs.

Before and after

Without the flake-module, you call lib.terranixConfiguration yourself and manually assemble packages, apps, and devShells. The Getting started with flakes page shows what this looks like.

With the flake-module, you declare terranixConfigurations and everything else is generated for you:

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs";
    terranix.url = "github:terranix/terranix";
    terranix.inputs.nixpkgs.follows = "nixpkgs";
    flake-parts.url = "github:hercules-ci/flake-parts";
    flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs";
  };

  outputs = inputs@{ flake-parts, ... }:
    flake-parts.lib.mkFlake { inherit inputs; } {
      imports = [ inputs.terranix.flakeModules.default ];
      systems = [ "x86_64-linux" ];

      perSystem = { pkgs, ... }: {
        terranix.terranixConfigurations.myproject = {
          modules = [ ./config.nix ];
        };
      };
    };
}

This single declaration auto-generates packages, apps, and devShells for each configuration.

What you get

packages

Each terranixConfigurations.<name> becomes a package. The package is an apply script that runs terraform init && terraform apply.

nix run .#myproject

Passthru scripts

The apply derivation carries init, plan, apply, and destroy as passthru attributes. This means you can run each command directly:

nix run .#myproject          # apply
nix run .#myproject.plan     # plan
nix run .#myproject.destroy  # destroy
nix run .#myproject.init     # init only

devShells

Run nix develop .#myproject to get an interactive shell with apply, plan, destroy, init, and the terraform/opentofu wrapper all on your PATH.

$ nix develop .#myproject
$ plan
$ apply
$ destroy

Set terranix.exportDevShells = false if you don't want these.

Terraform/OpenTofu wrapper

Each script automatically creates the working directory, symlinks the generated config.tf.json, and runs the appropriate terraform commands.

See Customizing the Terraform binary for how to use OpenTofu, bundle provider plugins, or inject secrets.

Configuration options

Each entry under terranix.terranixConfigurations accepts:

OptionDescription
modulesList of terranix modules to evaluate
extraArgsExtra arguments passed to module evaluation (as specialArgs)
workdirWorking directory for terraform state (defaults to the configuration name)
terraformWrapper.packageTerraform or OpenTofu package (default: pkgs.terraform)
terraformWrapper.extraRuntimeInputsAdditional packages available during terraform runs
terraformWrapper.prefixTextShell commands to run before each terraform invocation
terraformWrapper.suffixTextShell commands to run after each terraform invocation

Multiple configurations

You can define more than one configuration in the same flake. Each gets its own package, scripts, and devShell:

perSystem = { pkgs, ... }: {
  terranix.terranixConfigurations.staging = {
    modules = [ ./staging.nix ];
  };
  terranix.terranixConfigurations.production = {
    modules = [ ./production.nix ];
    terraformWrapper.package = pkgs.opentofu;
  };
};
nix run .#staging          # apply staging
nix run .#production.plan  # plan production
nix develop .#staging      # interactive shell for staging