<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
  <title>zimbatm's notes</title>
  <link>https://zimbatm.com/notes</link>
  <description>Notes on software engineering, Nix, and other topics</description>
  <atom:link href="https://zimbatm.com/notes/feed.xml" rel="self" type="application/rss+xml"/>
  <lastBuildDate>Wed, 11 Feb 2026 23:50:54 +0000</lastBuildDate>
  <item>
    <title>Garnix is so good. It's suprising it's not the default CI for all new Nix projects.</title>
    <link>https://zimbatm.com/notes/garnix-is-so-good-its-suprising-its-not-the-default-ci-for-all-new-nix-projects</link>
    <guid>https://zimbatm.com/notes/garnix-is-so-good-its-suprising-its-not-the-default-ci-for-all-new-nix-projects</guid>
    <pubDate>Mon, 23 Dec 2024 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>Is something I said on Bluesky last week. Here is a longer take on this:</p>
<p>I know <a href="https://garnix.io/">Garnix</a> quite well. We have the pleasure to count them as one of Numtide's customers. Julian and I launched the Nix Swiss-French meetup together. And I have been using them on my home repository for the past 6 months. So that's the disclaimer and context.</p>
<p>It's also why I know and understand Garnix quite well. Like I said in my post, it's surprising to me that not more people are using it today. To me, Garnix is the perfect Nix CI. Let me share what I see:</p>
<li>No CI-specific YAML. Whatever is in your flake.nix that runs on your machine, also runs in CI.</li>
<li>No vendor lock-in. If you don't like it anymore, it's easy to move back to GitHub Actions, or deploy your own buildbot-nix instance.</li>
<li>A globally shared binary cache.garnix.io. So every new project feeds and shares their build results. Faster compile for everyone. And no need to configure a different cache for each project. And because only the Garnix CI can push to the cache, you only need to trust the Garnix build infrastructure.</li>
<li>Supports the quad: Linux and macOS. Both with x86_64 and aarch64.</li>
<li>30s runs with hot <code>/nix/store</code>. Unlike GitHub Actions, builders don't flush their <code>/nix/store</code> on every run. That, and good hardware is what makes it possible to get ultra fast build turnarounds.</li>
   <strong>TL;DR: Point Garnix to your repo with a flake.nix in it, and you have a CI.</strong>
<p>Then combine this with <a href="https://github.com/numtide/blueprint">blueprint</a> to keep your flake super lean. Mergify + renovate to get automated flake updates. And you have a really nice setup.</p>
<p>The best is when I pull my dotfiles repo and everything is already prebuilt. No amount of customization I do on top of nixpkgs and NixOS is going to penalize me. And Nix is really _the_ language for people that like to fearlessly customize things.</p>
<p>Ok, enough rambling for a day. Does the vision make sense or are there aspects you’re considering that I haven’t touched upon?</p>
<a href="https://garnix.io/">https://garnix.io/</a>]]></description>
  </item>
  <item>
    <title>User-directed updates</title>
    <link>https://zimbatm.com/notes/user-directed-updates</link>
    <guid>https://zimbatm.com/notes/user-directed-updates</guid>
    <pubDate>Sat, 20 Jul 2024 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>The Crowdstrike BSoD outage is a good example of something that happens all the time. The user decides to use the software, to get something done. The software decides to run an update instead. The user now has to fix, find workarounds or get used to the user interface in the new version.</p>
<p>The same issue appears in Dockerfile and other developer environments. The developer wants to get something done, something changed, and they are now fixing their environment.</p>
<p>What if the user was in control? What if they could decide when to update their software? And rollback to a previous version if things start breaking?</p>
<p>In order to make that a reality, you would have to describe</p>
<p>This is only</p>
<p>This is something we take for granted when using Nix.</p>
<p>This is what Nix has to offer.</p>
<p>That’s Nix.</p>]]></description>
  </item>
  <item>
    <title>Guard your project’s Docker registry using Scarf and a custom domain</title>
    <link>https://zimbatm.com/notes/guarding-your-docker-registry-using-scarf</link>
    <guid>https://zimbatm.com/notes/guarding-your-docker-registry-using-scarf</guid>
    <pubDate>Sun, 26 Mar 2023 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>On March 14th, Docker Inc. informed us via email that they would no longer offer the Docker Hub Free Team plan. We had 30 days to pay $420 per year or else our organization would be deleted.</p>
<p>However, moving registries without causing disruption takes a long time. There is always a long tail of users that depend on the current location, buried in 20 layers of scripts or recursive Dockerfiles. Projects were being held hostage in some way.</p>
<p>Because Numtide worked with <a href="https://scarf.sh/">Scarf</a> before, I had migrated <a href="https://github.com/nix-community/docker-nixpkgs">https://github.com/nix-community/docker-nixpkgs</a> to it, and had it bound to a custom domain; docker.nix-community.org. So while all the other projects were scrambling away, all I had to do, is redirect the domain to a different registry. The only caveat that I found is that the images must live on the same path prefix, due to a limitation of the docker registry protocol.</p>
<p>In the end, Docker Inc. reverted their stance, but that is a good reminder; if you don’t control the domain, you don’t control the project. It was nice to be able to sit back and relax while all the other projects were scambling away.</p>
<details>
<summary>What is Scarf?</summary>
  <a href="https://scarf.sh/">Scarf</a> is a SaaS company that provides high-level insights about your project downloads. It’s useful for finding out which company is using your project.
</details>]]></description>
  </item>
  <item>
    <title>The NixOS “settings” option: when and how to use it</title>
    <link>https://zimbatm.com/notes/the-nixos-settings-option-when-and-how-to-use-it</link>
    <guid>https://zimbatm.com/notes/the-nixos-settings-option-when-and-how-to-use-it</guid>
    <pubDate>Fri, 20 Jan 2023 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>Two years ago, <a href="https://github.com/infinisil">@infinisil</a> <a href="https://github.com/NixOS/rfcs/pull/42">introduced RFC 0042</a>, a new <code>settings</code> option to NixOS modules. Previously, to define extra settings on top of the default ones, you would use the <code>extraConfig</code> parameter. But it was inconvenient to use it, and sometimes the option exposed faulty behavior. This is all gone with <code>settings</code>, which can specify configuration files as a structural Nix value.</p>
<p>NixOS modules that ship with nixpkgs has been slowly adopting it and now we have some experience of how it plays out in practice.</p>
<p>Here I will discuss the pros and cons of <code>settings</code> and its usage: when it's appropriate to use it and when to avoid it. I'll also give you some recommendations.</p>
<h2>What is the <code>settings</code> option?</h2>
<p>This new approach makes NixOS services configuration more extensible while reducing the number of module options authors have to describe. For instance, instead of writing:</p>
<pre><code class="language-nix">services.foo.extraConfig = ''
  # Can't be set in multiple files because string concatenation doesn't merge such lists
  listen-ports = 456, 457, 458

  # Can't override this setting because the module hardcodes it
  # bootstrap-ips = 172.22.68.74

  enable-ipv6 = 0
  ${optionalString isServer &quot;check-interval = 3600&quot;}
'';
</code></pre>
<p>You can now write:</p>
<pre><code class="language-nix">services.foo.settings = {
  listen-ports = [ 456 457 458 ];
  bootstrap-ips = [ &quot;172.22.68.74&quot; ];
  enable-ipv6 = false;
  check-interval = mkIf isServer 3600;
};
</code></pre>
<p>So, services now use pure Nix types for different settings instead of configuration file snippets.</p>
<h2>Upsides</h2>
<p>The most significant benefit of <code>settings</code> is that configuration files now have the same extension properties as the rest of NixOS modules. So it's easier to set and override configuration options for any modules. So, overall, it helps with module composability.</p>
<p>Another benefit is that the module author doesn't have to predict where the extension points should be added; all the configuration is extensible by default.</p>
<p>Another bright side is that the configuration is more likely to be valid, as its evaluation is tied to the Nix evaluation. This moves most typos from runtime issues to nix evaluation time issues (but not semantic issues).</p>
<h2>Downsides</h2>
<p>The users now have to convert configuration snippets from sources, such as documentation and Stackoverflow pieces of advice, to Nix code, and do it manually! This introduces manual friction and makes it harder to compare to previous or future versions of the configuration to see what changed between them.</p>
<p>This can be especially painful if the Nix data types don't map cleanly to the configuration format. <a href="https://github.com/tazjin">@tazjin</a> showed me a <a href="https://github.com/NixOS/nixpkgs/blob/nixos-22.11/nixos/modules/services/databases/openldap.nix#L129">great example</a> of such an issue the other day. A nightmare to work with!</p>
<p>We also had users’ reports that the capitalisation change needs to be clarified. In the nix world, we use <code>camelCase</code> but then setting keys, map 1:1 to the configuration option and might use another rule. Eg: <code>services.biboumi.credentialsFile</code> vs <code>services.biboumi.settings.xmpp_server_ip</code>.</p>
<h2>Recommendations to module authors</h2>
<ul><li><strong>Avoid \<em>\</strong></em><code>settings</code>\<strong><em>\</em> if the types don't map well</strong>. Don't use the <code>settings</code> if there is no precise 1:1 mapping between Nix data types and the target config file.</li>
</ul>  <strong>OK:</strong> JSON, TOML, YAML
  <strong>Not OK:</strong> most of the older stuff. Nginx, OpenLDAP, Apache2, ...
<ul><li><strong>Provide a \<em>\</strong></em><code>configFile</code>\<strong><em>\</em> escape hatch to the user.</strong> Every module that exposes a <code>settings</code> option should also provide a <code>configFile</code> option that contains the generated config file. This gives the user an escape hatch. The option documentation should be explicit that providing an alternative <code>configFile</code> will ignore all the settings options.</li>
</ul>
<pre><code class="language-nix">config.settings.myservice.configFile = pkgs.writeText &quot;myservice-config.toml&quot; (''
  # Hey, I can now add comments to the config file
'' + ''
  and_compose_snippets = true
'');

</code></pre>
<ul><li><strong>Provide Nix library to load configuration snippets.</strong> In order to retain the ability to copy-paste snippets around, it would be nice to provide a pure Nix parsing library that can convert the configuration snippet to Nix data. This is a stretch goal and has some evaluation performance implications. Eg:</li>
</ul>
<pre><code class="language-nix">services.myservice.settings = lib.fromTOML ''
  user = &quot;hello&quot;
'';
</code></pre>
<h2>Conclusion</h2>
<p>Here, I explored what the <code>settings</code> - the new option in nix modules are. I looked at the upsides and downsides and gave some advice on its usage.</p>
<p>So now you are informed to employ it as intended!</p>
<h3>Extra notes</h3>
<a href="https://github.com/NixOS/nixpkgs/issues/144575">https://github.com/NixOS/nixpkgs/issues/144575</a>]]></description>
  </item>
  <item>
    <title>the nix configuration</title>
    <link>https://zimbatm.com/notes/the-nix-configuration</link>
    <guid>https://zimbatm.com/notes/the-nix-configuration</guid>
    <pubDate>Thu, 18 Aug 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>There are some surprising corner cases with how Nix handles its configuration. My goal with this article is to clarify your mental model. I will take a few shortcuts to keep this focused on the high-level mechanisms.</p>
<h2>Baseline</h2>
<p>The first thing to establish is Nix has a client (nix CLI) / server (nix-daemon) architecture.</p>
<p>They read the same configuration format but look at different places and don’t look at different keys in the file (with some overlapping).</p>
<p>Configuration reference:</p>
<a href="https://nixos.org/manual/nix/stable/command-ref/conf-file.html">https://nixos.org/manual/nix/stable/command-ref/conf-file.html</a>
<h2>Server</h2>
<p>The nix-daemon reads from <code>/etc/nix/nix.conf</code>.</p>
<p>The configuration is loaded while the process is starting. If you change the config, don’t forget to restart the nix-daemon (eg: <code>pkill nix-daemon</code>). On NixOS this is handled automatically for you.</p>
<p>The server is mainly interested in configuration keys that change the building environment. Things like where to substitute existing build results from, how the build <a href="https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-sandbox"><code>sandbox</code></a> is configured, the <a href="https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-max-jobs"><code>max-jobs</code></a> concurrency, …, and who are the <a href="https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-trusted-users"><code>trusted-users</code></a> (more on this later).</p>
<h2>Client</h2>
<p>The client reads its config from <code>$NIX_USER_CONF_FILES</code>, <code>~/config/nix/nix.conf</code> and also <code>/etc/nix/nix.conf</code>, merging all the keys from right to left.</p>
<p>The client is mainly concerned about evaluation and CLI settings, like the <a href="https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-nix-path"><code>nix-path</code></a>, <a href="https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-restrict-eval"><code>restrict-eval</code></a>, if flakes are enabled, …</p>
<h2>Overriding server configuration from the client (trusted-users)</h2>
<p>We’re getting to the meat of the confusion.</p>
<p>When the nix client initiates a build (aka “realizes the derivation”), the nix client reads <code>/etc/nix/nix.conf</code>, and diffs it with its current configuration. If it sees changes in <code>substituters</code> and other build-specific configurations, it will forward them to the server along the build.</p>
<blockquote>Changing build configuration can affect the system’s integrity. This allows replacing <code>/nix/store</code> entries with corrupted payloads (like viruses). And probably escalate permissions to root.</blockquote>
<p>So by default, the server rejects configuration changes. Unless the sending user is part of the <code>trusted-user</code> list. If you ever saw an error message saying you are not a trusted user, that’s why.</p>
<p>On the other hand, this is very handy for setting per-project caches. Typically each project has its binary cache, and changing the server configuration on every project switch takes a while.</p>
<p>TODO: I’m not sure, but I don’t think what is described here works with remote builders. Even if it worked, it doesn’t know what the remote builder config looks like, it’s only comparing it to <code>/etc/nix/nix.conf</code>.</p>
<h2>Flakes</h2>
<p>Talking about flakes, if the <code>flake.nix</code> has a <code>nixConfig</code> section, the client will also load config from there.</p>
<p>Even if the user is not a trusted-user, this is a standardized place to document your project’s requirements. Eg:</p>
<pre><code class="language-nix">{
  description = &quot;My flake&quot;;

  # Points to our cache containing all the build results published by the CI.
  nixConfig.extra-substituters = [ &quot;https://nix-community.cachix.org/&quot; ];
  nixConfig.extra-trusted-public-keys = [ &quot;nix-community.cachix.org-1:mB9FSh9qf2dCimDSUo8Zy7bkq5CX+/rkCWyvRCYg3Fs=&quot; ];

  # ...
  inputs = {};
  outputs = { self, ... }: {};
}
</code></pre>
<p>Before applying the changes, the Nix CLI will ask you if you trust the configuration changes. This makes it safer to have your user be part of the system’s <code>trusted-users</code> list (assuming you trust that user).</p>
<h2>Conclusion</h2>
<p>As you might notice, I didn’t specify which configuration key applies to the server or the client. This belongs to the reference documentation. Hopefully, somebody will be motivated to fix it. 😇</p>
<p>As usual, ping me if anything here needs some clarification.</p>]]></description>
  </item>
  <item>
    <title>Enrolling existing AWS Account in ControlTower - AWSControlTowerExecution IAM role</title>
    <link>https://zimbatm.com/notes/enrolling-existing-aws-account-in-controltower-awscontroltowerexecution-iam-role</link>
    <guid>https://zimbatm.com/notes/enrolling-existing-aws-account-in-controltower-awscontroltowerexecution-iam-role</guid>
    <pubDate>Wed, 10 Aug 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>Hopefully, this page gets indexed on Google for the next person.</p>
<p>This is for people enabling AWS Control Tower on an existing AWS Organization.</p>
<p>AWS provides documentation on <a href="https://docs.aws.amazon.com/controltower/latest/userguide/enroll-account.html">how to enroll existing AWS accounts</a>. They mention that the old AWS accounts need an <code>AWSControlTowerExecution</code> role. And then never tells you how to create one.</p>
<p>So here is how:</p>
<ul><li>Log into the account that needs to be enrolled.</li>
<li>IAM →Roles → Create Role</li>
<li>Select trusted entity: AWS Account → Another AWS account. Enter the Management AWS Account ID → Next</li>
<li>Add permissions: AdministratorAccess ( <code>arn:aws:iam::aws:policy/AdministratorAccess</code> ) → Next</li>
<li>Name, review, and create:</li>
</ul>  - Role name: AWSControlTowerExecution
  - Create role
<p>Simple in retrospect</p>]]></description>
  </item>
  <item>
    <title>Nix packaging, the heretic way</title>
    <link>https://zimbatm.com/notes/nix-packaging-the-heretic-way</link>
    <guid>https://zimbatm.com/notes/nix-packaging-the-heretic-way</guid>
    <pubDate>Tue, 05 Jul 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>One difficulty when using Nix is that it’s possible to hit a purity wall. A dependency is not in nixpkgs (yet), and you have to package it yourself. But the project does some impure things during the build. It’s using some esoteric language that doesn’t have a <code><lang>2nix</code> tool yet.</p>
<p>And sometimes it’s hard to go to your customer/boss and tell them you have to spend the next 3 weeks doing “things right”(tm).</p>
<p>Luckily there is a workaround available, and this is why I’m writing this article. To show a quick but impure alternative that can be used in a pinch.</p>
<blockquote><strong>Don’t use this for nixpkgs</strong> - PRs will be rejected. And Hydra doesn’t build those either.</blockquote>
<h2>Use <code>__noChroot = true</code> in a pinch</h2>
<p>By default, derivations are built in a sandboxed environment, that doesn’t allow them to use the network. This is one of the core features that is used to make builds more reproducible. And also one of the main reasons why an impure build would fail.</p>
<p>By adding <code>__noChroot = true</code> on a derivation, it turns off the sandbox selectively for that derivation. Note that all users also need to have <a href="https://nixos.org/manual/nix/stable/command-ref/conf-file.html#conf-sandbox"><code>sandbox = relaxed</code></a> set in their <code>nix.conf</code> or <code>nixConfig.sandbox = "relaxed"</code> in their flake.nix.</p>
<p>So this is not a good solution for open source projects but can work in an enterprise setting, which is the only place I recommend using this.</p>
<h2>Example packaging flow-bin</h2>
<a href="https://github.com/flow/flow-bin">flow-bin</a> is missing from nixpkgs. It’s just an example that I found that is node, and that ships with a pre-compiled binary. You could try to use node2nix, npm2nix, npmlock2nix, yarn2nix (2 versions), … Or just do this hacky thing 🙂
<pre><code class="language-nix">{ nixpkgs ? import &lt;nixpkgs&gt; { } }:
let
  version = &quot;0.105.1&quot;;
in
nixpkgs.runCommand &quot;flow-bin-${version}&quot;
{
  # Disable the Nix build sandbox for this specific build.
  # This means the build can freely talk to the Internet.
  __noChroot = true;

  # Add all the build time dependencies
  nativeBuildInputs = [
    # Automatically patchelf all installed binaries
    nixpkgs.autoPatchelfHook
  ];

  # Add all the runtime dependencies
  buildInputs = [
    nixpkgs.nodejs
  ];
}
	# This part is a bit like a Dockerfile, without the apt-get installs.
  ''
    # Nix sets the HOME to something that doesn't exist by default.
	  # npm needs a user HOME.
    export HOME=$(mktemp -d)

    # Install the package directly from the Internet
    npm install flow-bin@${version}

    # Fix all the shebang scripts in the node_modules folder.
    patchShebangs .

    # Copy the node_modules and friends
    mkdir -p $out/share
    cp -r . $out/share/$name

    # Add a symlink to the binary
    mkdir $out/bin
    ln -s $out/share/$name/node_modules/.bin/flow $out/bin/flow
  ''
</code></pre>
<p>This kind of approach is quite generally applicable and should work for other languages as well. Of course it’s less reproducible, and if anything changes in the build script, there are no incremental build layers like in Docker.</p>
<p>You could split the build into different phases though, and that’s what we’ll be seeing next.</p>
<h2>Example packaging a NextJS project</h2>
<p>In this case, the code changes quite often and comes from the monorepo directly. We are not packaging a third-party library, but something that our developers are evolving daily.</p>
<p>We were using <a href="https://github.com/nix-community/npmlock2nix">npmlock2nix</a> to translate the npm package-lock.json to Nix at evaluation time. It’s a great project but, unfortunately, it <a href="https://github.com/nix-community/npmlock2nix/issues/140">doesn’t support the package-lock.json v2 format</a>. This is not a criticism of the project itself, it’s just a lot of work to keep up with the nodejs ecosystem (and the format itself got even more complicated).</p>
<p>This meant that we were stuck with nodejs 14.x, which is 2 years old and <a href="https://endoflife.date/nodejs">is not actively supported anymore</a>. And most frontend developers on the team also didn’t care about Nix and just installed nodejs directly in their macOS, meaning they had the latest version that generates v2 formats.</p>
<p>So <code>__noChroot = true</code> comes to the rescue. Here we split the build and install the <code>node_modules</code> impurely, but keep the core project sandboxed. This allows to minimize the surface and rebuild that happens.</p>
<pre><code class="language-nix"># Fill these arguments how you like
{ nixpkgs ? import &lt;nixpkgs&gt; { }
, nix-filter # an instance of https://github.com/numtide/nix-filter
}:
let self =
{
  # Pick the version of nodejs to use
  nodejs = nixpkgs.nodejs_18-x;

  # Build the node_modules separately, from package.json and package-lock.json.
  #
  # Use __noChroot = true trick to avoid having to re-compute the vendorSha256 every time.
  node_modules = nixpkgs.stdenv.mkDerivation {
    name = &quot;node_modules&quot;;

    src = nix-filter {
      root = ./.;
      include = [
        ./package.json
        ./package-lock.json
      ];
    };

    # HACK: break the nix sandbox so we can fetch the dependencies. This
    # requires Nix to have `sandbox = relaxed` in its config.
    __noChroot = true;

    configurePhase = ''
      # NPM writes cache directories etc to $HOME.
      export HOME=$TMP
    '';

    buildInputs = [ self.nodejs ];

    # Pull all the dependencies
    buildPhase = ''
      ${self.nodejs}/bin/npm ci
    '';

    # NOTE[z]: The folder *must* be called &quot;node_modules&quot;. Don't ask me why.
    #          That's why the content is not directly added to $out.
    installPhase = ''
      mkdir $out
      mv node_modules $out/node_modules
    '';
  };

  # And finally build the frontend in its own derivation
  my-frontend = nixpkgs.stdenv.mkDerivation {
    name = &quot;my-frontend&quot;;
    # Use the current folder as the input, without node_modules
    src = nix-filter {
      root = ./.;
      exclude = [
        ./.next
        ./node_modules
      ];
    };

    nativeBuildInputs = [ self.nodejs ];

    buildPhase = &quot;npm run build&quot;;

    configurePhase = ''
      # Get the node_modules from its own derivation
      ln -sf ${self.node_modules}/node_modules node_modules
      export HOME=$TMP
    '';

    # TODO: move to different derivation
    doCheck = true;
    checkPhase = ''
      npm run test
    '';

    # This is specific to nextjs. Typically you would copy ./dist to $out or
    # something like that.
    installPhase = ''
      # Use the standalone nextjs version
      mv .next/standalone $out

      # Copy non-generated static files
      cp -R public $out/public

      # Also copy generated static files
      mv .next/static $out/.next/static

      # Re-link the node_modules
      rm $out/node_modules
      mv node_modules $out/node_modules

      # Wrap the script
      cat &lt;&lt;ENTRYPOINT &gt; $out/entrypoint
      #!${nixpkgs.stdenv.shell}
      exec &quot;$(type -p node)&quot; &quot;$out/server.js&quot; &quot;$$@&quot;
      ENTRYPOINT
      chmod +x $out/entrypoint
    '';
  };
}; in self
</code></pre>
<h2>Example packaging a .NET project</h2>
<p>For .NET, there is a tool called <a href="https://github.com/winterqt/nuget2nix">nuget2nix</a> that is called to generate a <code>deps.nix</code> file, which is then passed to <code>nixpkgs.buildDotnetModule</code>‘s <code>nugetDeps</code> argument. So every time a dependency changes, don’t forget to call that tool again. Because most .NET developers are on Windows, they also don’t have the tool installed, so we wrote a CI step that would check that the file was up to date and push a fixup commit otherwise. Then we re-discovered that dependabot doesn’t have the same permissions and would fail. This dance was starting to get old pretty fast.</p>
<p>So here is the new solution: fetch the dependencies with <code>__noChroot = true</code> in one derivation, and then pass them into the main build:</p>
<pre><code class="language-nix"># Fill these arguments how you like
{ nixpkgs ? import &lt;nixpkgs&gt; { } # an instance of nixpkgs
, nix-filter # an instance of https://github.com/numtide/nix-filter
}:
let self =
{
  # The .NET packages we want to use
  dotnet-sdk = nixpkgs.dotnetCorePackages.sdk_6_0;
  dotnet-runtime = nixpkgs.dotnetCorePackages.aspnetcore_6_0;

  # Fetch all the dependencies in one derivation with __noChroot = true
  nugetDeps = nixpkgs.stdenv.mkDerivation {
    name = &quot;nuget-deps&quot;;

    # HACK: break the nix sandbox so we can fetch the dependencies. This
    # requires Nix to have `sandbox = relaxed` in its config.
    __noChroot = true;

    # Only rebuild if the project metadata has changed
    src = nix-filter {
      root = ./.;
      include = [
        (nix-filter.isDirectory)
        (nix-filter.matchExt &quot;csproj&quot;)
        (nix-filter.matchExt &quot;slnf&quot;)
        (nix-filter.matchExt &quot;sln&quot;)
      ];
    };

    nativeBuildInputs = [
      nixpkgs.cacert
      self.dotnet-sdk
    ];

    # Avoid telemetry
    configurePhase = ''
      export DOTNET_NOLOGO=1
      export DOTNET_CLI_TELEMETRY_OPTOUT=1
    '';

    projectFile = &quot;my-api.slnf&quot;;

    # Pull all the dependencies for the project
    buildPhase = ''
      for project in $projectFile; do
        dotnet restore &quot;$project&quot; \
          -p:ContinuousIntegrationBuild=true \
          -p:Deterministic=true \
          --packages &quot;$out&quot;
      done
    '';

    installPhase = &quot;:&quot;;
  };

  # Build the project itself
  my-api = nixpkgs.buildDotnetModule {
    pname = &quot;my-api&quot;;
    version = &quot;0&quot;;

    src = nix-filter {
      root = ./.;
      exclude = [
        # Filter out C# build folders
        (nix-filter.matchName &quot;bin&quot;)
        (nix-filter.matchName &quot;logs&quot;)
        (nix-filter.matchName &quot;obj&quot;)
        (nix-filter.matchName &quot;pub&quot;)
        (nix-filter.matchName &quot;.vs&quot;)
      ];
    };

    projectFile = &quot;my-api.slnf&quot;;

    # Replace the `nugetDeps = ./deps.nix` with the derivation.
    # This is only possible for nixpkgs that contains this PR:
    # https://github.com/NixOS/nixpkgs/pull/178446
    nugetDeps = self.nugetDeps;

    dotnet-sdk = self.dotnet-sdk;
    dotnet-runtime = self.dotnet-runtime;

    executables = [
      &quot;MyAPI&quot;
    ];
  };
}
</code></pre>
<h2>Conclusion</h2>
<p>So there you have it. I hope that the explanation and examples give you an idea of how to apply this in various contexts, and help unblock some packaging problems that you might have. It’s better to use Nix impurely than not at all, and the sandbox change is really localized and controlled.</p>
<p>Of course, if you want help with pure packaging, you can always reach out to <a href="https://numtide.com/contact">Numtide</a> and we can help you out.</p>
<p>That’s all, hope this was interesting!</p>]]></description>
  </item>
  <item>
    <title>Announcing nixpkgs-unfree</title>
    <link>https://zimbatm.com/notes/nixpkgs-unfree</link>
    <guid>https://zimbatm.com/notes/nixpkgs-unfree</guid>
    <pubDate>Fri, 04 Feb 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>Recently I was saying that we should avoid creating too many instances of nixpkgs. Either accept an argument or use the flake follows feature:</p>
<p>There is just one problem with this claim; what if you need to access unfree packages? For example, try running:</p>
<pre><code class="language-javascript">$ nix run nixpkgs#slack

error: Package ‘slack-4.22.0’ in /nix/store/fbcgjqs34vllzzppa1y213fbxx01sxn7-source/pkgs/applications/networking/instant-messengers/slack/default.nix:83 has an unfree license (‘unfree’), refusing to evaluate.

       a) To temporarily allow unfree packages, you can use an environment variable
          for a single invocation of the nix tools.

            $ export NIXPKGS_ALLOW_UNFREE=1

       b) For `nixos-rebuild` you can set
         { nixpkgs.config.allowUnfree = true; }
       in configuration.nix to override this.

       Alternatively you can configure a predicate to allow specific packages:
         { nixpkgs.config.allowUnfreePredicate = pkg: builtins.elem (lib.getName pkg) [
             &quot;slack&quot;
           ];
         }

       c) For `nix-env`, `nix-build`, `nix-shell` or any other Nix command you can add
         { allowUnfree = true; }
       to ~/.config/nixpkgs/config.nix.
(use '--show-trace' to show detailed location information)
</code></pre>
<p>Oops!</p>
<p>This whole error message is misleading. All the solutions proposed don’t work when using Flakes because Flakes evaluation is pure and doesn’t take your environment variables or config into account.</p>
<p>In order to fix that problem, allow me to introduce a new project:</p>
<a href="https://github.com/numtide/nixpkgs-unfree">https://github.com/numtide/nixpkgs-unfree</a>
It’s a small wrapper to nixpkgs with <code>allowUnfree = true;</code> enabled. I know I said not to create new instances of nixpkgs, but that’s the last one I promise 🙂
<p>So now with that, you can run:</p>
<p>``<code>plain text
$ nix run --no-write-lock-file github:numtide/nixpkgs-unfree#slack
<pre><code class="language-">
It’s also usable as a flake, so you can point nixpkgs to it:

</code></pre>nix
{
  inputs.nixpkgs.url = "github:numtide/nixpkgs-unfree";
}
</code>`<code></p>
<p>In the future, I also want to also keep the channels synchronized with nixpkgs so you can run </code>nix run github:numtide/nixpkgs-unfree/<channel>`. And potentially provide a binary cache for it.</p>
<p>That’s it for now, have a great weekend!</p>
<h3>Discussion</h3>
<p>If you want to comment on the article, head over to the NixOS Discourse:</p>
<a href="https://discourse.nixos.org/t/announcing-nixpkgs-unfree/17505">https://discourse.nixos.org/t/announcing-nixpkgs-unfree/17505</a>
<h3>Addendum: how many instances of allowUnfree are there on GitHub?</h3>
<p>Thanks to @tazjin for pointing me to a more usable code search: <a href="https://sourcegraph.com/search?q=context%3Aglobal%20file%3Aflake.nix%20allowUnfree&patternType=literal">https://sourcegraph.com/search?q=context:global+file:flake.nix+allowUnfree&patternType=literal</a></p>
<p>At the time of writing, there are 218 results.</p>]]></description>
  </item>
  <item>
    <title>Website setup: Notion, Super and CloudFlare</title>
    <link>https://zimbatm.com/notes/website-setup-notion-super-and-cloudflare</link>
    <guid>https://zimbatm.com/notes/website-setup-notion-super-and-cloudflare</guid>
    <pubDate>Sat, 29 Jan 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>Somebody asked me how this website was set up. So here it is 🙂</p>
<p>Before we jump in, here is a table of all the costs associated with this configuration.</p>
<h3>Costs</h3>
<ul><li><a href="https://namecheap.com/">Domain</a>: 74 cents per month for me.</li>
<li><a href="https://notion.so/">Notion</a>: free, or $4 per month.</li>
<li><a href="https://super.so/">Super</a>: $12 per month.</li>
<li><a href="https://cloudflare.com/">Cloudflare</a>: free.</li>
</ul>
<h2>How it works</h2>
<pre><code class="language-mermaid">flowchart LR
    Browser/Client &lt;--&gt; CloudFlare &lt;--&gt; Super &lt;--&gt; Notion
    CloudFlare --&gt; Worker --&gt; CloudFlare
</code></pre>
<h3>Notion</h3>
<p>Notion is a wiki-like SaaS. That is where the content of the site is stored. In principle, I prefer to use open-source software to hack on it, but there is also value in having access to a system that is maintained and improved over time. Since I am already using it daily to take journalling notes, it makes it easy to swap the content around. This very article was first written in my journal. Notion itself allows you to publish pages from their content, but it’s only available at <code><workspace>.notion.site/<page-name>-<page-id></code></p>
<a href="https://www.notion.so/">https://www.notion.so/</a>
<h3>Super</h3>
<p>That’s where <strong>Super</strong> comes in. It’s straightforward to set up and bind a published notion page to a domain, with some bells and whistles on top. For example, the CSS can get tweaked, add some privacy-preserving analytics, change the menu a bit.</p>
<a href="https://super.so/">https://super.so/</a>
<h3>Cloudflare</h3>
<p>One feature that both Super and Notion are missing is page redirects. Since this website had existing content and links pointing from the outside, I wanted to redirect the users to the new URLs. And I knew that with Cloudflare, it’s possible to intercept the traffic.</p>
<li>Register for Cloudflare</li>
<li>Import the existing DNS records</li>
<li>Change the NS records at Namecheap to point to Cloudflare</li>
<li>Point the naked domain to <code><site-name>.super.so</code></li>
   <a href="https://www.cloudflare.com/">https://www.cloudflare.com/</a>
<h3>Cloudflare workers</h3>
<p>Now that Cloudflare is in front, it’s possible to use their Worker feature to handle redirects. Once you’ve created a service, here is a little worker script that I wrote:</p>
<pre><code class="language-javascript">addEventListener(&quot;fetch&quot;, (event) =&gt; {
  event.respondWith(handleRequest(event.request));
});

const locationMap = {
  // Put your own routes here
  &quot;/NixFriday&quot;: &quot;/old-projects/nixfriday&quot;,
  &quot;/NixFlakes&quot;: &quot;/notes/nixflakes&quot;,
};

async function handleRequest(request) {
  const url = new URL(request.url);

  for (const key in locationMap) {
    // if the path as the 'key' prefix
    if (url.pathname.lastIndexOf(key, 0) == 0) {
      // redirect the user to locationURL
      const locationURL = locationMap[key];
      let headers = new Headers();
      headers.set(&quot;Location&quot;, locationURL);
      return new Response(&quot;The page moved to &quot; + locationURL, {
        status: 302,
        statusText: &quot;Found&quot;,
        headers: headers,
      });
    }
  }

  return new Response(&quot;This path doesn't match anything: &quot; + url.pathname, {
    status: 500,
    statusText: &quot;Server Error&quot;,
  });
}
</code></pre>
<p>And then attach the routes that need to be redirected to the worker:</p>
<img src="/data/notes/website-setup-notion-super-and-cloudflare/image-1.png" alt="">
<h2>What’s missing</h2>
<p>RSS feeds. That isn’t very pleasant, and I hope to solve it in the future.</p>
<p>Some syntax colouring. Nix syntax is supported, but not Terraform or TOML.</p>
<p>The MermaidJS block isn’t supported yet. This is a general issue where super has to keep up with notion changes, and the website could be in a broken state in the meantime.</p>
<p>No backup and restore. Their API allows exporting every page to Markdown so somebody can build a service on top.</p>
<h2>Conclusion</h2>
<p>My previous setup was a more classic one; a Git repository rendered to GitHub Pages. It had all the features that I wanted, except a minor issue; I wasn’t writing articles. Editing, remembering the syntax, previewing, thinking about an appropriate commit message, ... Somehow, the friction was preventing me from writing.</p>
<p>This new setup is far from ideal, but my bet seems to be paying off so far; Notion has less friction as the content gets published directly.</p>
<p>That’s it! Let me know if any details are unclear.</p>]]></description>
  </item>
  <item>
    <title>1000 instances of nixpkgs</title>
    <link>https://zimbatm.com/notes/1000-instances-of-nixpkgs</link>
    <guid>https://zimbatm.com/notes/1000-instances-of-nixpkgs</guid>
    <pubDate>Wed, 26 Jan 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<blockquote>💡 If you are coming here from the Determinate Systems article, you might come with the impression that this article is anti-flake. This isn’t the case. The purpose of this article is to talk about a specific issue that I see arriving down the road.</blockquote>
<p>This is a bit of a PSA for the NixOS community (and me), to try and expose something that I see:</p>
<blockquote>💡 dependencies should not create their own instance of nixpkgs</blockquote>
<p>Especially with the advent of Flakes, soon enough, we will end up with 1000 dependencies, each with its own instance of nixpkgs. Given that nixpkgs takes around 100MiB of RAM and a second to evaluate, that can quickly add up.</p>
<h2>How we got there</h2>
<p>Overlays everywhere. Here is an example of one of my own projects:</p>
<pre><code class="language-nix">pkgs = import inputs.nixpkgs {
  inherit system;
  config = { };
  overlays = [
    (final: prev: {
      fenix = import inputs.fenix {
        pkgs = prev;
      };
    })
  ];
};
</code></pre>
<p>nixpkgs overlays are super useful. They are a mechanism that allows taking nixpkgs, and extending it with your own packages and overrides. In most cases, it’s more manageable than forking nixpkgs and managing your own long-running branch. NixOS also doesn’t provide a standard way to have other package sets so it makes sense to have them all in one. Those two reasons are what made them popular.</p>
<p>There is just one problem; overlays are only usable when creating a new instance of nixpkgs. It’s time to stop using overlays (in most cases, see below).</p>
<h2>Solution; composition over inheritance</h2>
<p>This title doesn’t make 100% sense but you get it, compose instead of extending nixpkgs. Here are a few scenarios that you might encounter with proposed solutions:</p>
<h3>Nix classic</h3>
<p>Typically, in a Nix classic project, dependencies are pinned using <a href="https://github.com/nmattia/niv">niv</a>, and then you compose the different sources with something like this:</p>
<pre><code class="language-nix">{ system ? builtins.currentSystem }:
let
  sources = ./nix/sources.nix;

  pkgs = import sources.nixpkgs {
    inherit system;
    config = { };
    overlays = [(final: prev: {
      other-dep = import sources.other-dep { pkgs = prev; };
    })];
  };
in
# your code here accessing `pkgs.other-dep`
</code></pre>
<p>Instead of creating this one instance with an overlays, split it up like this:</p>
<pre><code class="language-nix">{ system ? builtins.currentSystem
, sources ? import ./nix/sources.nix
, nixpkgs ? import sources.nixpkgs { inherit system; config = { }; overlays = [ ]; }
, other-dep = import sources.other-repo { pkgs = nixpkgs; };
}:
# your code here accessing `nixpkgs` and `other-dep`
</code></pre>
<p>Exposing the constructors as a function argument allows a consumer of your project to inject their own instance of nixpkgs in there, and avoid creating a new instance. And also provide their own version of other-dep if they want to.</p>
<blockquote>💡 <code>pkgs</code> has been renamed to <code>nixpkgs</code> to make it clear that it’s just nixpkgs and not a random set of packages.</blockquote>
<h3>Nix Flakes</h3>
<p>Here is a synthetic example of what a Flake typically looks like:</p>
<pre><code class="language-nix">{
  description = &quot;My flake&quot;;

  inputs.nixpkgs.url = &quot;github:NixOS/nixpkgs/nixos-unstable&quot;;
  inputs.other-dep.url = &quot;github:other/dep&quot;;

  outputs = { self, nixpkgs, other-dep }: {
      packages = nixpkgs.lib.genAttrs [ &quot;x86_64-linux&quot; ] (system:
        let
          pkgs = import nixpkgs {
            inherit system;
            overlays = [(final: prev: {
              other-dep = import sources.other-dep { pkgs = prev; };
            })];
          };
        in
        # your code here accessing `pkgs` and `pkgs.other-dep`
     );
  };
}
</code></pre>
<p>Instead of instantiating a new nixpkgs, access <code>nixpkgs.legacyPackages.${system}</code> and then make sure that all dependencies use the same instance of nixpkgs.</p>
<pre><code class="language-nix">{
  inputs.nixpkgs.url = &quot;github:NixOS/nixpkgs/nixos-unstable&quot;;
  inputs.other-dep.url = &quot;github:other/dep&quot;;
  # Use the same version of nixpkgs as us
  inputs.other-dep.inputs.nixpkgs.follows = &quot;nixpkgs&quot;;

  outputs = { self, nixpkgs, other-dep }@inputs: {
      packages = nixpkgs.lib.genAttrs [ &quot;x86_64-linux&quot; ] (system:
        let
          p = {
            nixpkgs = inputs.nixpkgs.legacyPackages.${system};
            # other-dep would also access `inputs.nixpkgs.legacyPackages.${system}`
            # thus only using a single instance of it.
            other-dep = inputs.other-dep.packages.${system};
          };
        in
        # your code here accessing `p.nixpkgs` and `p.other-dep`
     );
  };
}
</code></pre>
<p>That way, there will only be a single instance of nixpkgs being evaluated, and consumers of your project can again follow the same practice.</p>
<h3>NixOS</h3>
<p>NixOS is a tough one because there are interactions between the module system and the packages. When using <code>nixos-rebuild</code>, NixOS will create its own instance of nixpkgs, based on the NIX_PATH and channels by default, and configured by <a href="https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=nixpkgs.">the </a><a href="https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=nixpkgs."><code>nixpkgs.*</code></a><a href="https://search.nixos.org/options?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=nixpkgs."> options</a>. Or when using flakes, it calls <code>pkgs.nixos</code> that injects its own instance of nixpkgs to the <code>nixpkgs.pkgs</code> option. There isn’t really room to provide more package sets side-by-side.</p>
<p>Luckily NixOS is a bit out of scope for this article because typically NixOS configs are at the root of the dependency tree 😅.</p>
<p>Ideally, we would introduce a new top-level <code>packages</code> attribute that can hold package sets side by side and would be used like this:</p>
<pre><code class="language-nix">{ config, ... }:
{
  systemPackages = [ config.packages.nixpkgs.hello ];
}
</code></pre>
<h2>Some more arguments against overlays</h2>
<p>Did you ever hit hard to debug infinite recursion issues? Without overlays, those are gone.</p>
<p>Given that the instance of pkgs is a global namespace, it can become difficult to reason about it once a few overlays have been added. Are they all using their own prefix inside of that global namespace? Is there any chance they might clash over each other? Are they overriding existing packages? All of this is gone without overlays.</p>
<p>Overlays are opaque before being applied. So tools like <code>nix flake show</code> won’t be able to inspect their content.</p>
<h2>When to use overlays</h2>
<p>To being said, even with all these arguments against overlays, there are places where they are still useful:</p>
<p>Contrary to what I said, if there are no Nix consumers of your repository, then don’t mind me, go crazy. This article is really aimed at 3rd-party dependencies and hopes to change the status quo.</p>
<p>Another example would be if your project really needs to patch nixpkgs. To get a whole set of nixpkgs out, with some internal dependency replaced with your own version. Imagine needing nixpkgs, but with a different version of OpenSSL, or different build flags. The point is that in these cases, you wouldn’t add new attributes to nixpkgs and only modify existing ones.</p>
<h1>Conclusion</h1>
<p>In this article we have seen two things; when to use overlays, and how to avoid creating too many instances of nixpkgs. Of course, the reality is always more nuanced than the points and I’m sure you will find corner cases where you still need to reach for those tools. But I hope you got the overall points and that it made sense.</p>
<p>Thanks for reading!</p>
<p>For comments and discussion: <a href="https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347/1">https://discourse.nixos.org/t/1000-instances-of-nixpkgs/17347/1</a></p>]]></description>
  </item>
  <item>
    <title>Nix 2.6 eval improvement</title>
    <link>https://zimbatm.com/notes/nix-26-eval-improvement</link>
    <guid>https://zimbatm.com/notes/nix-26-eval-improvement</guid>
    <pubDate>Tue, 25 Jan 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>With the recent <a href="https://discourse.nixos.org/t/nix-2-6-0-released/17324">Nix 2.6 release</a>, I was curious about how much impact all of @pennae’s work was having on the Nix evaluation.</p>
<blockquote>TL;DR: Nix evaluation is 11-17% faster in Nix 2.6 compared to Nix 2.5.1</blockquote>
<h2>Methodology</h2>
<p>I have both the nix and nixpkgs repo side by side.</p>
<p>nixpkgs is checked out at ac44b27bab615fd49bc94fe22124deae233b5c94 (latest master)</p>
<p>nix is checked out at 2.6.0</p>
<p>Then run <code>nix-build pkgs/top-level/release.nix -A metrics</code> and collect the output.</p>
<p>For nix 2.5.1 I added a space change to pkgs/top-level/metrics.nix to force the rebuild on my machine.</p>
<p>For nix 2.6 I injected the version from the nix repo:</p>
<pre><code class="language-diff">diff --git a/pkgs/top-level/metrics.nix b/pkgs/top-level/metrics.nix
index d413b881eaa..cf065697923 100644
--- a/pkgs/top-level/metrics.nix
+++ b/pkgs/top-level/metrics.nix
@@ -2,11 +2,17 @@

 with pkgs;

+let
+  nix = (import ../../../nix).defaultPackage.${pkgs.system};
+in
+
</code></pre>
<p>Then I took all the results and painstakingly inserted them in Notion, for your pleasure and mine:</p>
<table><tr><th>Metric</th><th>2.5.1</th><th>2.6.0</th><th>Diff %</th></tr><tr><td>nix-env.qaCount</td><td>38784</td><td>38784</td><td></td></tr><tr><td>nix-env.qaDrvAggressive.values</td><td>101913915</td><td>101689635</td><td></td></tr><tr><td>nixos.kde.maxresident</td><td>572032 KiB</td><td>560300 KiB</td><td></td></tr><tr><td>nix-env.qaDrv.allocations</td><td>8828388424 B</td><td>8641514600 B</td><td></td></tr><tr><td>nixos.smallContainer.allocations</td><td>280278984 B</td><td>270314728 B</td><td></td></tr><tr><td>nix-env.qa.allocations</td><td>1589209632 B</td><td>1533671496 B</td><td></td></tr><tr><td>nix-env.qaDrv.time</td><td>58.0178 s</td><td>54.589 s</td><td></td></tr><tr><td>nixos.kde.time</td><td>1.96151 s</td><td>1.74307 s</td><td></td></tr><tr><td>nixos.lapp.time</td><td>1.62311 s</td><td>1.43888 s</td><td></td></tr><tr><td>nixos.kde.values</td><td>5314604</td><td>5188433</td><td></td></tr><tr><td>nix-env.qaDrv.maxresident</td><td>6430276 KiB</td><td>6558340 KiB</td><td></td></tr><tr><td>nix-env.qa.values</td><td>13927806</td><td>14326969</td><td></td></tr><tr><td>nix-env.qaCountBroken</td><td>2666</td><td>2666</td><td></td></tr><tr><td>nixos.lapp.allocations</td><td>363063272 B</td><td>349837776 B</td><td></td></tr><tr><td>nixos.kde.allocations</td><td>422864776 B</td><td>409517776 B</td><td></td></tr><tr><td>nixos.smallContainer.time</td><td>1.02288 s</td><td>0.861962 s</td><td></td></tr><tr><td>nix-env.qaDrvAggressive.allocations</td><td>8828388424 B</td><td>8641514600 B</td><td></td></tr><tr><td>nix-env.qa.maxresident</td><td>2199724 KiB</td><td>2220716 KiB</td><td></td></tr><tr><td>nix-env.qaAggressive.maxresident</td><td>2199728 KiB</td><td>2220736 KiB</td><td></td></tr><tr><td>nix-env.qa.time</td><td>8.62761 s</td><td>7.51825 s</td><td></td></tr><tr><td>nix-env.qaDrv.values</td><td>101913915</td><td>101689635</td><td></td></tr><tr><td>nix-env.qaDrvAggressive.time</td><td>57.418 s</td><td>54.3827 s</td><td></td></tr><tr><td>loc</td><td>2341615</td><td>2341619</td><td></td></tr><tr><td>nix-env.qaAggressive.values</td><td>13927806</td><td>14326969</td><td></td></tr><tr><td>nix-env.qaAggressive.time</td><td>8.57256 s</td><td>7.62719 s</td><td></td></tr><tr><td>nixos.lapp.values</td><td>4697517</td><td>4572023</td><td></td></tr><tr><td>nixos.smallContainer.values</td><td>3341438</td><td>3243496</td><td></td></tr><tr><td>nixos.mallContainer.maxresident</td><td>480160 KiB</td><td>458036 KiB</td><td></td></tr><tr><td>nix-env.qaAggressive.allocations</td><td>1589209632 B</td><td>1533671496 B</td><td></td></tr><tr><td>nix-env.qaDrvAggressive.maxresident</td><td>6429816 KiB</td><td>6558460 KiB</td><td></td></tr><tr><td>nixos.lapp.maxresident</td><td>560988 KiB</td><td>550420 KiB</td><td></td></tr></table>]]></description>
  </item>
  <item>
    <title>2022, the year of writing</title>
    <link>https://zimbatm.com/notes/2022-the-year-of-writing</link>
    <guid>https://zimbatm.com/notes/2022-the-year-of-writing</guid>
    <pubDate>Wed, 05 Jan 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>A real developer should write their content in a markup language, commit and push it with Git, and then use a Static Site Generator. That's how a developer is supposed to handle their personal website, and that's how you know they are good... Right? For the longest time, I have been stuck mentally with this idea.</p>
<p>The problem is that writing is a messy process. Especially when my ideas are unclear, and it takes many small iterations before I get to a point where I am satisfied with the result. And I do not even claim that my writing style is good.</p>
<p>The point is that Git creates too much friction. I do not care about writing commit messages. It's awkward enough to share my ideas with the world. And now I have to worry about sharing my inner workings on top?</p>
<p>Markup languages also add too much friction. Why do I have to remember how to describe a table, properly nest bullet points. Writing needs to be as free as possible.</p>
<p>So for 2022, I am trying something new. I imported all my pages into <a href="http://notion.so/">Notion.so</a>, slapped <a href="http://super.so/">Super.so</a> in front of it, and voila, I have a dynamically editable website. There are a few drawbacks, like the lack of an RSS feed and no page redirects (DM me, I have a solution for that), but so far, it's working quite well.</p>
<p>At least that is the theory. Let's see what happens in reality.</p>]]></description>
  </item>
  <item>
    <title>Terraform patterns: usages of count</title>
    <link>https://zimbatm.com/notes/terraform-patterns-usages-of-count</link>
    <guid>https://zimbatm.com/notes/terraform-patterns-usages-of-count</guid>
    <pubDate>Sun, 02 Jan 2022 00:00:00 +0000</pubDate>
    <description><![CDATA[<blockquote>count (int) - The number of identical resources to create. This doesn’t apply to all resources. For details on using variables in conjunction with count, see Using Variables with count below.</blockquote>
<p>TL;DR: only use the <code>count</code> attribute to enable resources.</p>
<h2>Basic example</h2>
<p>The <code>count</code> attribute can be used to instantiate multiple resources with a single resource declaration. Here we instanciate 10 EC2 instances:</p>
<p>Eg:</p>
<p>``<code>plain text
resource "aws_instance" "web" {
  count         = 10
  ami           = "ami-0cdba8e998f076547"
  instance_type = "t2.micro"
}
<pre><code class="language-">
On the surface it looks useful but it suffers from a number of limitations that make it almost useless.

## Pattern: `enable` attribute

This is pretty much the only viable use-case for the `count` attribute. Use `count = 0` to disable a resource and `count = 1` to enable it.

Even then, evaluation might fail if the resource is disabled and another resource depends on it’s outputs.

Eg:

</code></pre>plain text
variable "enable_elb" {
  default = 1
}</p>
<p>resource "aws_elb" "bar" {
  count              = "${var.enable_elb}"
  name               = "foobar-terraform-elb"
  availability_zones = ["us-west-2a", "us-west-2b", "us-west-2c"]</p>
<p>listener {
    instance_port     = 8000
    instance_protocol = "http"
    lb_port           = 80
    lb_protocol       = "http"
  }</p>
<p>instances                   = ["${aws_instance.web.id}"]
  cross_zone_load_balancing   = true
  idle_timeout                = 400
  connection_draining         = true
  connection_draining_timeout = 400
}
<pre><code class="language-">
## Anti-pattern: avoid repetition

Forget DRY with Terraform. Copy-and-paste is your friend :slight_smile:

This is a natural usage of `count`. Don’t do this:

</code></pre>plain text
variable "users" {
  type = "list"
}</p>
<p>resource "aws_iam_user" "my-users" {
  count = "${length(var.users)}"
  name = "${element(var.users, count.index)}"
  path = "/"
}
<pre><code class="language-">
Let’s say I instantiace that module with:

</code></pre>plain text
module "my-users" {
  source = "../tf_my_users"
  users = [
    "bob",
    "alice",
    "jannet",
  ]
}
<pre><code class="language-">
This will work great on the first invocation. The problem is that each `aws_iam_user` is actually a different resource.

Let’s say that later that `bob` leaves the company.

</code></pre>plain text
aws_iam_user.my-users.0 "bob" => "alice"
aws_iam_user.my-users.1 "alice" => "jannet"
aws_iam_user.my-users.2 "jannet" => ""
</code>``</p>
<p>All the users get re-created, invalidating their AWS credentials. With bad luck your current AWS account might be in that list.</p>
<p>So in conclusion, Terraform deals with individual resources. Having them tied to a specific ordering is quite a bad idea as it makes the application of those resource inflexible.</p>
<p>For that use-case it’s better to copy-and-paste the IAM user. Or write a script that generates the terraform code from the list of users.</p>]]></description>
  </item>
  <item>
    <title>Summary of Nix Flakes vs original Nix</title>
    <link>https://zimbatm.com/notes/summary-of-nix-flakes-vs-original-nix</link>
    <guid>https://zimbatm.com/notes/summary-of-nix-flakes-vs-original-nix</guid>
    <pubDate>Mon, 27 Dec 2021 00:00:00 +0000</pubDate>
    <description><![CDATA[<h2>Quick recap</h2>
<p>Flakes was born when Shea Levy, who worked at Target at the time, decided to hire Eelco (who works at Tweag) to solve a set of issues that Target was having.</p>
<p>Flakes is a set of extensions for the Nix language that is currently (Dec 2021) behind an experimental flag.</p>
<p>While adding a set of useful features, Flakes also gathered some controversy in the NixOS community. This article is summarizing to the best of my ability the various viewpoints of flakes.</p>
<h2>Flakes features</h2>
<p>Flakes are a lot of things at the same time.</p>
<h3>1. A single project entry-point</h3>
<p>Nix is very flexible and each project tends to have a slightly different shape. Flakes introduces a top-level <code>flake.nix</code> file that serves as the main entry-point to a project. It declares inputs and outputs for the project (more on that later).</p>
<h3>2. Dependency management</h3>
<p>Flakes introduces a way to declare third-party dependencies. Instead of using a tool like <code>niv</code>, or manually updating versions and sha256, the <code>flake.nix</code> file declares all these inputs. An additional <code>flake.lock</code> file is introduced to pin those dependencies.</p>
<h3>3. Pure evaluation</h3>
<p>Flakes disables features such as <code>builtings.getenv</code>, <code>builtins.currentSystem</code>, <code>builtins.getCurrentTime</code>. Given that Nix is supposed to help make builds more reproducible, it’s nice that the evaluation is now pure by default.</p>
<h3>4. Evaluation caching</h3>
<p>With flakes being pure and controlling the inputs, it’s able to cache the evaluation outputs. The cache is currently keyed based on the Git SHA1. This allows speeding up some operations drastically.</p>
<h3>5. New CLI</h3>
<p>The Nix CLI has historically been confusing. As part of the Flakes effort, the CLI has been re-vamped to live behind a single <code>nix</code> binary, similar to how all <code>git</code> commands are living behind that name.</p>
<p>Note that the <code>nix</code> command was already in progress before flakes.</p>
<h2>Arguments against</h2>
<p>Here are all the arguments that I have seen that are against Flakes. It’s not because I listed all of them that they are all necessarily valid. Each argument should be weighed independently.</p>
<p>In order of my mind remembering:</p>
<h3>a. Keep nixpkgs a monorepo</h3>
<p>Some people consider that the biggest value in nix is not the language, but the <code>nixpkgs</code> repo that contains all the package definitions. And that this value is intrinsically tied to having a monorepo because it allows to easily do large refactors. Their fear is that if Flakes becomes stable, that the repository will be split into many different pieces.</p>
<p>Without the linerization of history, it would also become more expensive to fill the binary cache with all the possible combinations of git commits between the repositories.</p>
<h3>b. The Flakes RFC was closed</h3>
<p>After long conversations, <a href="https://github.com/NixOS/rfcs/pull/49">RFC0049</a> has been withdrawn. Yet Flakes still got implemented, albeit behind an experimental flag. That makes some people grumpy as they perceive it as being a breach of process.</p>
<h3>c. recurceIntoAttrs was lost</h3>
<code>nix-build</code> will build all the values of an attrset, and follow recursively is an attrset has a <code>recurseForDerivations = true;</code> key-value pair. Flakes now only allows building one attributes at the time, and only if it’s a derivation. This removes some heuristic in Flakes, but also prevents some use-cases like turning a monorepo into a tree of attrsets mapping the folder structure.
<code>nix flake check</code> will complain if the output of the <code>package</code> is not flat.
<h3>d. Flake.nix is not exactly a nix file</h3>
<p>While <code>flake.nix</code> has the syntax of a Nix file, only the outputs allow function calls. The rest of the structure is more like JSON, where only pure values are accepted.</p>
<h3>e. The flake.nix file is not ergonomic</h3>
<p>The <code>flake.nix</code> output, in particular, is quite confusing to people. Most projects use the <code>numtide/flake-utils</code> repo to make them more palatable.</p>
<h3>f. The strict dependency on Git</h3>
<p>A common issue that new users are facing is that flakes will complain that a file doesn’t exist, when in fact it does. The issue is that flakes only consider files that are part of the git index. <code>git add thefile</code> and then things are working again. And Flakes uses the staging area file list, but not the staging area contents. That makes the git experience counterproductive.</p>
<p>The strict dependency on Git means that companies that are using Mercurial or other source control won’t be able to use flakes.</p>
<h3>g. Evaluation caching is not useful during development</h3>
<p>Because the evaluation is keyed on the git commit, active development on a repository will mostly likely have invalidated the evaluation cache.</p>
<h3>h. System tuples are hard-coded with Flakes</h3>
<p>Before flakes appeared, nixpkgs was starting to move past the <code><arch>-<kernel></code> tuple (eg: <code>x86_64-linux</code>) to allow better expressing system variants such as distinctions between musl or glibc-based systems. As part of flakes’ design, the use is now locked into these tuples as they are mandated by Nix itself.</p>
<p>Flakes also uses these tuples quite a lot, and they are easy to mistype. Especially <code>x86_64-linux</code> that uses both an underscore and a dash.</p>
<h3>More?</h3>
<p>Let me know if I forgot any major points.</p>
<h2>Conclusion</h2>
<p>So here we are. With the recent Nix 2.4 release, Flakes is available behind a feature flag in a stable release. It’s being increasingly adopted by more and more people. Even though it hasn’t gone through an RFC, I think we reached a critical mass of users.</p>
<p>It’s unfortunate that Flakes is a bundle of features that all have to be adopted, or not. It puts us in a bit of a Python 3.0 situation, where it splits the community in two, and the transition is painful. To me, there are clearly good arguments on both sides.</p>]]></description>
  </item>
  <item>
    <title>Ocean Sprint 2021 Report</title>
    <link>https://zimbatm.com/notes/ocean-sprint-2021-report</link>
    <guid>https://zimbatm.com/notes/ocean-sprint-2021-report</guid>
    <pubDate>Fri, 17 Dec 2021 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>After almost two years of being cooped up at home, the main thing I ended up doing was talking to people. It was nice to catch up and exchange ideas without the friction of computers in between. I didn’t get a lot done this week but I think that syncing up brains will be useful down the line.</p>
<p>With Bernardo, we spent a bit of time looking at voting systems. We were talking about how to improve governance and leadership, and ended up looking at other communities like Debian, Python, Kubernetes, ... In particular, we were interested in their voting systems as a way to make the system more democratic. If the RFCs could be voted on when they are “ready”, it would change the nature of the shepherds to be mostly helping build the best possible RFC.</p>
<p>The rest of my time was spent benchmarking nix and nixpkgs. I think that our community is relying on nixpkgs overlays too much, and I needed those benchmarks to make one of my points. It resulted in a series of articles that I have started writing and will be publishing soon on <a href="https://zimbatm.com/">https://zimbatm.com</a>/. Of course, as with each new article, I will first have to re-do the website entirely with a new static site generator 😊</p>
<p>WIP blog post: <a href="https://numtide.notion.site/nixpkgs-instances-are-expensive-b65f68c4a31145aa986fb99496a21e44">https://numtide.notion.site/nixpkgs-instances-are-expensive-b65f68c4a31145aa986fb99496a21e44</a></p>]]></description>
  </item>
  <item>
    <title>Bisect Debugging</title>
    <link>https://zimbatm.com/notes/bisect-debugging</link>
    <guid>https://zimbatm.com/notes/bisect-debugging</guid>
    <pubDate>Thu, 21 Oct 2021 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>I was using <code>git-bisect</code> a lot and asked myself if this approach was generally applicable. It turns out that it works surprisingly well in a lot of cases. And I keep getting reminded that it works through practice. That’s why I’m writing this article.</p>
<p>But first, here are two triggers that show that I don’t know what I’m doing:</p>
<ul><li>Randomly upgrade packages in the hope that it will fix a bug.</li>
<li>Create bigger instance sizes in the hope that it will fix performance issues.</li>
</ul>  When I see myself doing these things, it’s time to bust out bisect debugging.
<h2>What is bisect debugging?</h2>
<p>The idea is simple; apply the <a href="https://en.wikipedia.org/wiki/Binary_search_algorithm">Binary search alorithm</a> to the debugging process. If you can map the search space into a flat line, then you are guaranteed to find a solution in <code>O(log n)</code> steps.</p>
<p>The first step is to map the search space. Find two bounds to the problem. On one side it’s working, and on the other it’s failing.</p>
<p>Cut the space in half, and test if the error occurs there. If it is, move the cursor to the half that’s working, otherwise, move it to the half that’s broken.</p>
<p>Repeat until you pin-point the problem.</p>
<p>That’s the overall process.</p>
<p>Of course, reality is messy and doesn’t always cleanly map to linear search space. And finding the exact half is not always measurable. But following that process has been immensely useful to me, over and over again.</p>
<h2>Why is it so good?</h2>
<p>Mechanical; debugging is an uncertain process. Uncertainty is a big driver for procrastination. Especially as a young engineer, or when under time pressure, the uncertainty can become quite unbearable. Because bisect debugging is mechanical, I find that it helps reduce that uncertainty and focus on finding the root cause of the problem instead. It also helps to communicate to management or other teams what we tried and what the next steps are.</p>
<p>Knowledge; the process of mapping the search space in itself is useful. It helps expose the areas that I don’t understand. In order to build the mental picture of the search space, I have to know what the space is like. Over time, this makes me a better engineer.</p>
<h2>Times it doesn’t work</h2>
<p>Mapping is too hard.</p>
<p>The search space is not linearizable.</p>
<h2>Examples</h2>
<p>TODO: Add some worked examples.</p>
<p>A good one is how to debug a networking connection issue.</p>
<h2>Bonus: weighted bisect debugging</h2>
<p>With experience, it’s possible to speed up the search process even further. If you know where the problem is more likely to be, split to that location instead of the half. Of course, even with experience, you might still be wrong.</p>]]></description>
  </item>
  <item>
    <title>Maintaining Open Source</title>
    <link>https://zimbatm.com/notes/maintaining-open-source</link>
    <guid>https://zimbatm.com/notes/maintaining-open-source</guid>
    <pubDate>Sun, 15 Nov 2020 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>This topic is recurring on HN. Some maintainer gets burned out and declares that Open Source is toxic. Ensues discussions.</p>
<p>Here are some thoughts on this subject, mostly to clarify my own ideas.</p>
<h2>On maintaining direnv</h2>
<p>I have been maintaining [Direnv] for 10+ years now and have been fortunate. There hasn’t been too many toxic users there. Maybe a shell extension is geeky enough so that mostly knowledgeable people filter through. I have a sense that with experience, also comes respect. We have been there and know what it means to maintain a project.</p>
<p>But still, it’s not easy.</p>
<p>I still remember taking 30min to reply to a single issue, and I still do from time to time. Saying the right thing is difficult and nerve-wrecking. Especially when the user has a good idea. I don’t want to let them down, but also this particular issue is not interesting to me.</p>
<p>The hardest part, to me, is to set the right mood. Fostering collaboration is difficult. I feel like I haven’t succeeded to do that. On the other hand, it’s quite common to have projects with a single main contributor. So is it my fault? Or is the project not suited for a more vibrant collaboration?</p>
<p>To me the whole endeavor feels a lot like some sort of exercise. A communication exercise. It’s never easy, but it gets easier with time. And it has its own type of rewards.</p>
<p>The biggest thing that I had to learn is where I stand. Each new issue is like a test. I found that if I can communicate that clearly, people are generally understanding. Saying yes is easy, it’s mostly about different types of no: out of scope for this project, interesting but needs a PR, …</p>
<p>That’s all for today.</p>
<h2>Links to some other opinions</h2>
<ul><li>https://raccoon.onyxbits.de/blog/bugreport-free-support/</li>
<li>https://lobste.rs/s/yz15mn/about_open_source_community_opennesss</li></ul>]]></description>
  </item>
  <item>
    <title>The Git staging area</title>
    <link>https://zimbatm.com/notes/the-git-staging-area</link>
    <guid>https://zimbatm.com/notes/the-git-staging-area</guid>
    <pubDate>Tue, 13 Oct 2020 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>I see a lot of users out there that don’t understand the Git staging area.</p>
<p>Conceptually this is the biggest change between traditional Source Control Management (SCM) tools and Git. Tutorials should explain that first:</p>
<p>Traditional SCM: code changes -> commit Git: code changes -> staging area -> commit</p>
<p>To understand Git, the important part is to form that mental model. The code changes don’t go to a commit directly. Instead, they are put in that staging area. That area is a virtual place where all the changes get accumulated. When typing <code>git commit</code>, the changes are moved from that area into the commit.</p>
<p>A user who doesn’t have that mental model will find the Git tooling confusing. This feature both provides more flexibility, and makes all the CLI interfaces more complicated to use.</p>
<p>Here is how I translate a number of commands mentally:</p>
<ul><li><code>git add somefile</code>: add the changes in “somefile” into the staging area</li>
<li><code>git add -p</code>: selectively add changes to the staging area.</li>
<li><code>git commit -a</code>: add all the changes of the tracked files into the staging area, and then create a commit from it.</li>
</ul>  Hope this helps, z]]></description>
  </item>
  <item>
    <title>Deploying to AWS with Terraform and Nix</title>
    <link>https://zimbatm.com/notes/deploying-to-aws-with-terraform-and-nix</link>
    <guid>https://zimbatm.com/notes/deploying-to-aws-with-terraform-and-nix</guid>
    <pubDate>Sat, 23 May 2020 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>Let's say that you want to deploy this NixOS configuration onto AWS:</p>
<code>configuration.nix</code>
<pre><code class="language-nix">{ ... }:
{
  # Put your NixOS configuration here. Eg:
  services.nginx.enable = true;
}
</code></pre>
<p>The first thing to do is to create another NixOS configuration that includes the amazon-image config and your main config. This is what ultimately is going to end-up on the VM:</p>
<code>aws-deploy.nix</code>
<pre><code class="language-nix">{ modulesPath, ... }:
{
  imports = [
    &quot;${modulesPath}/virtualisation/amazon-image.nix&quot;
    # path to your config
    ./configuration.nix
  ];
}
</code></pre>
<p>TODO: Setup CI here and Eval NixOS to pre-fill the cache.</p>
<p>With that in hand, we can now write a bit of Terraform code:</p>
<p>``<code>plain text
variable "name" {
  description = "Name prefix"
}</p>
<h1>Generate a SSH key-pair</h1>
resource "tls_private_key" "machine" {
  algorithm = "RSA"
}
<h1>Record the SSH public key into AWS</h1>
resource "aws_key_pair" "machine" {
  key_name   = var.name
  public_key = tls_private_key.machine.public_key_openssh
}
<h1>Store the private key locally. This is going to be used by the deploy_nixos module below</h1>
<h1>to deploy NixOS.</h1>
resource "local_file" "machine_ssh_key" {
  sensitive_content = tls_private_key.machine.private_key_pem
  filename          = "${path.module}/id_rsa.pem"
  file_permission   = "0600"
}
<h1>This is the security group that will be attached to the instance</h1>
resource "aws_security_group" "machine" {
  name = var.name
}
<h1>A bunch of rules for the group</h1>
resource "aws_security_group_rule" "machine_ingress_ssh" {
  description       = "Allow SSH from everywhere"
  type              = "ingress"
  from_port         = 22
  to_port           = 22
  protocol          = "tcp"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.machine.id
}
<p>resource "aws_security_group_rule" "machine_ingress_http" {
  description              = "Allow HTTP from everywhere"
  type                     = "ingress"
  from_port                = 80
  to_port                  = 80
  protocol                 = "tcp"
  cidr_blocks              = ["0.0.0.0/0"]
  security_group_id        = aws_security_group.machine.id
}</p>
<p>resource "aws_security_group_rule" "machine_egress_all" {
  description       = "Allow to connect to the whole Internet"
  type              = "egress"
  from_port         = 0
  to_port           = 0
  protocol          = "-1"
  cidr_blocks       = ["0.0.0.0/0"]
  security_group_id = aws_security_group.machine.id
}</p>
<h1>Permissions for the AWS instance</h1>
data "aws_iam_policy_document" "machine" {
  statement {
    sid = "1"
<p>actions = [
      "s3:ListAllMyBuckets",
      "s3:GetBucketLocation",
    ]</p>
<p>resources = [
      "arn:aws:s3:::*",
    ]
  }
}</p>
<h1>A bunch of IAM resources needed to give permissions to the instance</h1>
resource "aws_iam_role" "machine" {
  name = var.name
<p>assume_role_policy = <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Effect": "Allow",
      "Sid": ""
    }
  ]
}
EOF
}</p>
<p>resource "aws_iam_role_policy" "machine" {
  name   = var.name
  role   = aws_iam_role.machine.name
  policy = data.aws_iam_policy_document.machine.json
}</p>
<p>resource "aws_iam_instance_profile" "machine" {
  name       = var.name
  role       = aws_iam_role.machine.name
  depends_on = [aws_iam_role_policy.machine]
}</p>
<h1>The actual AWS instance</h1>
resource "aws_instance" "machine" {
  # Base image to start the instance with
  ami                  = module.nixos_image.ami
  iam_instance_profile = aws_iam_instance_profile.machine.id
  instance_type        = "c5.large"
  key_name             = aws_key_pair.machine.key_name
  security_groups      = [aws_security_group.machine.name]
  tags                 = { "Name" = var.name }
<p>root_block_device {
    volume_type = "gp2"
    volume_size = "50" # GiB
  }</p>
<p>lifecycle {
    create_before_destroy = true
  }
}</p>
<h1>This deploys the NixOS configuration onto the VM</h1>
module "machine_deploy" {
  source = "git@github.com:tweag/terraform-nixos.git//deploy_nixos?ref=dbba649db86d90166d7573bb60ba40ac790e17d1"
<p># FIXME: pin nixpkgs
  # NIX_PATH = "nixpkgs=${path.module}/../../nix/nixpkgs.nix"
  nixos_config = "${path.module}/configuration.nix"</p>
<p>target_host          = aws_instance.machine.public_ip
  target_user          = "root"
  ssh_private_key_file = local_file.machine_ssh_key.filename</p>
<p>triggers = {
    # Force a new deployment if the instance ID has changed. The ID changes if
    # the instance is re-created for example.
    machine_id = aws_instance.machine.id
  }
}
</code>`<code></p>
<h2>Downsides</h2>
<ul><li>No auto-scaling: only a single VM gets configured</li>
<li>No auto-healing: if the VM goes down, it takes another </code>terraform apply` to re-deploy the system configuration.</li>
</ul>
<h2>Upsides</h2>
<ul><li>Simple setup.</li>
<li>Direct feedback on deployment.</li>
<li>It's easy to migrate this auto-scaling in the future.</li>
</ul>
<h2>TODO</h2>
<ul><li>Use CI + Cachix to pre-build the NixOS machine.</li>
<li>Write a terraform aws_image_nixos_custom module for auto-scaling scenarios.</li>
<li>Secret management → use SSM</li>
<li>Better SSH key management?</li></ul>]]></description>
  </item>
  <item>
    <title>Meetup NixOS Suisse Romande - Exercise</title>
    <link>https://zimbatm.com/notes/meetup-nixos-suisse-romande-exercise</link>
    <guid>https://zimbatm.com/notes/meetup-nixos-suisse-romande-exercise</guid>
    <pubDate>Wed, 20 May 2020 00:00:00 +0000</pubDate>
    <description><![CDATA[<p>Pour cet episode nous allons faire un petit exercise pour apprendre a utiliser NixOS.</p>
<h2>Preparation</h2>
<li>Installez VirtualBox: <a href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a></li>
<li>Telechargez l'image VirtualBox de NixOS: <a href="https://channels.nixos.org/nixos-20.03/latest-nixos-x86_64-linux.ova">https://channels.nixos.org/nixos-20.03/latest-nixos-x86_64-linux.ova</a></li>
<li>Chargez l'image dans VirtualBox avec "Fichier" → "Importer l'Appliance" → "Importer"</li>
   Une fois demarer, vous pouvez vous connecter avec l'utilisateur <code>demo</code> et mot the passe <code>demo</code>. Il egalement est possible d'obtenir l'acces administrateur avec la commande <code>sudo -i</code> une fois connecter.
<h2>Exercise 1 - installation de paquet</h2>
<p>Ouvrez la configuration systeme avec <code>sudo nixos-rebuild edit</code> et changez la configuration.</p>
<p>Trouvez un paquet a installer en parcourant <a href="https://nixos.org/nixos/packages.html?channel=nixos-20.03">https://nixos.org/nixos/packages.html?channel=nixos-20.03</a> et ajoutez le a <code>environment.systemPackages</code>.</p>
<p>Par exemple:</p>
<pre><code class="language-nix">{
  environment.systemPackages = [
    pkgs.hello
  ];
}
</code></pre>
<p>Une fois modifier, enregistrez le fichier et executez <code>sudo nixos-rebuild switch</code> pour activer la nouvelle configuration.</p>
<p>Vous pouvez tester que la nouvelle configuration se soit activee en executant le programme dans la ligne de command:</p>
<pre><code class="language-nix">$ hello
Hello, world!
</code></pre>
<h2>Exercise 2 - configuration de service</h2>
<p>Explorez les options dans <a href="https://nixos.org/nixos/packages.html?channel=nixos-20.03">https://nixos.org/nixos/packages.html?channel=nixos-20.03</a> et choisissez un service a installer.</p>
<p>Tout comme dans l'exercise 1 nous allons ouvrir le fichier de configuration avec <code>sudo nixos-rebuild edit</code> , modifier et enregistrer les changements, et finalement executer <code>nixos-rebuild switch</code> pour activer la configuration.</p>
<p>Si vous trouvez la syntaxe nix confuse, n'hesitez pas a consulter ce petit guide (en anglais): <a href="https://github.com/tazjin/nix-1p">https://github.com/tazjin/nix-1p</a></p>
<p>Prenez notes des etapes et problemes que vous avez rencontrer.</p>
<h2>Exercice 3 - retour sur une ancienne configuration</h2>
<p>Redemarrer la VM avec <code>reboot</code>. Dans le menu de demarrage vous trouverez toutes les version precedentes de la configuration systeme.</p>
<p>Chosissez une ancienne version et demarrer la machine avec. Constatez que vous etes bien retourner dans un etat precedent en consultant l'etat systeme. (par exemple avec <code>systemctl status</code> ou en executant un des programmes.</p>
<pre><code class="language-nix">$ hello
hello: command not found
</code></pre>
<h2>Exercise 4 - reclamez de l'espace</h2>
<p>Redemarrer le systeme a la derniere configuration ou lancez <code>nixos-rebuild switch</code>.</p>
<p>Lancez <code>df -h</code> pour voir combien d'espace disque est disponible.</p>
<p>Nous allons maintenant supprimer les anciennes configurations. Lances <code>sudo nix-collect-garbage -d</code> pour effacer les vieux profiles et fichiers associes.</p>
<p>Voila, vous pouvez maintenant constanter que vous avec plus d'espace disponible en lancant <code>df -h</code> a nouveau.</p>]]></description>
  </item>
</channel>
</rss>