Nov 25, 2025Julian K. Arni

garnix actions

Running Nix apps outside a sandbox

garnix actionsshare

If you were to hang around a Nix user, or a functional programmer, you might be excused in thinking that they see no place for side-effects. That purity/determinism/declarativeness are their holy trinity, and anything else the work of the devil.

But in reality the functional obsession is only with clearly separating that which is effectful from that which is pure. In the end, you still need to deploy a new version of your software, or send emails, or hit APIs. There are many places where you might find or create this separation; but one that is becoming increasingly well accepted is that building and testing your program should be done is a deterministic and (morally) pure way; running it not. If you stop to think about it, this makes sense. The non-deterministic is, in a very literal sense, the contingent — what could be otherwise. Your built artifact should work in any of the possible worlds in which it was designed to run; so it shouldn't embed or depend on contingencies about specific ones. Similarly, tests indicate the correctness of the program, which is also independent of accidental facts about when and where the tests are run.

Another (much more practical) guideline for where the separation goes is based on noting that removing impurities takes work. Maybe there's an external API call in your tests; that's not ideal, but removing it means instrumenting your code with mocks and test flags. So the more incremental perspective might simply be: make pure what you can, when you can.

garnix has always allowed pure Nix builds and tests — that's our bread and butter. It didn't easily let you do impure things, though. You could hook up GitHub Actions to run after garnix, and fetch what it had built, but it was a bit inconvenient. Now, however, you can also run the programs we've built for their effects — specifically, to run arbitrary Nix flake apps. This allows precisely this mixing of purity and effects. Let's say you want to use our statix app to submit lints in Nix code as PR comments on the right line numbers:

{
  inputs = ...;
  outputs = { ... }: {
    apps.x86_64-linux.statix = garnix-actions.lib.x86_64-linux.statix
      { actionName = "statix";
        encryptedTokenFile = "./secrets/statixToken";
      };
  };
}

(You can see a description of the arguments, as well as more actions, here.) And then let garnix know when it should be run in your garnix.yaml:

actions:
  - on: push
    action: statix
    withRepoContents: true

A comment automatically created by a garnix action (with reviewdog and statix).

The on field indicates the trigger for the action. push, which is currently the only allowed value, indicates that it is run on any push. (In the future, we imagine adding trigger such as build-failed, all-builds-done, cron, as well as slack-message, github-comment, email, etc.)

Another example would be setting up an environment with Nix that has the right version of rust and other tools, but then simply calling cargo build impurely. Further down the line, you could move the build to Nix, but keep the test as an action.

By default, the app is run in a micro VM. We've managed to get startup time to under a second (longer with large closures). This in turn means we are in "serverless" territory, and indeed one of our goals is to have an http trigger so that you can have an alternative to services such as AWS Lambda that very easily works with any Nix executable.

Compared to GitHub Actionsshare

This may seem very similar to GitHub Actions. And there is in fact a lot of overlap. But it's worth being clear about the differences:

  • Running garnix actions locally is easy by design.
  • Partly because of this design, it's easy for garnix actions to have dev scripts that help pick configuration or set up secrets.
  • There is hardly any "wrapping" to make something a garnix action. If you have a flake app, you just need three or four very short lines of YAML to run it.
  • Relatedly, there is very little vendor lock-in.
  • Composition is just the standard one you know from your command line. actions are just programs — you can pipe between them, write other scripts that call out to them, etc.
  • Caching is (still) much better. The build of your app is cached by default, and in the much more sophisticated caching scheme of Nix.

There are also downsides:

  • There's no notion of a pipeline, so there's no built-in way to visualize or break-down logs or execution. Every action is, from the framework's perspective, a single black box.
  • Similarly, there is no built-in way to give permissions or secrets to particular steps of an action. There are the usual Linux mechanisms, such as bubblewrap, to run specific programs within an action in restricted environments, where they e.g. don't have access to secrets or environment variables. That gives you plenty of control, but that's a bit more work, and that means a lot of people won't bother.

In garnix actions, you don't by default get access to the entire repo and .git directory (though you can set withRepoContents: true if you need that). Instead, you can pick and choose the parts of the repo you need, and reference them directly with Nix paths. This helps secure the confidentiality of your code, as actions only have access to what they need. It also opens the possibility of adding a filter to the action, so it only runs when something you actually use changed.

shared-resources runnershare

In addition to the microVM option, we also have a shared-resources action runner for enterprise deployments that would like to shared resources between actions. One example is a docker daemon and cache. Another is the Nix daemon (in case you are doing Nix builds in your action).

This only is available for enterprise deployments since we provision servers specifically for you.

Conclusionshare

garnix actions let you do things that were only possible via GitHub Actions previously, and in ways that have several advantages. They also let you more easily make the process of converting to full Nix more incremental.

You can see more documentation about actions in our docs, and some example of actions, as well as helpers for writing them, here.

Continue Reading

Oct 30, 2025Alex David, Sönke Hahn, and Julian K. Arni

A supply-chain attack on Nix, and our approach to solving it.

Oct 29, 2025Alex David and Julian K. Arni

Solving the issues with remote building in Nix

Oct 20, 2025Alex David, Julian K. Arni, Sönke Hahn

Nix makes CIs easy to compare; we benchmarked the main Nix CIs.

View Archive
black globe

Say hi, ask questions, give feedback