Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configuration for common programs #10

Open
energizah opened this issue Jun 13, 2020 · 8 comments
Open

Configuration for common programs #10

energizah opened this issue Jun 13, 2020 · 8 comments
Labels
enhancement New feature or request

Comments

@energizah
Copy link

energizah commented Jun 13, 2020

I haven't thought this through, but what about supplementing the files= and directories= with programs= or services= which would know that

  • bluetooth=true means persist /var/lib/bluetooth and
  • networkManager=true means persist /var/lib/NetworkManager/system-connections

and so on?

This has the advantage that "what files does this program need to persist" can be figured out by the community once instead of by each user individually.

@talyz
Copy link
Collaborator

talyz commented Dec 14, 2020

Sorry for not replying earlier to this. It sounds like a great idea! I think we could even go further with it and automatically figure out what to persist based on what NixOS options are enabled, or at least default our options to the relevant NixOS option value.

@talyz talyz added the enhancement New feature or request label Dec 14, 2020
@lovesegfault
Copy link
Collaborator

One idea on how to make this work, at least for the NixOS module, would be to design it to look like

{
  environment.persistence = {
    stateDirectory = "/example/state";
    directories = [ "/etc/example"  "/var/lib/example" ];
  };
}

Then we could have a number of ++ lib.optionals config.hardware.bluetooth.enable [ "/var/lib/bluetooth" ] etc. Maybe even behind a enableDefaultDirectories = lib.mkDefault true;

The main loss here is the ability to have multiple state directories.

Another alternative is letting users set a rootState = lib.mkDefault false; attr to their persistence config, and we then assert only one config has that set, and to that one we ++ all the per-service/per-program state dirs.

What do you think @talyz?

@talyz
Copy link
Collaborator

talyz commented Dec 16, 2020

I'm leaning toward the latter alternative, but was thinking we could let the user decide this on a submodule / persistent path level, so an example config would look like

{
  environment.persistence."/persistent" = {
    presets =  {
      enable = true;
      bluetooth = false;
    };
    directories = [
      "/var/lib/additional_directory/bluetooth"
    ];
    files = [
      "/etc/my_important_file"
    ];
  };
}

The bluetooth options default value would be set to config.hardware.bluetooth.enable and its value would decide whether we add the /var/lib/bluetooth directory to the directories list.

The duplicate check should really be done by comparing all the directory and file lists across all persistent paths, making sure all items in all lists are unique. This would catch all duplicates, whether they're introduced by presets, the user or both.

@KFearsoff
Copy link

I am interested in doing that and am willing to start the process. That said, I am pretty new to the Nix ecosystem, so I am at a bit of a loss where to start. Any pointers towards the first step?

@Majiir
Copy link

Majiir commented Nov 5, 2022

Candidates for special configuration

Machine ID

This is simply defined with environment.persistence.<dir>.files = [ "/etc/machine-id" ]. Since it's such a commonly recommended configuration, it might be worth a config flag like environment.persistence.<dir>.machineId = true.

Clock file

On systems without an RTC (e.g. a Raspberry Pi), the clock file can be crucial for startup. For example, bind will fail to validate DNSSEC keys if the clock is wrong, and the service will fail to start correctly. Like machine-id, this configuration is simple (environment.persistence.<dir>.files = [ "/var/lib/systemd/timesync/clock" ]) but it could be nice to wrap it in a simple config like environment.persistence.<dir>.clock = true. Other time services like chrony also rely on persistent clock files, so this one parameter could inspect the config to see which are enabled and persist whichever clock files are needed.

SSH host keys

This snippet persists the host keys for services.openssh, which populates the hostKeys parameter even when the defaults are used:

{
  environment.persistence."<dir>".files =
    lib.concatMap (key: [ key.path (key.path + ".pub") ]) config.services.openssh.hostKeys;
}

This is nontrivial and a good candidate for defining once. A config setting like environment.persistence.<dir>.sshHostKeys = true could inspect config to see which SSH services are enabled and persist their host keys.

Questions

Aliasing

The clock file and SSH host keys are examples where the same kind of state could be managed by different services. For example, systemd-timesync or chrony could be responsible for a clock file.

  1. Should these be exposed through a single config flag (clock) or a separate flag per service?
  2. If they are exposed through a single flag, should there be a single persisted path (e.g. <dir>/clock) or a separate path per service?

Defaults

The whole point of impermanence is to manage opt-in state. However, some state is crucial to system operation in ways that users may not anticipate. Arguably, all three of the examples above are state that users may not think to persist, but which will cause serious issues if not persisted. It may be sensible to persist them by default.

  1. Should impermanence ever persist state by default?
  2. If so, how should it handle the possibility that multiple persistence directories are configured? (For example, if clock is persisted by default, which persistence directory should clock be stored in?)

@KFearsoff
Copy link

KFearsoff commented Nov 5, 2022

This is simply defined with environment.persistence.<dir>.files = [ "/etc/machine-id" ]. Since it's such a commonly recommended configuration, it might be worth a config flag like environment.persistence.<dir>.machineId = true.

I don't think bind mounts work in this case, actually: systemd generates new machine-id before the bind mount is finished. Or at least, I had that problem a few months ago. My solution was to use a symlink instead, it works great. I think we should implement #99 for NixOS as well, because there might be other software that conflicats with bind mounts.

This snippet persists the host keys for services.openssh, which populates the hostKeys parameter even when the defaults are used:

{
  environment.persistence."<dir>".files =
    lib.concatMap (key: [ key.path (key.path + ".pub") ]) config.services.openssh.hostKeys;
}

This is nontrivial and a good candidate for defining once. A config setting like environment.persistence.<dir>.sshHostKeys = true could inspect config to see which SSH services are enabled and persist their host keys.

I am not sure that works, too. I think I had problems trying to bind mount the SSH keys. I haven't tried symlinks; my solution was to redefine hostKeys.*.path from /etc/ssh/... to /persist/etc/ssh/.... We can test it once we have something going.

I can't speak about the clock file, I don't have a RPI. But my system that persist /var/lib/systemd via bind mount works fine. But you've missed one more crucial path that needs persisting, that is /var/lib/nixos. That directory keeps track of UIDs and GIDs of services; since NixOS uses dynamic users for systemd services whenever it can, it is important to persist their UIDs and GIDs to not have corrupted state on disk.

  1. Should impermanence ever persist state by default?

I think it should not, but due to a slightly different reason than what you might imagine. I think there are definitely reasonable defaults that Impermanence should suggest. I don't think it should ever enable them (unless we are talking strictly about symlinks), since bind mounts are destructive. If you've started persisting /var/lib/test when you hadn't persisted it before - /var/lib/test will be empty, since it will be bind-mounted from an empty directory /persist/var/lib/test. This should be written in caps, bold and red letters all over the documentation, and it is for this reason that I think we shouldn't enable any templates by default.

  1. If so, how should it handle the possibility that multiple persistence directories are configured? (For example, if clock is persisted by default, which persistence directory should clock be stored in?)

You brought up a good point, so don't mind if I'll use it! That problem solves itself if we just don't enable any presets by default. But I think it is a good idea to suggest using several persistence directories. Currently, I see the following categories for the data worth persisting:

  • system data (such as things discussed above) - should be persisted, shouldn't be backed up
  • service data (such as databases) - should be persisted, should be backed up
  • user data (such as photos) - should be persisted, should be backed up

I think that it's a good idea to do presets with these categories in mind. It would be also pretty nice if the docs would suggest that layout and show examples with it.

I'm not currently working on the PR that would implement presets API. I tried, but I failed miserably because I'm just not good enough at Nix. But I can share some knowledge and know-how about things that should probably go into design, so if someone makes a PR - please do ping me, I'll try to help however I can!

@KFearsoff KFearsoff mentioned this issue Nov 13, 2022
11 tasks
@KFearsoff
Copy link

I've opened a draft PR that aims to implement presets. While it's still a very early stage, feedback is very welcome #108

ambroisie added a commit to ambroisie/nix-config that referenced this issue Oct 25, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Oct 26, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Oct 26, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Oct 26, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Nov 11, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Nov 11, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Nov 11, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Nov 11, 2023
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
@CertainLach
Copy link

CertainLach commented Jun 30, 2024

What about instead of doing this just for this project, it can also be done for nixos configuration as a whole? There is already a bootspec project (NixOS RFC 0125: https://github.com/NixOS/rfcs/blob/master/rfcs/0125-bootspec.md), which standartizes nixos way of listing bootloader entries, what about persistencespec?

Then most of the work might be offloaded to nixos module authors themselves, every file to be stored should be described in nixos module itself, i.e postgresql module should define

# Entries like this can even be produced by systemd module itself, when service
# declares StateDirectory (It can even handle DynamicUser with its /var/lib/private state dir)
persistent.postgresql = {
  directories = ["/var/lib/postgresql"];
  meta.kind = lib.persistence.systemdStateDir;
};

Those entries then can be used in manual for nixos itself, for some backup solutions, and of course, for impermanence.

Impermanence then would extend persistent.<name> attrset with its own options

persistent.postgresql.storageClass = "main";
environment.persistence."/persistent".classes = ["main"];

ambroisie added a commit to ambroisie/nix-config that referenced this issue Jul 2, 2024
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Jul 2, 2024
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
ambroisie added a commit to ambroisie/nix-config that referenced this issue Jul 2, 2024
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
PhDyellow pushed a commit to PhDyellow/nixos-config that referenced this issue Aug 22, 2024
ambroisie added a commit to ambroisie/nix-config that referenced this issue Nov 7, 2024
TODO:
* Look at for more inspiration nix-community/impermanence#108
* Do home-manager
* Common files nix-community/impermanence#10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants