--- title: "Nix Flakes: Exposing and using NixOS Modules" date: 2022-03-31 series: nix-flakes tags: - nixos vod: twitch: https://www.twitch.tv/videos/1437346416 youtube: https://youtu.be/wCZ9SwmgSck --- Nix flakes allow you to expose NixOS modules. NixOS modules are templates for system configuration and they are the basis of how you configure NixOS. Today we're going to take our Nix flake [from the last article](/blog/nix-flakes-2-2022-02-27) and write a NixOS module for it so that we can deploy it to a server. [If you haven't read the other articles in this series, you probably should. This article builds upon the previous ones.](conversation://Mara/hacker) NixOS modules are the main building block of how NixOS servers are configured. They are like lego blocks that help you build up a server from off the shelf parts. A module describes a desired system state and they build off of eachother in order to end up with a more elaborate result. [You can think about them like Ansible playbooks, but NixOS modules describe the desired end state instead of the steps you need to get to that end state.](conversation://Mara/hacker) NixOS modules are functions that take in the current state of the system and then return things to add to the state of the system. Here is a basic NixOS module that enables [nginx](https://nginx.org/): ```nix { config, pkgs, lib, ... }: { config = { services.nginx.enable = true; }; } ``` This function takes in the state of the world and returns additions to the state of the world. This will use the nginx module that ships with NixOS to give you a basic nginx setup that has the upstream default configuration in it. NixOS has a way to run other instances of NixOS with [NixOS containers](https://nixos.org/manual/nixos/stable/index.html#ch-containers). We can use them to test our NixOS module as we write it. [This probably won't work on a non-NixOS machine. You will need to install NixOS in order to test this. For an easy way to do this, see nixos-infect, a script you can put into a cloudconfig when spinning up a new server. You can also install NixOS manually in a VM, but for now it may be better to use a cloud server as the path of least resistance. Installing NixOS with a flake will be a part of a future article in this series.](conversation://Mara/hacker) In Nix you can merge two attribute sets using the `//` operator. This allows you to add two attribute sets into one larger one, such as like this: ``` nix-repl> { foo = 1; } // { bar = 2; } { bar = 2; foo = 1; } ``` We will use this to add the container configuration to the flake at the end of the flake.nix file. At the end of your flake.nix (just before the final closing `}`), there should be a line that looks like this: ```nix }); ``` This is what terminates the `outputs` declaration from all the way at the top. In order to add the container configuration, you should change this to look like this: ```nix }) // { }; ``` Then we can add the container configuration to the flake: ```nix }) // { nixosConfigurations.container = nixpkgs.lib.nixosSystem { system = "x86_64-linux"; modules = [ ({pkgs, ...}: { # Only allow this to boot as a container boot.isContainer = true; networking.hostName = "gohello"; # Allow nginx through the firewall networking.firewall.allowedTCPPorts = [ 80 ]; services.nginx.enable = true; }) ]; }; }; ``` This will create a container (with the hostname "gohello") that starts nginx and allows traffic to go to nginx on TCP port 80. You can start up the container with the `nixos-container` command: ```console $ sudo nixos-container create gohello --flake .#container host IP is 10.233.1.1, container IP is 10.233.1.2 ``` Then you can start the container with this command: ```console $ sudo nixos-container start gohello ``` And then we can try to connect to nginx to see if it's working: ```console $ curl http://10.233.1.2
If you see this page, the nginx web server is successfully installed and working. Further configuration is required.
For online documentation and support please refer to
nginx.org.
Commercial support is available at
nginx.com.
Thank you for using nginx.
``` We have nginx! Now that we have our container to test with, let's write the configuration for the service. At a basic level we need the following things: - A systemd unit for orchestrating the HTTP server process - nginx configuration to reverse proxy to that HTTP server Above the container definition, add this basic NixOS module template: ```nix nixosModule = { config, lib, pkgs, ... }: with lib; let cfg = config.xeserv.services.gohello; in { options.xeserv.services.gohello = { enable = mkEnableOption "Enables the gohello HTTP service"; }; config = mkIf cfg.enable { }; }; ``` This will create a NixOS module that will only be enabled when the configuration setting `xeserv.services.gohello.enable` is set to `true`. Everything else we do here will build on this. [You can and probably do want to change the namespace `xeserv` here, it is a placeholder that is not likely to conflict with anything else.](conversation://Mara/happy) Create a basic systemd service with this template: ```nix config = mkIf cfg.enable { systemd.services."xeserv.gohello" = { wantedBy = [ "multi-user.target" ]; serviceConfig = let pkg = self.packages.${system}.default; in { Restart = "on-failure"; ExecStart = "${pkg}/bin/web-server"; DynamicUser = "yes"; RuntimeDirectory = "xeserv.gohello"; RuntimeDirectoryMode = "0755"; StateDirectory = "xeserv.gohello"; StateDirectoryMode = "0700"; CacheDirectory = "xeserv.gohello"; CacheDirectoryMode = "0750"; }; }; }; ``` [NOTE: You will want to be sure to do the following things to your copy of gohello: