diff --git a/blog/nix-flakes-1-2022-02-21.markdown b/blog/nix-flakes-1-2022-02-21.markdown index 9bcc0dd..b91dbb4 100644 --- a/blog/nix-flakes-1-2022-02-21.markdown +++ b/blog/nix-flakes-1-2022-02-21.markdown @@ -136,7 +136,10 @@ Then you can look at `flake.nix` to see what's up: pkgs = nixpkgsFor.${system}; in { - go-hello = pkgs.buildGoModule { + # The default package for 'nix build'. This makes sense if the + # flake provides only one package or there is a clear "main" + # package. + default = pkgs.buildGoModule { pname = "go-hello"; inherit version; # In 'nix develop', we don't need a copy of the source tree @@ -156,11 +159,6 @@ Then you can look at `flake.nix` to see what's up: vendorSha256 = "sha256-pQpattmS9VmO3ZIQUFn66az8GSmB4IvYhTTCFn6SUmo="; }; }); - - # The default package for 'nix build'. This makes sense if the - # flake provides only one package or there is a clear "main" - # package. - defaultPackage = forAllSystems (system: self.packages.${system}.go-hello); }; } ``` @@ -197,7 +195,6 @@ Let's take a closer look at the higher level things in the flake: outputs = { self, nixpkgs }: { packages = { ... }; - defaultPackage = { ... }; }; } ``` @@ -462,7 +459,7 @@ nixosModules.bot = { config, lib, ... }: { Group = "mara-bot"; Restart = "always"; WorkingDirectory = "/var/lib/mara-bot"; - ExecStart = "${self.defaultPackage."${system}"}/bin/mara"; + ExecStart = "${self.packages."${system}".default}/bin/mara"; }; }; }; diff --git a/blog/nix-flakes-2-2022-02-27.markdown b/blog/nix-flakes-2-2022-02-27.markdown index b820885..b163fcf 100644 --- a/blog/nix-flakes-2-2022-02-27.markdown +++ b/blog/nix-flakes-2-2022-02-27.markdown @@ -19,6 +19,10 @@ chance of bitrotting. I will make every attempt to update it if things change, however flakes have been fairly consistent for a few years now.](conversation://Cadey/coffee) +[EDIT(20220327 14:13): A previous version of this article said to use +`defaultPackage` for the default package. This is deprecated and you should use +`packages.default` instead.](conversation://Cadey/coffee) + [What is a package? I've seen this term thrown around with phrases like "Nix is a @@ -180,10 +184,10 @@ web server template by defining another package: ```nix # flake.nix -# after defaultPackage packages = { + default = ...; docker = let - web = self.defaultPackage.${system}; + web = self.packages.${system}.default; in pkgs.dockerTools.buildLayeredImage { name = web.pname; tag = web.version; @@ -373,7 +377,7 @@ the systemd unit: web-service = pkgs.substituteAll { name = "web-server.service"; src = ./systemd/web-server.service.in; - web = self.defaultPackage.${system}; + web = self.packages.${system}.default; }; ``` @@ -399,7 +403,7 @@ Then you can add the bit that builds the portable service: ```nix portable = let - web = self.defaultPackage.${system}; + web = self.packages.${system}.default; in pkgs.portableService { inherit (web) version; name = web.pname; diff --git a/blog/nix-flakes-3-2022-03-31.markdown b/blog/nix-flakes-3-2022-03-31.markdown new file mode 100644 index 0000000..8dc6bc4 --- /dev/null +++ b/blog/nix-flakes-3-2022-03-31.markdown @@ -0,0 +1,364 @@ +--- +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: