Closed CMCDragonkai closed 10 years ago
Apparently Nix doesn't support this atm. However one can set timeout to be zero.
Ah, I would bet that the default grub timeout is what accounts for the delay during vagrant up
, where it takes a while for SSH to be available. Totally didn't cross my mind. I'll probably set my default to 0 seconds for my box.
Thanks for pointing this out!
It's more than that. NixOS takes a while to boot. CoreOS is one of the fastest booters, although nothing compares to HalVM or Erlang on Xen.
What do you think of slimming down the distribution? Perhaps uninstalling system packages during the provisioning phase? Is this actually possible? I would like a CoreOS lookalike for NixOS. I'm thinking of removing anything that's not necessary for the functioning of the OS like perl, python...etc.
What do you think of slimming down the distribution?
That would be awesome, but might be difficult to do right now.
I think the current focus in NixOS is expanding support for more declarative control around provisioning services using its module approach, and adding/refining packages. It would be awesome to slim-down/speed-up the official NixOS distro, but it might be difficult to get support given the current priorities.
As an alternative, I could see using light-weight OSes like CoreOS along with Nix, perhaps taking a similar approach to what Zef Hemel proposes here:
Just a question regarding the whole declarative control. Is it right to think of the system environment packages specified in the configuration.nix as declaratively controlled but the user specific packages that are installed via nix-env -i
are imperatively controlled?
When I first got started with nix, I was very interested in the declarative concept. I thought that all packages including user packages would be declaratively controlled. But this is not the case right? I was expecting a configuration.nix for a specific user, and there you would add your packages, and if you removed them, Nix will auto uninstall them. Right now it seems user based packages have no advantages in them and seems very similar to the other package managers like apt... etc.
I guess my question would be what's the best way to take advantage of NixOS and Nix functional properties for the running of servers. As you can probably guess, I'm not going to be using Nix for general desktop work, but I'm investigating it for the purposes of running highly scalable servers, and I'm attracted to functional immutability concepts.
Regarding the whole aim of NixOS, I understand. Just wondering if you knew a way to remove system packages from NixOS that are unnecessary. I think one of the reasons CoreOS boots faster is because it doesn't have a package manager.
Just a question regarding the whole declarative control. Is it right to think of the system environment packages specified in the configuration.nix as declaratively controlled but the user specific packages that are installed via nix-env -i are imperatively controlled?
I'd start with what it means to be declarative. To my mind, that means saying what you want, vs how to get it. Let's also compare with the typical way servers are provisioned, using a web app as an example; you:
In the steps above, you can see that we're explicitly wiring up all of the dependancies. This could be automated to some degree with lengthy bash scripts or, better yet, something like Chef/Puppet/Ansible; however, those solutions fall short in many ways.
Getting back to being declarative, you want to be able to say "run app1 and app2", and then the necessary users are created, packages are installed, PHP is built, etc. On any server that you want to run app1 or app2, you ought to be able to do so with a single line in your configuration.nix:
app1.enable = true;
You don't want to worry about merging configuration sections for nginx into one valid file, or merging the necessary users (or removing old unused users), or manage multiple builds of PHP, or the creation/deletion of systemd units.
You can avoid all of that stuff using NixOS modules. You can have ten apps specify a fragment of nginx configuration, and they all get merged together. Any necessary users are created, and old ones are pruned. Prerequisite packages are automatically installed - so if app1 depends on nginx, nginx will be installed and enabled if app1 is enabled. Due to the Nix language being purely functional, you don't have to worry about part of app2's setup overwriting files/data/configs that app1's.
The module system in NixOS is where the real power lies. If you haven't done so already, I would highly suggest the exercise of writing your own module for a little project - perhaps a super simple CRUD app with a SQL database backend and a proxy in the front. You can see an example of a minimal module.nix in Zalora's sproxy repo: https://github.com/zalora/sproxy/blob/master/module.nix
For reference: https://nixos.org/wiki/NixOS:Modules http://nixos.org/nixos/manual/#sec-writing-modules
So, yeah - any use of nix-env -i
should, ideally, be reserved for development/desktop use. You also want to avoid specifying environment.systemPackages
; instead, what you want is for your modules to register systemd units via systemd.services
, and those units will reference packages by their store path. A great example of this is the nginx module:
https://github.com/NixOS/nixpkgs/blob/29027fd1e12461fc5ff5722bea79df7ff4299599/nixos/modules/services/web-servers/nginx/default.nix#L91-104
If you have nginx services.nginx.enable = true;
in your config, nginx will be built, a systemd unit for nginx will be created so that nginx starts on boot, the nginx user and group will be created, and both the config file and the nginx binary will be referenced directly from the nix store, rather than cluttering some global config folder.
To me, that's pretty revolutionary when it comes to configuration management. If team A is working on a project to be used by team B, team A can hand off a module that completely specifies all dependancies and exposes simple, declarative options to be set by team B. Team B never needs to worry about how it all works, they just use it. They also never have to worry about the module trying to write files to nonexistent folders or overwriting preexisting files, because all of the requisite config files get stored in the nix store, prefixed by their hash just like any other derivation.
Awesome answer. Thanks. It really made me understand the difference between the configuration.nix and nix-env -i
.
Just a question, it seems that I have to submit modules to the nixpkgs in order to get them available in main channels. However I'm planning for some private modules, is it possible to host a private nixpkgs channel so I can acquire those custom modules without waiting for the guys at nixpkgs project to accept my PR and backport it to whatever I'm using?
You don't need to fork nixpkgs (although you can; you'd then use the fork like: nixos-rebuild switch -I nixpkgs=/path/to/my/nixpkgs
); you can just write imports = [ ... module paths ... ]
in your configuration.nix
, then you could store your own modules in your private repository. It's not immediately obvious, but the configuration.nix
file in this repo and all of files that it imports are modules too, they just happen to be really boring ones that merely configure other interesting modules - so while they don't contain config
or options
attributes (as described in the documentation), they could if I decided to.
(Thanks for all the questions, by the way. Writing about this helps solidify my own understanding.)
I know that there's a nix-channels command: https://nixos.org/wiki/Install/remove_software#nix-channels
I would like to host my own channel on the cloud, so I don't have to clone a nixpkgs into every single computer. That way I can just add the new nix-channel, and install away. (Do you know Nix automatically merges multiple channels together in order to acquire packages, or do you have to specify a particular channel? This would be important if I'm mixing and matching between private modules and public modules in a single configuration.nix file.)
Is there some sort of specification or standard for a nix-channel? So I can self-host it somewhere. Even better if it could have some sort of authentication.
Ah, I should have paid a little more attention to your question: you asked specifically about creating a nix channel, which I didn't really answer. That's a good question. I haven't created a channel before, and I don't know what all that entails, nor do I know about the pros/cons compared to just pulling down a repo and passing -I nixpkgs=...
. I intend to go with the latter for some real world deployments that I'm working up to, and maybe some day I'll try out the channel approach too.
Here we go; these two parts of the docs mention how channels work: http://nixos.org/nix/manual/#sec-nix-channel http://nixos.org/nix/manual/#sec-channels
So it looks like channels are made up of two things:
url/nixexprs.tar.bz2
- the bzipped Tar archive of nix expressions.url/MANIFEST
- the binary cache created by using nix-push.As for security, I don't know if there's an auth mechanism, but one solution would be to have one node serve the channel behind the firewall. That falls down with multiple LANs though, if you're shooting for one single source of truth... I'd love to know how other organizations use NixOS in production.
For these sorts of tasks (configuring channels, invoking nixos-rebuild) I could imagine Chef et al would still be very useful. NixOS subsumes many of those functions, but the few remaining bits of statefullness could still use some help from outside automation.
I'm going to be attempting to build my own modules. Looking at the nginx service example, does that mean that it's possible to do this:
environment.systemPackages = with pkgs; [
nginx
];
or
services.nginx.enable = true;
I'm guess the above simply installs nginx but doesn't run it, whereas the bottom installs nginx AND runs it, therefore not requiring environment.systemPackages to first install it?
This also corresponds with the nixpkgs structure:
Here's where services go: https://github.com/NixOS/nixpkgs/tree/master/nixos/modules/services
Here's where packages go: https://github.com/NixOS/nixpkgs/tree/master/pkgs
If I create a module inside the services area, do I need to a corresponding pkgs module as well? Or does the services area supersede the pkgs module? Or are they separate?
For example here we have the MongoDB pkg: https://github.com/NixOS/nixpkgs/blob/master/pkgs/servers/nosql/mongodb/default.nix and here we have the MongoDB service: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/services/databases/mongodb.nix
Also your example of:
app1.enable = true;
Makes me think that one can create custom top level modules?
Right now I can use "environment.etc.etc" or "nixpkgs.etc.etc" or "services.etc.etc". I'm assuming these are all built in namespace/object/functions. Supposing that app1.enable = true;
is possible, then are you suggesting that I can create my own top level module/namespace/object/functions and make it available to my configuration.nix? After all app1
does not currently exist as a built in object.
In most languages, one has to import all those objects before using them. Nix expressions seem to do a similar thing, but yet I am confused as this thing:
{ config, pkgs, ... }:
I suppose the "..." refers to everything else... Then while bother with config
or pkgs
. Whey not just { ... }:
? Where does Nix import those objects, or are they all global objects?
I'm guess the above simply installs nginx but doesn't run it, whereas the bottom installs nginx AND runs it, therefore not requiring environment.systemPackages to first install it?
That's right. In the first case, nginx will be built/downloaded (if it isn't already in the nix store), and a symlink will be placed in /run/current-system/sw/bin
(which NixOS ensures is on the $PATH, making it available systemwide). That's pretty much all environment.systemPackages
does.
The second example, however, is quite different. While it does build/download nginx, it does not place a symlink in /run/current-system/sw/bin
. Additionally, it creates the config file, nginx user/group, and the systemd units that ensure nginx is running with the configured user/group and conf file.
If I create a module inside the services area, do I need to a corresponding pkgs module as well? Or does the services area supersede the pkgs module? Or are they separate?
The actual directory layout doesn't matter much - I think you might be seeing a connection that isn't there.
I'll use one of my own repos as an example. As part of my application for a job recently, I wrote a little Haskell web application along with a supporting NixOS module: https://github.com/cstrahan/cgroups
Some quick background: the application is a FastCGI application, and I intended to make it easy to spin up the app and stick nginx in front of it. Nginx can't directly execute FCGI apps, so I need to use spawn-fcgi
to execute the app and fork off the worker process. spawn-fcgi
creates a Unix domain socket that nginx can then connect to and proxy requests to. So the dependencies look like:
nginx --> spawn-fcgi --> app
Back to your question about the relationship between pkgs
and the services/modules. If you check out the module.nix, you'll see that I have an option for the spawn-fcgi
program:
spawn_fcgi = mkOption {
default = pkgs.callPackage ./spawn-fcgi.nix { }; # XXX: rework after #2695
type = types.package;
description = "The built spawn-fcgi to use";
};
Note the default
attribute. At the time, my PR to add the spawn-fcgi
package had not yet been merged, so I needed to supply that myself; to do so, I just used the same pkgs.callPackage <PATH> { ... default attrs ...}
mechanism that's used in pkgs/top-level/all-packages.nix for all the other packages, and I supply a relative path to ./spawn-fcgi.nix. So you can see that you don't really need to use pkgs
, but you can if what you need is there. pkgs
is just the attribute set from <nixpkgs/pkgs/top-level/all-packages.nix>
, so if you add a package to your own channel, it'll be available to your modules through pkgs
.
I do the same thing for the app itself:
package = mkOption {
default = pkgs.callPackage ./default.nix { };
type = types.package;
description = "The built cgroups package";
};
By making package
and spawn_fcgi
options, the user can override either if desired (maybe they want to use bleeding-edge versions, or apply custom patches). If I didn't want to make either package configurable, I could have just as well put a let package = pkgs.callPackage ./default; in ...
at the top of the file, and used that instead of cfg.package
.
Makes me think that one can create custom top level modules?
Correct. There are some conventions though, like placing services under services
, as in services.nginx
, or in my example services.cgroups_api
.
Right now I can use "environment.etc.etc" or "nixpkgs.etc.etc" or "services.etc.etc". I'm assuming these are all built in namespace/object/functions.
They're not built-in in the primitive/magical sense, but they are built-in in so far as they are imported implicitly before your configuration.nix
is evaluated. You can see the list of default imports in nixos/modules/module-list.nix. If you look in nixos/lib/eval-config.nix, you see how NixOS combines this list of implicit imports along with your configuration.nix
to set up your machine.
A quick refresher on Nix syntax; when you see something like the following:
{
services.nginx.enable = true;
services.mysql.enable = true;
}
That's just syntactic sugar for the following:
{
services = {
nginx = {
enable = true;
};
mysql = {
enable = true;
};
};
}
So when you say services.foo.bar.baz = ...
, you're actually creating a brand new attribute set with an attribute named services
, which itself is a brand new attribute set with the other foo/bar/baz attrs nested inside.
A reasonable question would be "but isn't that setting a global services
variable that would override any setting from other imported files?" And the answer would be "no". In each module, you define whatever settings you want as an attrs set (in this case, a services
attrs set with a nginx
attrs set with enable
set to true
), and then all the attrs are collected from every import recursively (a.nix
might import b.nix
which might then import c.nix
, etc). Once all the files have been evaluated, the attributes are then all merged together into one big config attrs set.
After all app1 does not currently exist as a built in object.
Because all each module is doing is creating an attribute set (i.e. a dictionary/hashmap/associative-array), this is perfectly fine. When working with Nix, it's common to write nix files that have the general shape:
{ input_attr }:
{
output_attr = ... do something with input_attr ...;
...
}
The above snippet is just a function that expects an attr set with a key/value pair input_attr
and then returns an attr set with a key/value pair output_attr
. Inside the function (anything after the :
) we can reference that input_attr
. This is called "destructuring assignment". We could just as well do the following, but it wouldn't be as elegant:
arg:
{
output_attr = ... do something with arg.input_attr ...;
...
}
You see, technically, Nix only supports functions with one argument, but the destructuring assignment allows us to fake multiple named arguments. Here's an example of a function that is immediately invoked:
({ foo, baz }: ... do something with foo and baz ...) { foo = "bar"; baz = "quux"; }
In the above, the function expects and is given one argument: { foo = "bar"; baz = "quux"; }
. Inside the function, you can then reference foo
and baz
directly.
What happens if we have we provide not only foo
and baz
as arguments, but also blah
? It will throw an exception. That's because destructuring assignment by default expects the provided keys to match exactly what was given in the function signature - anything more or less is an error. What if, however, we wanted the destructuring assignment to ignore any extra keys? We can do that with ...
- the function will then ignore any extraneous args.
Now let's imagine that you want to define a function called add
that takes the sum of two numbers. You could do the following:
add = { num1, num2 }: num1 + num2;
# would be used like: add { num1 = 3; num2 = 4; }
And nix would evaluate that as 7
. While that works, it's really ugly. Fortunately, there's an FP technique called "currying" that we can use to make this prettier:
add = num1: num2: num1 + num2;
# would be used like: add 3 4
And that's much nicer. This works because the function takes it's first argument (num1) and then returns a new function that takes the second argument (num2), and then the two numbers are added. Anywhere in nix where you see someFunction arg1 arg2 arg3 etc
, that's what's going on.
I suppose the "..." refers to everything else... Then while bother with config or pkgs. Whey not just { ... }:?
pkgs
and config
are arguments that Nix gives you, and you often want to refer to them. (Remember that your module file is just on big function). If you don't use one or the other, you can leave it out.
Where does Nix import those objects, or are they all global objects?
Again, each module file is really just a big function, and config
& pkgs
are just arguments to that function.
Every file in Nix evaluates to some value. That value can be a function, a number, a string, an attribute set, etc. Aside from a set of core builtins that the interpreter always keeps in scope, every expression must be explicitly given the arguments that it needs. That's why so many files in the Nix source look something like:
{ stdenv }:
stdenv.mkDerivation { ... }
Aside from the builtins, there aren't any global variables in Nix, so to make use of stdenv
, it has to be provided by somewhere else. That's often handled with callPackage
- it takes a path to a nix file and evaluates it, expecting the result to be a function that it can hand off stdenv
and friends.
It's a funky paradigm coming from the imperative languages commonly used today (C/Java/PHP/etc). To get a solid handle on the language itself, I'd recommend checking out "Chapter 5. Writing Nix Expressions" in the Nix manual. If you nix-env -i nix-shell
, you an run nix-shell
to get an interactive REPL to play around with.
Let me know if you have any questions :). At the depth we're getting into here, it might make sense at some point to jump on a Google Hangout and toss ideas back and forth, just to reduce latency.
I want to skip the whole boot selection, usually you need to set this:
However I can only find one option inside Nix that sets up the timeout to be 0. Nothing with regards to the HIDDEN_TIMEOUT and HIDDEN_TIMEOUT_QUIET.