Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

So, there is no /nix/etc - anything in /nix is immutable. I haven't done this and hopefully someone will correct me if I get the details wrong, but I think the way you'd make this work within nix is you'd make your own package, let's call it "my-sshd-config," and it depends on a certain version of sshd. Then you'd have /nix/store/abcd1234-my-sshd-config-1.0/etc/sshd_config. (where "abcd1234" is a hash of everything in your package, including the specific version of ssh you depend on)

If you want to upgrade sshd and the config changes, you'd make my-sshd-config 2.0, and nix would put its files in /nix/store/efgh5678-my-sshd-config-2.0/etc/ssh_config. You could have both of these installed at the same time.

Then, it's up to you to stop the sshd running out of my-sshd-config 1.0 and to start the one running out of my-sshd-config 2.0. Once you're confident of the upgrade, you can then tell Nix to clean up my-sshd-config 1.0, but that's just removing files, since you've already stopped the service.

(We're about to upgrade sshd at work next week and I wish we had something like this, honestly, both so I could distribute the files to machines well before the upgrade and so that it's easy to roll the upgrade back - just stop the new sshd and start the old one. Then there wouldn't be concerns about config drift, forgetting to revert files, etc. The only configuration that I'd have to manage is which one is active.)

Most people who use Nix without NixOS aren't running services like sshd out of it, but NixOS will do basically this for handling service upgrades. Conceptually NixOS gives you one mutable configuration knob, which is "what is all the systemwide stuff for this system." You can depend on some particular sshd and its config, some particular init system and its config, some particular syslogd and its config, etc. If you want to change any config, you make a new package with the changed config, and then flip the one mutable thing to point to it instead. (This does mean that it's easy to roll back your system to an old state if you realize you made a mistake!)

There is a limitation here, which is that Nix can't do a lot about config in your home directory, like ~/.ssh/ssh_config (for the SSH client). You'll have to be careful with that just like you would with upgrades in general. Alternatively, you could make your own Nix packages that include client config, and avoid storing config in your home directory.



Oh dear... so you have to write a Nix package (and learn how the language and package management work) just to modify global config files, instead of just editing them? That sounds like an absolute nightmare for a local machine, though possibly a great tool for automated systems.

Also, if there's no /nix/etc... then what is Nix modifying? sshd (or any other more common program; I'm just using sshd as an example to understand the rest of the system) won't magically know to look at /nix/store/efgh5678-my-sshd-config-2.0/etc/ssh_config, right? I thought you'd at least need a symlink like /nix/etc/ssh/sshd_config to link to it. Unless you're saying Nix modifies your system's /etc/ssh/sshd_config directly and makes it a symlink to the immutable Nix store? In which case, wouldn't it just trample everything your own distro's package manager does in that folder?


NixOs manages configuration in a really elegant way with modules. Modules are a structured way of combining different sources of configuration.

E.g. you might need to configure a list of users on the system. You might manually configure your user in your top-level module:

  users = [ "dataflow" ];
But if you have postgres enabled, then it might configure a user in it's own module.

  users = [ "postgres" ];
When the modules are evaluated and combined, you can end up with a list that contains both:

  users = [ "postgres" "dataflow" ];
Compare this to conventional distros. If you edit your distro's default config file, and the distro updates the default config file, how will your modified config be updated? E.g. Arch just puts the new config in a .pacnew file, and you will just keep using your old config. pacman prints a line out, but it often scrolls off the top of the screen and it's easy to miss. It's up to you to manually merge those configs now. E.g. my arch system has 22 .pacnew files in /etc, and occasionally things break or don't work as well because updates aren't applied to the configs. Debian has debconf, but that always seemed more like a set of ad-hoc workarounds than a solution.


It's not done that way.

If you install Nix on Linux or OS X, you just have packages installed but configuration (if any) you have to edit manually like you would in another Linux distro.

If you use NixOS in addition to packages it also uses modules. Modules take care of setting up services, adding users/groups etc and placing the configuration in right place. You use module configuration to configure the application, and Nix generates actual config.

Here[1] is example of how PostgreSQL is configured.

[1] https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/s...


I think most people who use Nix on Redhat, Debian, Ubuntu and so on probably don't use it to provide system services like `sshd`.

A more common use would be to install a package in a version that isn't available, or where you would need to link to a different version of the library than is available on your system. Nix makes the dreaded autotools dance much more bearable, even for someone like me who doesn't really know any C++.

Another common situation is when you are a software developer working on many projects with overlapping but slightly different dependencies where slightly different sometimes turns out to be incompatible. Ideally you could handle this with `virtualenv` or your language's equivalent, but I often find I end up with multi-language projects where this just doesn't work. For example, if one of my Python libraries also wants some C libraries in a particular version. Nix is ideal for such a situation.

Docker and docker-compose can work too, but, eh, it's kind of slow, and it'll probably break a few months down the line when all the JavaScript library authors decide that building things with `plink` is gauche and everyone should move to `smooch` instead, or when CRAN decides it doesn't want to keep these old versions of libraries any more.

And, what do you do about your linting and formatting and code completion tools? Do those go inside Docker too, because then the Docker slowness really shows up.

(If you don't have these sorts of problems often, then Nix may not help you all that much. I guess reducing the amount of stuff you install and uninstall with `apt` or `rpm` probably still reduces the rate at which your system rots though.)

Having said all that, if you decided that you did want to use Nix to provide packages for your system services, then there's no difficulty in specifying a mutable config file in /etc/wherever. That part would be handled in your init system config (say, a systemd unit) instead of inside Nix.

It's for NixOS that you would use the Nix language to configure everything, and I've found that experience to vary quite a lot.

I've used some obscure or packages which didn't expose enough of the configuration to do what I needed by default, and so I had to make my own version. This can be pretty annoying and time consuming.

Nixpkgs' maintainers get a lot of work done individually because of the tooling, but there just aren't enough of them yet.

On the other hand, I've found that configuring popular things like Nginx and Postgres is often much easier in the Nix language.

It's much harder to make a small syntax error, you have access to some helpful templating constructs, and you can re-use the same constants for different config files in different packages.

Similarly, for NixOS, configuring your systemd services is really well handled.

Wow, that ended up long winded. Sorry.


Interestingly, sshd WILL magically know to look at /nix/store/efgh5678-my-sshd-config-2.0/etc/ssh_config. Usually you would use sshd as a systemd service (possible to do and manage with Nix in Ubuntu, but why would you?). If so, you would write the systemd configuration using Nix and the service file deployed to NixOS (or Ubuntu, imagine deploying some custom/proprietary service you want isolated from whatever else the client has on the machine) would have all the long hashes auto-magically inserted to produce this (this is on my machine, all i had to write was "services.openssh.enable = true;" but additional configuration is available, see https://nixos.org/nixos/options.html#services.openssh.):

  [Unit]
  After=network.target
  Description=SSH Daemon
  X-Restart-Triggers=/nix/store/d4ys2c8kzzcp3g4fv3ivy7a5nkayg7w2-sshd.conf-validated

  [Service]
  Environment="LD_LIBRARY_PATH=/nix/store/71mr6yjmia7y8lw4g5ghk5ag9yq5ir2i-nss-mdns-0.10/lib:/nix/store/zbxfs37qjj6ddrfnzrdnxnkrvvm1ddsf-systemd-245.3/lib"
  Environment="LOCALE_ARCHIVE=/nix/store/9b725cly2a6a61vb8bgz7cyr0xr8y2av-glibc-locales-2.30/lib/locale/locale-archive"
  Environment="PATH=/nix/store/5yx7mv7md9c9nldj69inrnr7rjdkzqq3-openssh-8.2p1/bin:/nix/store/miwvn81sgbbcq5bfglr6v3pwchgsd00c-gawk-5.0.1/bin:/nix/store/ca9mkrf8sa8md8pv61jslhcnfk9mmg4p-coreutils-8.31/bin:/nix/store/hg3albf7g05ljfqrfjhd58rblimrp6ph-findutils-4.7.0/bin:/nix/store/8pajzfyqx1v7dz1znrnrc4pqj5rmnx24-gnugrep-3.4/bin:/nix/store/jpqlmf3wqg281j8fdz50kjl525pfsxjc-gnused-4.8/bin:/nix/store/zbxfs37qjj6ddrfnzrdnxnkrvvm1ddsf-systemd-245.3/bin:/nix/store/5yx7mv7md9c9nldj69inrnr7rjdkzqq3-openssh-8.2p1/sbin:/nix/store/miwvn81sgbbcq5bfglr6v3pwchgsd00c-gawk-5.0.1/sbin:/nix/store/ca9mkrf8sa8md8pv61jslhcnfk9mmg4p-coreutils-8.31/sbin:/nix/store/hg3albf7g05ljfqrfjhd58rblimrp6ph-findutils-4.7.0/sbin:/nix/store/8pajzfyqx1v7dz1znrnrc4pqj5rmnx24-gnugrep-3.4/sbin:/nix/store/jpqlmf3wqg281j8fdz50kjl525pfsxjc-gnused-4.8/sbin:/nix/store/zbxfs37qjj6ddrfnzrdnxnkrvvm1ddsf-systemd-245.3/sbin"
  Environment="TZDIR=/nix/store/wmry9mqmimq8ib8ijli4g1yx92gxjli5-tzdata-2019c/share/zoneinfo"
  
  
  X-StopIfChanged=false
  ExecStart=/nix/store/5yx7mv7md9c9nldj69inrnr7rjdkzqq3-openssh-8.2p1/bin/sshd -f /etc/ssh/sshd_config
  ExecStartPre=/nix/store/1mzzy0dwjzy6kcwad7q79pvc444yn288-unit-script-sshd-pre-start
  KillMode=process
  Restart=always
  Type=simple
No symlinks to /etc/sshd. This service would be independent from other software on the host system, other than PID1 managing it.


Interesting, thank you! So that means programs are sometimes patched to look for configs in nonstandard locations generated by Nix during installation time. But then if I wish to change any of those configs (maybe to change one of the defaults)... I have to copy them, make my modifications, generate my own package for them, and install them to wire them in as substitutes for the existing packages. Then when the upstream package changes the config file, I have to generate a new package with all the conflicts manually resolved, right? It seems a bit of an arduous process, though I do see the appeal.


> programs are sometimes patched to look for configs in nonstandard locations generated by Nix

This may be true, but typically the NixOS module will specify the generated config file via the command line [0] or symlink the generated config to the default location in /etc [1]. I don't believe it's terribly common to patch programs to have different config file paths in nixpkgs.

If you're just using plain Nix on a foreign distro, and not, say, home-manager or similar, it's up to you to provide your own configuration including service units. Presumably you could use Nix for this as well, but I'm not terribly familiar with using Nix on foreign distros.

[0]: https://github.com/NixOS/nixpkgs/blob/de493bd74921139860624e... [1]: https://github.com/NixOS/nixpkgs/blob/de493bd74921139860624e...


Woah, I'm lost here. The idea is that it uses /etc/ssh/sshd_config as its input? (How do you handle upgrades, then?) What is d4ys2c8kzzcp3g4fv3ivy7a5nkayg7w2?


In NixOS, typically the config files will be generated from scratch using the Nix language, see for example sshd [0] or bind [1].

`d4ys2c8kzzcp3g4fv3ivy7a5nkayg7w2` is a hash of the inputs to a derivation (a package in Nix terms).

In a sense, at it's lowest level, a derivation is a function `f(x) -> y` where `x` is some Nix expressions (including the inputs and how to build it, often in bash) and `y` is a nix store path. The nix store path includes the hash which is a hash of `x`.

For bind, the config file itself is a derivation, it just uses a plain string (interpolated with variables via Nix) into the writeText wrapper.

[0]: https://github.com/NixOS/nixpkgs/blob/de493bd74921139860624e... [1]: https://github.com/NixOS/nixpkgs/blob/de493bd74921139860624e...


I haven't used NixOS, so take me with a grain of salt - it looks like the actual way of doing this in NixOS is that you have a systemwide configuration file that you can edit, and running "nixos-rebuild" will pick up your changes and automatically make the packages you need. See "Changing the Configuration" in the manual: https://nixos.org/nixos/manual/index.html So, at the end of the day, there is a Nix package, but you don't interact with it by using the packaging tools, you interact with it by editing a file and then running a command that snapshots the current version of the file and does everything for you.

If you're running your own services, you don't have to go through Nix packaging, you can handle this yourself if you have a way you prefer. For example, if you're running WordPress out of your home directory, you can have a git repo with some config files and a script that runs a particular version of Apache, MySQL, PHP, WordPress, etc. out of Nix. If you want to upgrade, edit the versions in the script and also the config files, then tell them all to restart. You can't rely on having a single systemwide version of Apache like you can with a traditional distro, but on the other hand, you aren't tied to whatever version the system wants to give you, you can keep running the current version until you're ready to upgrade.

I'm looking forward to Shopify's part 2 blog post to see what they do exactly. :)

> sshd (or any other more common program; I'm just using sshd as an example to understand the rest of the system) won't magically know to look at /nix/store/efgh5678-my-sshd-config-2.0/etc/ssh_config, right?

Conceptually, my-sshd-config includes a script (or systemd unit, or whatever) that has a reference to a particular version of sshd and also has your config, and so it would run "/nix/store/aaaa1111-openssh-9.0/bin/sshd -f /nix/store/efgh5678-my-sshd-config-2.0/etc/sshd_config". The openssh package doesn't know about you, and you can't change it, but you know about it. (In other words, the inputs that resulted in the hash efgh5678 include "aaaa1111-openssh-9.0".)

It looks like the actual way you do this in NixOS is that the sshd package provides a function in the Nix language which takes some config as input and spits out a package as output. So your systemwide config file loads the sshd package and calls a function, which returns a systemd unit with the right filenames. https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/s...


Wow interesting, okay. I think I'll probably have to give it a shot at some point to try it out. Hopefully it'll live up to the expectations :-) thanks a ton for all the explanations!





Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: