Modules¶
This guide covers how to write .nix configuration modules for System Manager, including services, packages, /etc files, and tmpfiles.
Building System Manager .nix files¶
Ready for an example! For this example, we're going to use the following:
-
Our files will live in
~/.config/system-manager -
We'll have two files, one
flake.nix, andsystem.nix
Note that we'll be using the files generated by System Manager's init subcommand. But to show that we're not locked into that format, later we'll demonstrate a single flake.nix file. Then in the sections that follow, we'll demonstrate how you can further split up your files.
We'll demonstrate how to install an app on your machine, then we'll add another app, then we'll uninstall the first app.
We'll also demonstrate how to move items from your /etc/nix/nix.conf file into your System Manager configuration file.
The Main flake.nix File¶
We recommend you start with a basic flake.nix file similar to this:
This is a typical flake with an inputs and an outputs section. The inputs loads in nixpkgs and system-manager. The outputs part has one primary job: It calls System Manager's makeSystemConfig function, passing in any number of .nix modules.
Each module, in turn, must specify a config object, containing configuration settings. These can be in separate files, and Nix will merge them into a single config object that gets passed into makeSystemConfig.
Your config attribute set can have:
nixpkgs.hostPlatform: This specifies the platform such asnixpkgs.hostPlatform = "x86_64-linux";environment, consisting ofsystemPackagesetcsystemd.servicessystemd.tmpfiles
For example, you could then replace the
line with individual .nix files. For example, you might have one file that installs the bat command, and another file that installs the tree command.
As an example, let's put these two files in a modules folder under the folder holding flake.nix. Replace the modules line with this:
Then here are the individual "recipe" files.
modules/bat.nix
modules/tree.nix
Why take this approach? Because you could, for example, have many different recipes stored in a GitHub repo (or anywhere, really), and you could easily drop them into your system, adding a single line in flake.nix for each. Each one would have their own software installations. And this solves the problem described in Dealing with Conflicting .nix Files
Managing System Services¶
System Manager lets you manage systemd services declaratively, using the same module language you used for installing packages or creating files under /etc. Instead of manually placing service files in /etc/systemd/system or enabling them with systemctl, you describe the service in a Nix module--its command, environment, dependencies, restart behavior, and any timers or sockets it needs.
System Manager then generates the correct systemd unit files, installs them into the right directory, and reloads systemd automatically during a switch. This approach gives you repeatability and safety: if you rebuild a machine, the same services come back exactly as before; if a service configuration breaks, you simply roll back to the previous generation. Declarative service management also avoids drift--no accidental edits, no forgotten manual steps, and no inconsistencies between machines or team members.
Using this approach, instead of manually saving a file in /etc/systemd/system and then manually starting and stopping the service, you use a .nix file to declaratively state what you want the service to look like and that you want it to be active.
Then you can take this same .nix file, place it on another system, and run System Manager again, and you'll have the service installed in a way that's identical to the first system.
The following example demonstrates how to specify a system service and activate it.
We're assuming you're using a flake.nix similar to what's found in The Main flake.nix File.
Note:
This line is required in the above example:
(There are other options for wantedBy; we discuss it in full under Specifying wantedBy Setting)
Activate it using the same nix command as earlier:
This will create a system service called say-hello (the name comes from the line systemd.services.say-hello) in a unit file at /etc/systemd/system/say-hello.service with the following inside it:
Tip
Compare the lines in the say-hello.service file with the say_hello.nix file to see where each comes from.
You can verify that it ran by running journalctl:
and you can find the following output in it:
Note
If you remove the ./apps.nix line from flake.nix, System Manager will see that the configuration changed and that the apps listed in it are no longer in the configuration. As such, it will uninstall them. This is normal and expected behavior.
Specifying the wantedBy Setting¶
The wantedBy attribute tells systemd when to automatically start a service. System Manager includes its own systemd target that you can use in the wantedBy setting to automatically start any services immediately after applying the changes, as well as after reboot. Here's an example wantedBy line in a .nix configuration file:
(By allowing the service to start after applying changes, you don't need to reboot for the service to start.)
But you're not limited to just this target. For example, if you're creating a system service that runs on a schedule, you might use this:
Managing Software Installations¶
System Manager allows you to install software in a fully declarative way similar to installing system services. Instead of relying on a traditional package manager and running commands like apt install or dnf install, you list the packages you want in your configuration file. During a switch, System Manager builds a new system profile that includes those packages, activates it, and ensures the software is available on your PATH. This makes installations reproducible and version-controlled. If you reinstall your operating system or set up a new machine, the exact same tools will appear automatically. And because software installation is tied to your configuration (not to manual actions), System Manager prevents drift--no forgotten tools, no mismatched versions across machines, and no surprises when you rollback or update.
Note
To install software, you add attributes to the config.environment.systemPackages attribute set.
Example: Installing a couple apps¶
Starting with a flake such as this:
Notice this flake references a file called apps.nix. In that file we'll add to the systemPackages attribute. Here's the apps.nix file:
When you run System Manager, you should have the packages hello and bat available.
Note
The first time you install an app through System Manager, System Manager will add a file inside /etc/profile.d/. This file adds /run/system-manager/sw/bin/ to a user's path when they log in. If this is the first time you've installed an app on this system with System Manager, you'll need to either source that file, or simply log out and log back in.
If you prefer, you can combine the above two .nix files into a single flake:
Working With /etc Files Declaratively¶
Many applications and services rely on configuration files stored under /etc, and System Manager lets you manage those files declaratively as well. Instead of manually editing files like /etc/some_config, you define them in your Nix configuration and let System Manager write them during a switch. This ensures that your system state is always consistent with your configuration and avoids accidental edits or configuration drift. If you ever rebuild your machine, those files are recreated exactly as before, including permissions, contents, and paths. And because System Manager keeps previous generations, you can safely roll back to earlier versions of /etc files if needed. Declarative /etc management is especially powerful in shared or multi-machine environments, where consistency and repeatability matter most.
Oftentimes, when you're creating a system service, you need to create a configuration file in the /etc directory that accompanies the service. System Manager allows you to do that as well.
Note
To install software, you add attributes to the config.environment.etc attribute set.
Example: Creating a file in /etc¶
Starting with a flake such as this:
Notice this references a file called files1.nix. To create files, you add attributes to the config.environment.etc attribute set as follows:
This creates a single file inside the folder /etc/test/test2/ called something.txt.
After running the above with System Manager, you can verify the file exists:
Note that if you prefer, you can combine the above flake and separate .nix file into a single flake like so:
Permissions¶
NixOS uses the standard modes of file permissions, consisting of three octal digits; the first represents the user; the second represents the group; the third represents all other users (sometimes called "world" or "others").
Each digit is the sum of the permissions it grants:
- 4 = read (r)
- 2 = write (w)
- 1 = execute (x)
So "0755" means:
- 7 (4+2+1) = owner can read, write, and execute
- 5 (4+1) = group can read and execute
- 5 (4+1) = others can read and execute
Common examples:
"0644" = owner can read/write, everyone else can only read
"0755" = owner can do everything, everyone else can read and execute
"0400" = owner can only read, nobody else can do anything
"0600" = owner can read/write, nobody else can touch it
Users and Groups¶
To specify a user and group as owners for a file, you can either use the user ID and group ID, or the user name and group name. Here's an example that uses user ID and group ID (notice we set uid and gid):
And here's an example that uses named user and group (notice we set user and group):
Tip
This use of uid/gid for numeric IDs and user/group for names aligns with NixOS standards.
Supporting System Services with tmp files and folders¶
Some systemd services need runtime directories, temporary files, or specific filesystem structures to exist before they can start. The systemd.tmpfiles configuration provides a declarative way to create these files and directories, set their permissions and ownership, and manage cleanup policies. This is particularly useful for volatile directories like those under /var/run/, /tmp/, or custom application directories that need to be recreated on each boot with the correct permissions.
For example, if you're running a web application that stores temporary uploads in /var/app/uploads/, you can use tmpfiles to ensure this directory exists with the correct permissions when the system boots. Without tmpfiles, your service might fail to start because the directory doesn't exist yet, or it might have the wrong ownership and your application can't write to it.
For this we offer two distinct syntaxes you can use, depending on your needs, as shown in the following sample code:
The first example (rules), creates a directory called /var/tmp/system-manager with mode 0755, owned by user root and group root. (The - means no age-based cleanup.)
The second example creates the same type of directory at /var/tmp/sample with mode 0755, but uses the structured settings format. Since user and group aren't specified, they default to root. This Nix-friendly syntax is more readable and easier to maintain than raw tmpfiles.d strings.