From 3c338eb017f7e9f51d7551bea87ef01e5d2ef929 Mon Sep 17 00:00:00 2001 From: Jeffrey Freckleface Cogswell Date: Sun, 18 May 2025 20:12:53 -0600 Subject: [PATCH 01/22] docs: add examples, getting started, install, reference guide --- docs/site/examples.md | 970 ++++++++++++ docs/site/faq.md | 146 ++ docs/site/getting-started.md | 342 ++++ docs/site/getting-started/index.md | 3 - docs/site/install.md | 76 + docs/site/reference-guide.md | 2060 +++++++++++++++++++++++++ docs/{site => }/stylesheets/extra.css | 0 7 files changed, 3594 insertions(+), 3 deletions(-) create mode 100644 docs/site/examples.md create mode 100644 docs/site/faq.md create mode 100644 docs/site/getting-started.md delete mode 100644 docs/site/getting-started/index.md create mode 100644 docs/site/install.md create mode 100644 docs/site/reference-guide.md rename docs/{site => }/stylesheets/extra.css (100%) diff --git a/docs/site/examples.md b/docs/site/examples.md new file mode 100644 index 0000000..18147db --- /dev/null +++ b/docs/site/examples.md @@ -0,0 +1,970 @@ +# System Manager Examples + +[Note: This is a WIP -- I will be updating these samples to be more consistent with the recent samples in the README.] + +This document provides practical examples of using system-manager to manage system configurations on any Linux distribution. Each example demonstrates different capabilities and use cases. + +## Table of Contents + +1. [Example 1: Installing a Timer](#example-1-installing-a-timer) +2. [Example 2: Installing Docker](#example-2-installing-docker) +3. [Example 3: Software Package Management (Emacs and Others)](#example-3-software-package-management-emacs-and-others) +4. [Example 4: User Management with Userborn (PR #266)](#example-4-user-management-with-userborn-pr-266) + +--- + +## Example 1: Installing a Timer + +This example demonstrates how to install a timer that runs every one minute. First, here's a flake.nix file: + +### flake.nix + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ ./system.nix ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +And then here's a system.nix file referenced from within `flake.nix`: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ ./system.nix ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +### Usage + +```bash +# Activate the configuration +nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo +``` + +Then restart the system; the timer will start automatically. + +```bash +# View the file created every one minute +cat /tmp/simple-timer.log +``` + +### Notes + +- The timer will not start automatically until you reboot the system. If you wish to start it manually, you can do so by typing: + +```bash +sudo systemctl start simple-timer.timer +``` + +--- + +## Example 2: Installing Docker + +This example shows how to install Docker and configure it as a systemd service. + +### flake.nix + +```nix +{ + description = "System Manager - Docker Example"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, system-manager }: { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Install Docker and related tools + environment.systemPackages = with nixpkgs.legacyPackages.x86_64-linux; [ + docker + docker-compose + docker-buildx + ]; + + # Docker daemon configuration + environment.etc."docker/daemon.json".text = '' + { + "log-driver": "json-file", + "log-opts": { + "max-size": "10m", + "max-file": "3" + }, + "storage-driver": "overlay2", + "storage-opts": [ + "overlay2.override_kernel_check=true" + ] + } + ''; + + # Create Docker systemd service + systemd.services.docker = { + enable = true; + description = "Docker Application Container Engine"; + documentation = [ "https://docs.docker.com" ]; + after = [ "network-online.target" "firewalld.service" "containerd.service" ]; + wants = [ "network-online.target" ]; + requires = [ "docker.socket" ]; + wantedBy = [ "system-manager.target" ]; + + serviceConfig = { + Type = "notify"; + ExecStart = "${nixpkgs.legacyPackages.x86_64-linux.docker}/bin/dockerd --host=fd://"; + ExecReload = "/bin/kill -s HUP $MAINPID"; + TimeoutStartSec = 0; + RestartSec = 2; + Restart = "always"; + StartLimitBurst = 3; + StartLimitInterval = "60s"; + + # Security settings + LimitNOFILE = 1048576; + LimitNPROC = "infinity"; + LimitCORE = "infinity"; + TasksMax = "infinity"; + Delegate = "yes"; + KillMode = "process"; + OOMScoreAdjust = -500; + }; + }; + + # Docker socket + systemd.sockets.docker = { + enable = true; + description = "Docker Socket for the API"; + wantedBy = [ "sockets.target" ]; + + socketConfig = { + ListenStream = "/var/run/docker.sock"; + SocketMode = "0660"; + SocketUser = "root"; + SocketGroup = "docker"; + }; + }; + + # Create necessary directories and setup + systemd.tmpfiles.rules = [ + "d /var/lib/docker 0710 root root -" + "d /var/run/docker 0755 root root -" + "d /etc/docker 0755 root root -" + ]; + } + ]; + }; + }; +} +``` + +### Usage + +```bash +# Activate the configuration +nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo + +# Check Docker service status +sudo systemctl status docker + +# Test Docker +sudo docker run hello-world + +# Check Docker version +sudo docker --version + +# View Docker logs +sudo journalctl -u docker -f +``` + +### Notes + +- Ensure the `docker` group exists on your system +- Add your user to the docker group: `sudo usermod -aG docker $USER` +- You may need to log out and back in for group changes to take effect +- This example uses the Docker socket for API communication + +--- + +## Example 3: Software Package Management (Emacs and Others) + +This example demonstrates installing software packages like emacs and other development tools. It also shows what happens when you remove a package from the configuration. + +### flake.nix + +```nix +{ + description = "System Manager - Software Package Management"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, system-manager }: { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Install various software packages + environment.systemPackages = with nixpkgs.legacyPackages.x86_64-linux; [ + # Editors + emacs + vim + neovim + + # Development tools + git + tmux + htop + + # Shell utilities + ripgrep + fd + bat + exa + fzf + + # Network tools + curl + wget + + # System tools + tree + ncdu + + # Programming languages + python3 + nodejs + go + ]; + + # Create a configuration file for easy reference + environment.etc."installed-packages.txt".text = '' + Installed packages via system-manager: + + Editors: + - emacs + - vim + - neovim + + Development Tools: + - git + - tmux + - htop + + Shell Utilities: + - ripgrep (rg) + - fd + - bat + - exa + - fzf + + Network Tools: + - curl + - wget + + System Tools: + - tree + - ncdu + + Programming Languages: + - python3 + - nodejs + - go + + These packages are managed by system-manager. + Check /nix/store for the actual installations. + ''; + + # Create a simple systemd service that uses one of the installed packages + systemd.services.software-info = { + enable = true; + description = "Log installed software information"; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + wantedBy = [ "system-manager.target" ]; + script = '' + echo "=== System Manager Software Installation Report ===" > /tmp/software-report.txt + echo "Generated on: $(date)" >> /tmp/software-report.txt + echo "" >> /tmp/software-report.txt + + echo "Emacs version:" >> /tmp/software-report.txt + ${nixpkgs.legacyPackages.x86_64-linux.emacs}/bin/emacs --version | head -n1 >> /tmp/software-report.txt + echo "" >> /tmp/software-report.txt + + echo "Vim version:" >> /tmp/software-report.txt + ${nixpkgs.legacyPackages.x86_64-linux.vim}/bin/vim --version | head -n1 >> /tmp/software-report.txt + echo "" >> /tmp/software-report.txt + + echo "Git version:" >> /tmp/software-report.txt + ${nixpkgs.legacyPackages.x86_64-linux.git}/bin/git --version >> /tmp/software-report.txt + echo "" >> /tmp/software-report.txt + + echo "Python version:" >> /tmp/software-report.txt + ${nixpkgs.legacyPackages.x86_64-linux.python3}/bin/python3 --version >> /tmp/software-report.txt + + echo "Report saved to /tmp/software-report.txt" + cat /tmp/software-report.txt + ''; + }; + } + ]; + }; + }; +} +``` + +### Initial Installation + +```bash +# Activate the configuration +nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo + +# Verify packages are available +which emacs +which vim +which git +which python3 + +# Check the software report +cat /tmp/software-report.txt + +# List installed packages +cat /etc/installed-packages.txt +``` + +### Package Removal Demonstration + +Now let's see what happens when we remove packages. Create a modified version of the flake: + +#### flake-minimal.nix + +```nix +{ + description = "System Manager - Minimal Software Package Management"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, system-manager }: { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Reduced package list - removed emacs, neovim, and most other tools + environment.systemPackages = with nixpkgs.legacyPackages.x86_64-linux; [ + # Keep only essential tools + vim + git + htop + ]; + + environment.etc."installed-packages.txt".text = '' + Installed packages via system-manager (MINIMAL): + + Editors: + - vim + + Development Tools: + - git + - htop + + Note: Many packages have been removed from the previous configuration. + ''; + + systemd.services.software-info = { + enable = true; + description = "Log installed software information"; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + wantedBy = [ "system-manager.target" ]; + script = '' + echo "=== Minimal Software Installation Report ===" > /tmp/software-report.txt + echo "Generated on: $(date)" >> /tmp/software-report.txt + echo "" >> /tmp/software-report.txt + + echo "Vim version:" >> /tmp/software-report.txt + ${nixpkgs.legacyPackages.x86_64-linux.vim}/bin/vim --version | head -n1 >> /tmp/software-report.txt + echo "" >> /tmp/software-report.txt + + echo "Git version:" >> /tmp/software-report.txt + ${nixpkgs.legacyPackages.x86_64-linux.git}/bin/git --version >> /tmp/software-report.txt + + cat /tmp/software-report.txt + ''; + }; + } + ]; + }; + }; +} +``` + +### Testing Package Removal + +```bash +# First, verify emacs is available with the full configuration +which emacs +# Should output: /nix/store/.../bin/emacs + +emacs --version +# Should show emacs version + +# Now switch to the minimal configuration +nix run 'github:numtide/system-manager' -- switch --flake /path/to/flake-minimal.nix --sudo + +# Try to run emacs again +which emacs +# Should output: (nothing or "emacs not found") + +# Check if the binary still exists in the nix store +ls -la /nix/store/*emacs*/bin/emacs 2>/dev/null || echo "Emacs removed from active profile" + +# The package is no longer in the system PATH +echo $PATH +# You'll notice the emacs store path is no longer included + +# View the updated installed packages list +cat /etc/installed-packages.txt +# Will show only vim, git, and htop +``` + +### What Actually Happens When You Remove a Package? + +When you remove a package from your system-manager configuration and re-run it: + +1. **The package is removed from the system PATH**: The symbolic links in `/nix/var/nix/profiles/system-manager-profiles/*/bin/` will no longer point to the removed package + +2. **The Nix store paths remain**: The actual package files stay in `/nix/store/` until garbage collection + +3. **No files are deleted from /nix/store automatically**: System-manager doesn't immediately delete packages to allow rollbacks + +4. **The package becomes eligible for garbage collection**: Once it's not referenced by any profile, running `nix-collect-garbage` will remove it + +5. **Configuration files are updated**: Any `/etc/` files managed by system-manager are updated to reflect the new state + +### Demonstration Script + +Here's a complete script to demonstrate the package removal behavior: + +```bash +#!/bin/bash + +echo "=== System Manager Package Removal Demonstration ===" +echo "" + +# Step 1: Apply full configuration +echo "Step 1: Installing full software suite..." +nix run 'github:numtide/system-manager' -- switch --flake /path/to/full/config --sudo +echo "" + +# Step 2: Verify emacs is available +echo "Step 2: Verifying emacs installation..." +which emacs +emacs --version | head -n 1 +echo "" + +# Step 3: Save the emacs store path +EMACS_PATH=$(which emacs) +echo "Emacs is currently at: $EMACS_PATH" +echo "" + +# Step 4: Apply minimal configuration +echo "Step 3: Switching to minimal configuration (removing emacs)..." +nix run 'github:numtide/system-manager' -- switch --flake /path/to/minimal/config --sudo +echo "" + +# Step 5: Try to find emacs +echo "Step 4: Checking if emacs is still accessible..." +which emacs 2>/dev/null || echo "✓ Emacs is no longer in PATH" +echo "" + +# Step 6: Check if files still exist in nix store +echo "Step 5: Checking if emacs files still exist in nix store..." +if [ -f "$EMACS_PATH" ]; then + echo "✓ Emacs binary still exists at: $EMACS_PATH" + echo " (It will be garbage collected when you run: nix-collect-garbage)" +else + echo "✗ Emacs binary no longer exists" +fi +echo "" + +# Step 7: Show what garbage collection would do +echo "Step 6: Preview what garbage collection would remove..." +nix-store --gc --print-dead | grep emacs | head -n 5 +echo " ... (and possibly more)" +echo "" + +echo "=== Summary ===" +echo "When you remove a package from system-manager:" +echo " 1. ✓ It's removed from your PATH" +echo " 2. ✓ New sessions won't have access to it" +echo " 3. ✓ Store files remain until garbage collection" +echo " 4. ✓ You can rollback to previous configurations" +echo " 5. ✓ Running 'nix-collect-garbage' removes unused packages" +echo "" +echo "The software is effectively UNINSTALLED from your system perspective," +echo "but the files remain for potential rollback until you garbage collect." +``` + +### Key Takeaways + +- **Removing packages from the configuration makes them unavailable** - They won't be in PATH for new shells/sessions +- **The software IS effectively uninstalled** from a user perspective +- **Store files persist for rollback capability** until garbage collection +- **You can always rollback** to previous configurations that had those packages +- **Garbage collection is manual** - run `nix-collect-garbage` to reclaim disk space + +--- + +## Example 4: User Management with Userborn (PR #266) + +This example demonstrates how to create and manage users using the userborn feature from PR #266. This is currently a work-in-progress feature but shows the future direction of user management in system-manager. + +**Note**: This example is based on PR #266 which is still in draft status. The implementation may change before being merged. + +### flake.nix + +```nix +{ + description = "System Manager - User Management Example"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + # Userborn for user management (from PR #266) + # Note: This is a WIP feature - pin to a specific commit for stability + userborn = { + url = "github:JulienMalka/userborn/stateful-users"; + # For production, pin to a specific commit: + # url = "github:JulienMalka/userborn/6e8f0d00e683049ac727b626552d5eba7f3471ff"; + }; + }; + + outputs = { self, nixpkgs, system-manager, userborn }: { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable userborn for user management + services.userborn.enable = true; + services.userborn.package = userborn.packages.x86_64-linux.default; + + # Set stateful users mode + systemd.services.userborn.environment.USERBORN_STATEFUL = "1"; + + # Define users + users.users = { + # Create a developer user + alice = { + isNormalUser = true; + description = "Alice Developer"; + home = "/home/alice"; + createHome = true; + homeMode = "0700"; + + # Set user shell + shell = nixpkgs.legacyPackages.x86_64-linux.bash; + + # Add to groups + extraGroups = [ "wheel" "docker" "networkmanager" ]; + + # Set initial password (will prompt to change on first login) + # Note: In production, use hashedPasswordFile instead + # Generate with: mkpasswd -m sha-512 + # Example hash for password "changeme": + initialHashedPassword = "$6$rounds=656000$YourSalt$HashedPasswordString"; + + # User-specific packages + packages = with nixpkgs.legacyPackages.x86_64-linux; [ + vim + git + tmux + htop + ]; + }; + + # Create a service account user + servicebot = { + isSystemUser = true; + description = "Service Bot Account"; + home = "/var/lib/servicebot"; + createHome = true; + group = "servicebot"; + + # System users typically use nologin + shell = "${nixpkgs.legacyPackages.x86_64-linux.shadow}/bin/nologin"; + }; + + # Create a web developer user + webdev = { + isNormalUser = true; + description = "Web Developer"; + home = "/home/webdev"; + createHome = true; + + shell = nixpkgs.legacyPackages.x86_64-linux.zsh; + extraGroups = [ "www-data" "developers" ]; + + # User-specific packages for web development + packages = with nixpkgs.legacyPackages.x86_64-linux; [ + nodejs + python3 + go + docker-compose + ]; + }; + }; + + # Define groups + users.groups = { + developers = { + gid = 3000; + members = [ "alice" "webdev" ]; + }; + + servicebot = { + gid = 3001; + }; + }; + + # Enable required shell programs + programs.bash.enable = true; + programs.zsh.enable = true; + + # Create user home directory templates + systemd.tmpfiles.rules = [ + "d /home/alice/.config 0700 alice alice -" + "d /home/webdev/.config 0700 webdev webdev -" + "d /var/lib/servicebot 0750 servicebot servicebot -" + ]; + + # Create a welcome message for new users + environment.etc."skel/.bash_profile".text = '' + # Welcome message + echo "Welcome to this system managed by system-manager!" + echo "Your user account is managed declaratively." + echo "" + + # Source bashrc if it exists + if [ -f ~/.bashrc ]; then + source ~/.bashrc + fi + ''; + + environment.etc."skel/.bashrc".text = '' + # Basic bash configuration + export PATH=$HOME/.local/bin:$PATH + + # Aliases + alias ll='ls -alh' + alias la='ls -A' + alias l='ls -CF' + + # Prompt + PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' + ''; + + # Activation script to set up user environments + system.activationScripts.user-setup = { + text = '' + echo "Setting up user environments..." + + # Copy skeleton files to user homes if they don't exist + for user_home in /home/alice /home/webdev; do + if [ -d "$user_home" ]; then + for skel_file in /etc/skel/.bash_profile /etc/skel/.bashrc; do + target="$user_home/$(basename $skel_file)" + if [ ! -f "$target" ]; then + cp "$skel_file" "$target" + chown $(basename $user_home):$(basename $user_home) "$target" + fi + done + fi + done + + echo "User environment setup complete." + ''; + }; + } + ]; + }; + }; +} +``` + +### Usage + +```bash +# Activate the configuration with user management +nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo + +# Verify users were created +id alice +id webdev +id servicebot + +# Check user groups +groups alice +groups webdev + +# List all users (filter for our created users) +cat /etc/passwd | grep -E 'alice|webdev|servicebot' + +# Check home directories +ls -la /home/alice +ls -la /home/webdev +ls -la /var/lib/servicebot + +# Switch to the alice user (requires password) +su - alice + +# As alice user, check available packages +which vim +which git +which tmux +``` + +### Setting Passwords + +For production use, you should use `hashedPasswordFile` instead of hardcoded passwords: + +```nix +users.users.alice = { + # ... other config ... + hashedPasswordFile = "/run/secrets/alice-password"; +}; +``` + +Generate a hashed password: + +```bash +# Generate a hashed password +mkpasswd -m sha-512 + +# Or use this one-liner to create a password file +mkpasswd -m sha-512 | sudo tee /run/secrets/alice-password +sudo chmod 600 /run/secrets/alice-password +``` + +### User Modification Example + +To modify a user, simply update the configuration and re-run system-manager: + +```nix +# Add alice to more groups +users.users.alice = { + # ... existing config ... + extraGroups = [ "wheel" "docker" "networkmanager" "video" "audio" ]; + + # Add more packages + packages = with nixpkgs.legacyPackages.x86_64-linux; [ + vim + git + tmux + htop + # New packages + ripgrep + fd + bat + ]; +}; +``` + +### Removing a User + +To remove a user, simply remove their configuration: + +```nix +# Before: users.users.alice = { ... }; +# After: (remove the alice user block entirely) + +users.users = { + # alice removed + webdev = { + # ... webdev config remains ... + }; + servicebot = { + # ... servicebot config remains ... + }; +}; +``` + +Then re-activate: + +```bash +nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo + +# The user will be removed from /etc/passwd and /etc/shadow +# Note: Home directory may remain and need manual cleanup +``` + +### Important Notes About PR #266 + +1. **Work in Progress**: This PR is still in draft status and the API may change +2. **Userborn Integration**: Requires the userborn package for systemd-sysusers integration +3. **Stateful Users**: The example uses `USERBORN_STATEFUL = "1"` for stateful user management +4. **Password Management**: Use `initialPassword` or `initialHashedPassword` for first-time setup, then users can change their passwords +5. **Activation Scripts**: The PR adds support for `system.activationScripts` which allows custom setup logic + +### Testing the User Creation + +Here's a complete test script: + +```bash +#!/bin/bash + +echo "=== User Management Test Script ===" +echo "" + +# Apply the configuration +echo "Step 1: Creating users..." +nix run 'github:numtide/system-manager' -- switch --flake /path/to/user/example --sudo +echo "" + +# Test user creation +echo "Step 2: Verifying user 'alice' was created..." +if id alice &>/dev/null; then + echo "✓ User alice exists" + echo " UID: $(id -u alice)" + echo " GID: $(id -g alice)" + echo " Groups: $(groups alice)" + echo " Home: $(eval echo ~alice)" + echo " Shell: $(getent passwd alice | cut -d: -f7)" +else + echo "✗ User alice was not created" +fi +echo "" + +# Test system user +echo "Step 3: Verifying system user 'servicebot'..." +if id servicebot &>/dev/null; then + echo "✓ System user servicebot exists" + echo " UID: $(id -u servicebot)" + echo " Shell: $(getent passwd servicebot | cut -d: -f7)" +else + echo "✗ System user servicebot was not created" +fi +echo "" + +# Test groups +echo "Step 4: Verifying groups..." +if getent group developers &>/dev/null; then + echo "✓ Group developers exists" + echo " GID: $(getent group developers | cut -d: -f3)" + echo " Members: $(getent group developers | cut -d: -f4)" +else + echo "✗ Group developers was not created" +fi +echo "" + +# Test home directories +echo "Step 5: Checking home directories..." +for user in alice webdev; do + if [ -d "/home/$user" ]; then + echo "✓ Home directory exists for $user" + ls -ld "/home/$user" + else + echo "✗ Home directory missing for $user" + fi +done +echo "" + +echo "=== Test Complete ===" +``` + +### Advantages of Declarative User Management + +1. **Reproducibility**: User accounts are defined in code +2. **Version Control**: User configurations can be tracked in git +3. **Consistency**: Same user setup across multiple machines +4. **Documentation**: User configuration serves as documentation +5. **Rollback**: Can rollback to previous user configurations + +--- + + + +## Contributing + +If you have additional examples or improvements to these examples, please contribute to the [system-manager repository](https://github.com/numtide/system-manager) or this documentation repository. diff --git a/docs/site/faq.md b/docs/site/faq.md new file mode 100644 index 0000000..4fa75d1 --- /dev/null +++ b/docs/site/faq.md @@ -0,0 +1,146 @@ +# FAQ + +## General Tips and Best Practices + +### 1. Always Test in a VM First + +Before applying changes to your production system, test in a safe environment: + +```bash +# Build the configuration first to check for errors +nix build .#systemConfigs.default + +# For actual VM testing, use a tool like NixOS's VM builder +# or test in a container/virtualized environment +``` + +### 2. Use Flake Inputs Follows + +This ensures consistent nixpkgs versions: + +```nix +inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; # Use the same nixpkgs + }; +}; +``` + +By default, each flake input pins its own version of its dependencies, which means you could end up with multiple versions of nixpkgs. The `follows` directive tells Nix to use your nixpkgs instead of the one bundled with system-manager, ensuring consistent package versions across your entire configuration while reducing disk usage and evaluation time. + +### 3. Modular Configuration + +Split your configuration into multiple files: + +``` +. +├── flake.nix +└── modules + ├── default.nix + ├── services.nix + ├── packages.nix + └── users.nix +``` + +### 4. Check Logs + +Always check systemd logs after activation: + +```bash +sudo journalctl -u system-manager.target +sudo journalctl -xe +``` + +### 5. Garbage Collection + +Regularly clean up old generations: + +```bash +# Remove old system-manager profiles +sudo nix-env --profile /nix/var/nix/profiles/system-manager-profiles --delete-generations old + +# Run garbage collection +sudo nix-collect-garbage -d +``` + +### 6. Rollback + +If something goes wrong, you can rollback: + +```bash +# List generations +sudo nix-env --profile /nix/var/nix/profiles/system-manager-profiles --list-generations + +# Rollback to previous generation +sudo nix-env --profile /nix/var/nix/profiles/system-manager-profiles --rollback + +# Activate the previous generation +nix run 'github:numtide/system-manager' -- activate --sudo +``` + +--- + +## Troubleshooting + +### Service Won't Start + +```bash +# Check service status +sudo systemctl status + +# View detailed logs +sudo journalctl -u -n 50 + +# Check if service file exists +ls -la /etc/systemd/system/.service +``` + +### Package Not Found in PATH + +If you just installed System Manager, and installed a package through it, try logging out and logging back in to pick up the path. + +```bash +# Check if package is in the profile +ls -la /nix/var/nix/profiles/system-manager-profiles/*/bin/ + +# Verify the package is in your config +cat /etc/installed-packages.txt + +# Check PATH +echo $PATH +``` + +### Permission Denied + +Ensure you're running system-manager with sudo: + +```bash +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +### Configuration Won't Build + +```bash +# Check for syntax errors +nix flake check + +# Build without activation +nix build .#systemConfigs.default + +# View build logs +nix log /nix/store/ +``` + +--- + +## Additional Resources + +- [System Manager GitHub Repository](https://github.com/numtide/system-manager) +- [System Manager Documentation](https://github.com/numtide/system-manager/tree/main/manual) +- [NixOS Module Options](https://search.nixos.org/options) +- [Nix Package Search](https://search.nixos.org/packages) +- [PR #266: User Management with Userborn](https://github.com/numtide/system-manager/pull/266) + +--- \ No newline at end of file diff --git a/docs/site/getting-started.md b/docs/site/getting-started.md new file mode 100644 index 0000000..d116903 --- /dev/null +++ b/docs/site/getting-started.md @@ -0,0 +1,342 @@ +# Getting Started + +If you've heard of NixOS, you've probably heard that it lets you define your entire system in configuration files and then reproduce that system anywhere with a single command. System Manager brings that same declarative model to any Linux distribution*, with no reinstalling, no switching operating systems, and no special prerequisites beyond having Nix installed. + +*Presently System Manager is only tested on Ubuntu, and is limited to only Linux distributions based on systemd. + +# Installation + +See [Install.md](install.md) for information on installing System Manager. + +# Initializing Your System + +To get started with System Manager, you can run our init subcommand, which will create an initial set of files in the `~/.config/system-manager` folder. + +For this first step to work, you **must** enable experimental features in the nix.conf file. (Simply adding the flags to the nix command isn't enough in this step. Afterwards you can remove the setting from your nix.conf file.) + +In the shell prompt, use your favorite editor with sudo to open the following file: + +``` +vi /etc/nix/nix.conf +``` + +Add the following line if it isn't already present: + +``` +experimental-features = nix-command flakes +``` + +Save the file and exit. Next, enter the following: + +``` +nix run 'github:numtide/system-manager' -- init +``` + +(Remember, the double dash -- signifies that any options following it are passed to the following command, in this case System Manager, rather than to the main command, `nix`). + +You might see the following for questions; you can simply answer yes to them: + +* Do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.numtide.com' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + +* Do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + + +After running the command you will have the following files in your `~/.config/system-manager` folder: + +* `flake.nix` -- A flake entrypoint that loads the system.nix file +* `system.nix` -- The declarative file that describes what your system should look like. + +!!! Tip + Because this is your first time running System Manager, Nix will download and build several files, which might take some time. This only happens once, and in the future, System Manager will run very quickly. + +!!! Note + If you activate flakes through the command-line, but not through your /etc/nix/nix.conf file, then System Manager won't create the initial flake.nix file for you. In that case, you can manually create it and paste in the code we provide below, or activate the experimental features (nix-command and flakes) in /etc/nix/nix.conf, and then re-run the System Manager init command. + +Here are the contents of the files that were created: + +## flake.nix + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ + ./system.nix + ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +## system.nix + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable and configure services + services = { + # nginx.enable = true; + }; + + environment = { + # Packages that should be installed on a system + systemPackages = [ + # pkgs.hello + ]; + + # Add directories and files to `/etc` and set their permissions + etc = { + # with_ownership = { + # text = '' + # This is just a test! + # ''; + # mode = "0755"; + # uid = 5; + # gid = 6; + # }; + # + # with_ownership2 = { + # text = '' + # This is just a test! + # ''; + # mode = "0755"; + # user = "nobody"; + # group = "users"; + # }; + }; + }; + + # Enable and configure systemd services + systemd.services = { }; + + # Configure systemd tmpfile settings + systemd.tmpfiles = { + # rules = [ + # "D /var/tmp/system-manager 0755 root root -" + # ]; + # + # settings.sample = { + # "/var/tmp/sample".d = { + # mode = "0755"; + # }; + # }; + }; + }; +} +``` + +# Example: Installing/Uninstalling Apps + +First, let's build a configuration file that installs or uninstalls apps. + +!!! Tip + The idea is that the configuration file describes what the system should look like. Keep that in mind, as opposed to thinking that the configuration file "installs software" or "uninstalls software." + +To get started, we'll create another .nix file that will install a single app. Then we'll run System Manager, and verify it's installed. + +Then to demonstrate what System Manager can do, we'll add another line to the configuration file with another app; run System Manager again, and again verify its installation. + +Then after that we'll remove one of the apps from the configuration file, run System Manager, and verify that the app is no longer installed. + +This will fully demonstrate the declarative nature of these configuration files. + +First, in the ~/.config/system-manager folder, create a file apps.nix and place the following in it: + +```nix +{ pkgs, ... }: +{ + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + tldr + ]; +} +``` + +This configuration states that the system being configured should have the `tldr` app present, and if isn't, System Manager will install it. (Notice how we phrased that! We didn't just say this file installs the app. With .nix files, it's important to get into the mindset that they state what the system should look like.) + +Now add the file to the modules list in flake.nix by replacing this modules line: + +```diff +- modules = [ ./system.nix ]; ++ modules = [ ++ ./system.nix ++ ./apps.nix ++ ]; +``` + +Note: By default, system.nix includes starter code and some commented out examples, and nothing else. So you can leave it in the list; in its original state, it doesn't do anything. + +Next, we'll run System Manager. + + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +After a short moment, the `tldr` app should be installed on your system. + +!!! Tip + The first time you install software with System Manager, it adds a path to your $PATH variable by creating an entry in /etc/profile.d/. This won't take effect until you log out and back in; or you can source the file like so: `source /etc/profile.d/system-manager-path.sh` After that, you should find the tldr program: `which tldr` should yield `/run/system-manager/sw/bin//tldr`. + +Now to demonstrate the declarative feature of System Manager, let's add another app to the list. Here's a fun app called cowsay. Add a single line "cowsay" to the list passed into systemPackages: + +```nix +{ pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + tldr + cowsay + ]; + }; +} +``` + +Run System Manager again with the same command as above, and you should now have `cowsay` on your system: + + +```bash +~/.config/system-manager$ cowsay Hello! + ________ +< Hello! > + -------- + \ ^__^ + \ (oo)\_______ + (__)\ )\/\ + ||----w | + || || +~/.config/system-manager$ +``` + +Remember, this is a declarative approach; System Manager did not re-install `tldr`. It looked at the list (tldr, cowsay) and compared it to what is currently installed. It saw that `tldr` is already installed, so it skipped that one. It saw `cowsay` is *not* installed, so it installed it, so that the system matches the configuration file. + +Now let's remove `cowsay` from the list of installed software. To do so, simply remove the line (or comment it out): + +```nix +{ pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + tldr + ]; + }; +} +``` + +Notice this file now looks exactly as it did before adding in cowsay, meaning System Manager the system will now look like it did before adding in `cowsay`. Re-run System Manager and you'll see that `cowsay` is no longer installed. + +## Understanding the config attribute set + +In the above example, we added attributes to the systemPackages set, which is a part of the environment attribute set, which in turn is part of the config attribute set. + +When System Manager runs, with the help of the Nix language, you can have multiple config attribute sets, and System Manager combines them into a single attribute set. This allows you to have different setups in separate files, and simply combine them side by side, only having to add on to this line: + +```nix + modules = [ + ./system.nix + ./apps.nix + ]; +``` + +However, you need to be careful. Suppose you have a different set of software you want to install, and you create a flake in another area in the filesystem with that software. It might not work the way you intend. + +With that second flake, System Manager will gather up any apps you have in the systemPackages attribute, and compare that to what it has already installed earlier. If the packages installed earlier aren't included, it will remove those packages. (That includes the apps you installed with the "other" flake.) + +In other words, you cannot have two separate flakes, one for one set of software, the other for a different set of software, and bounce between those flakes. System Manager will treat the second as requesting to uninstall what it installed earlier. + +To make the above work, your best bet is to create a single flake and add in individual files that contain the apps you want to install, and always run from that same location. + +# Concepts for people new to Nix + +## Understanding Imperative State vs Declarative State + +Imperative state means you change the system by hand, step by step. You run commands like apt install, edit files under /etc with a text editor, toggle systemd services, and make changes as you think of them. You're telling the computer how to do something: + +> "Install this package, then edit this file, then restart this service." + +Each action mutates the system in place, and over time the machine can drift into a state that's hard to reproduce. + +(To "mutate" something simply means to change it in place. When a system mutates, its files, settings, or state are altered directly, step by step, rather than being reconstructed from a clean, known description.) + +Declarative state, on the other hand, means you don't tell the system how to do the steps — you tell it what you want the final system to look like, and the tool (System Manager, NixOS, Home Manager, etc.) figures out the steps automatically. + +> "This machine should have these packages, these /etc files, and these services enabled." +When you activate that configuration, the tool builds the desired end state and applies it in a predictable, repeatable way. + +Here's A simple analogy: + +Imperative is like writing a recipe with every individual action: "Chop onions. Heat pan. Add oil..." + +Declarative is like saying, "I want a finished lasagna," and the system knows how to assemble it reliably every time. + +Declarative state avoids drift, keeps everything versioned and reproducible, and makes rollback simple. Imperative state is flexible and quick, but much harder to track or repeat. + +> Traditional programming languages are typically imperative in nature. + +If you're familiar with coding, a language like JavaScript is imperative in that you describe everything in a step by step fashion. A language like HTML is declarative in that you simply state what the web page should look like, without saying how to do it. + +## A note about objects in your `.nix` files + +Nix gives you significant flexibility in creating your objects that you use inside a `.nix` file. + +For example, you could have a `config` object that looks like this: + +``` +config = { + nixpkgs = { + hostPlatform = "x86_64-linux"; + } +} +``` + +This declares an object stored as `config` with a single member called `nixpkgs`; that `nixpkgs` member then has a single member called `hostPlatform`, holding the string literal `"x86_64-linux"`. + +But Nix allows great flexilibyt in how you declare such objects. Consider the following: + +```nix + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + } +``` +This creates the exact same object. Nix allows you to string together members with a dot between them, and it will construct the inner object accordingly. + +!!! Note + In the examples throughout this and other guides here, we use a mixture of the above syntax. + diff --git a/docs/site/getting-started/index.md b/docs/site/getting-started/index.md deleted file mode 100644 index 5a6ab6f..0000000 --- a/docs/site/getting-started/index.md +++ /dev/null @@ -1,3 +0,0 @@ -# Getting Started - -Documentation coming soon. diff --git a/docs/site/install.md b/docs/site/install.md new file mode 100644 index 0000000..24484b2 --- /dev/null +++ b/docs/site/install.md @@ -0,0 +1,76 @@ +# System Requirements + +[TODO: I realized I wrote this section twice, here and in the getting-started guide. I'll combine them and provide the same in both, since some people might just read one doc or the other.] + +In order to use System Manager, you need: + +* **A Linux machine.** We've tested System Manager with Ubuntu both as standalone and under Windows Subsystem for Linux (WSL). +* **At least 12GB Disk Space.** However, we recommend at least 16GB, as you will be very tight for space with under 16GB. (This is primarily due to Nix; if you're using System Manager to configure, for example, small servers on the Cloud, 8GB simply won't be enough.) +* **Nix installed system-wide.** (System Manager doesn't work with a per-user installation of Nix) +* **Flakes** enabled + +!!! Important + System Manager does not work with the single-user installation option for Nix. + +!!! Important + At this time, System Manager requires flakes to be enabled. + +# Installation + +Because Nix can load code (called "flakes") remotely, you don't need to download System Manager. Simply running it the first time will automatically install it in what's called the Nix Store, which is a special directory on your system (typically /nix/store) where Nix keeps all packages and their dependencies in isolation. + + +## Regarding Experimental Features + +System Manager requires flakes to run. You can enable flakes using one of two methods: + +* By adding the following line to /etc/nix.conf + +``` +experimental-features = nix-command flakes +``` + +* Or by adding the --extra-experimental-features option to the `nix` command line like so: + +``` +--extra-experimental-features 'nix-command flakes' +``` + +Note, however, that if you use the `init` subcommand to initialize an environment, and you do *not* have experimental features enabled in your `nix.conf` file, you will only get a default `system.nix` file, and not an associated `flake.nix` file. + +!!! Recommendation + If you need to run the init subcommand, but prefer to pass the `--extra-experimental-features` option to the command line, we recommend at least temporarily adding the aforementioned line to the `nix.conf` file. + +!!! Important + This is optional, but ultimately System Manager prefers to manage the nix.conf file for you, after which you can declare experimental features inside the flake.nix file as shown later in [Letting System Manager manage /etc/nix/nix.conf](#letting-system-manager-manage-etcnixnixconf). + + +## Running under sudo + +System Manager needs `sudo` access to run. As such, we've provided a command-line option, --sudo, that allows you to grant sudo rights to System Manager. + +**System Manager is still in early development, and for now the --sudo commmand line option is required.** + +!!! Note + Adding yourself to Nix's trusted-users configuration won't help here. Trusted users have elevated privileges within the Nix daemon, but System Manager requires root filesystem permissions to modify /etc, manage services, and install system packages. You'll still need to use sudo. + +## How can I tell whether Nix is installed for the whole system or just me? + +Simply type + +``` +which nix +``` + + +If you see it's installed off of your home directory, e.g.: + +``` +/home/username/.nix-profile/bin/nix +``` + +Then it's installed just for you. Alternatively, if it's installed for everybody, it will be installed like so: + +``` +/nix/var/nix/profiles/default/bin/nix +``` \ No newline at end of file diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md new file mode 100644 index 0000000..cd8ceb4 --- /dev/null +++ b/docs/site/reference-guide.md @@ -0,0 +1,2060 @@ +# Reference Guide + +[PLEASE NOTE AS YOU EDIT THIS: I KNOW I MISSED BACKTICKS IN A LOT OF PLACES. I'LL GO THROUGH IT CAREFULLY AND ADD THEM ALL IN, SO DON'T WORRY ABOUT ADDING COMMENTS ABOUT THEM.] + +To get the most out of System Manager, we're offering this guide to help you make the best decisions based on your particular situation. + +# Table of Contents + +- [System Requirements](#system-requirements) +- [Installation](#installation) + - [Regarding Experimental Features](#regarding-experimental-features) + - [Running under sudo](#running-under-sudo) + - [Command Line Usage](#command-line-usage) +- [Command-line Options](#command-line-options) + - [init](#init) + - [switch](#switch) + - [register](#register) + - [build](#build) + - [deactivate](#deactivate) + - [pre-populate](#pre-populate) + - [sudo](#sudo) +- [Setting up a folder and file structure](#setting-up-a-folder-and-file-structure) + - [Deciding on a folder structure](#deciding-on-a-folder-structure) + - [Choosing a location](#choosing-a-location) + - [Choosing a file structure](#choosing-a-file-structure) + - [Dealing with conflicting .nix files](#dealing-with-conflicting-nix-files) +- [Letting System Manager manage `/etc/nix/nix.conf`](#letting-system-manager-manage-etcnixnixconf) +- [Recommended Workflow for Starting Out](#recommended-workflow-for-starting-out) +- [Using System Manager in a non-Interactive Setting](#using-system-manager-in-a-non-interactive-setting) +- [Recommended Workflow if You Already Have Your Nix Files](#recommended-workflow-if-you-already-have-your-nix-files) +- [Building system-manager .nix files](#building-system-manager-nix-files) + - [The Main flake.nix File](#the-main-flakenix-file) +- [Managing System Services](#managing-system-services) + - [Specifying the wantedBy Setting](#specifying-the-wantedby-setting) +- [Managing Software Installations](#managing-software-installations) + - [Example: Installing a couple apps](#example-installing-a-couple-apps) +- [Working With /etc Files Declaratively](#working-with-etc-files-declaratively) + - [Example: Creating a file in /etc](#example-creating-a-file-in-etc) + - [Permissions](#permissions) + - [Users and Groups](#users-and-groups) +- [Supporting System Services with tmp files and folders](#supporting-system-services-with-tmp-files-and-folders) +- [Working with remote flakes](#working-with-remote-flakes) + - [What's a flake.lock file?](#whats-a-flakelock-file) + - [Setting up your project for remote hosting](#setting-up-your-project-for-remote-hosting) + - [When should you update your flake.nix file?](#when-should-you-update-your-flakenix-file) + - [Can't System Manager build flake.lock for me?](#cant-system-manager-build-flakelock-for-me) + - [Ensuring success](#ensuring-success) + - [Running System Manager with a remote flake](#running-system-manager-with-a-remote-flake) +- [Using Blueprint with System Manager](#using-blueprint-with-system-manager) + - [Using multiple configuration files with Blueprint](#using-multiple-configuration-files-with-blueprint) +- [Full Examples](#full-examples) + - [Full Example: Installing PostgreSQL](#full-example-installing-postgresql) + - [Full Example: Installing Nginx](#full-example-installing-nginx) + - [Full Example: Installing Nginx for HTTPS with a Secure Certificate](#full-example-installing-nginx-for-https-with-a-secure-certificate) + - [Full Example: Managing a System that runs Custom Software](#full-example-managing-a-system-that-runs-custom-software) + - [Live example](#live-example) +- [Optional: Installing System Manager Locally](#optional-installing-system-manager-locally) + +- FAQ (Maybe put in its own document) + +# Command Line Usage + +The basic command looks like this: + +```nix +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +This is the most common scenario you'll use. +## Command-line Options + +### init + +This subcommand creates two initial files for use with system manager, a fully-functional flake.nix, and a system.nix file that contains skeleton code. + +#### Command line options + +**path:** The path where to create the files. If the path doesn't exist, it will be created. + +#### Example + +``` +nix run 'github:numtide/system-manager' -- init +``` + +This will create the initial files in `~/.config/system-manager`. + +``` +nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-manager' +``` + +This will create the initial files in `/home/ubuntu/system-manager`. + +!!! Note + Presently, System Manager requires Flakes to be active. If you choose to not include the experimental features line in /etc/nix/nix.conf (and instead use the experimental features command line option), then init will only create a system.nix file, rather than both a flake.nix file and system.nix file. + +### switch + +The `switch` subcommand builds and activates your configuration immediately, making it both the current running configuration and the default for future boots. Use it whenever you want to apply your changes. + +**Note: Rollbacks are not yet implemented.** + +The following two parameters are currently both required: + +**--flake**: Specifies a flake to use for configuration. + +**--sudo**: Specifies that System Manager can use sudo. + +### register + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `register` subcommand builds and registers a System Manager configuration, but does not activate it. Compare this to `switch`, which does everything register does, but then activates it. + +### build + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `build` subcommand builds everything needed for a switch, but does not register it. + +### deactivate + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `deactivate` deactivates System Manager. + +### pre-populate + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `prepopulate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. + +### sudo + +The sudo subcommand grants sudo access to System Manager, while running under the current user. All created files with be owned by the current user. + +# Setting up a folder and file structure + +Before you begin with System Manager, you'll need to decide on your folder structure. + +!!! Note + If you prefer, you can host all your System Manager configuration files on a remote Git repo (such as GitHub), and then you don't need to worry about where on your computer to store the files. For more info, see [Working with Remote Flakes](#working-with-remote-flakes). + +Technically, you are free to set up your folders and files however you like; System Manager does not enforce any rules, thus allowing you full flexibility. Below are simply some options that we recommend. + +!!! Tip + While you are free to have different system manager .nix files scattered throughout your system, we recommend, if possible, keeping them in a single location simply for organizational purposes. But again, this is just a recommendation and you're not bound by any such rules. + +## Deciding on a folder structure + +You’ll need to choose where your System Manager configuration will live. Here are two main organizational patterns we recommend. + +* **Option A**: A single folder for all your configuration + +A single folder keeps everything together. This offers a clean long-term solution, along with easy version control. It's also convenient for replicating between machines. + +* **Option B**: A separate folder for each use case + +While not as common, it’s entirely possible to organize your System Manager configuration into multiple independent folders, each focused on a specific use case. In this model, you treat each configuration as its own standalone unit, often stored in its own Git repository. + +For example, you might keep: + +* a dedicated configuration folder strictly for managing nginx, + +* another for custom systemd services, + +* another for developer tools, + +* and yet another for monitoring and observability packages. + +In this manner, you can then build up a system by picking and choosing which services you need for a particular machine, and pull each one down from GitHub. + +To make this happen, however, requires careful consideration [as we discuss later](#dealing-with-conflicting-nix-files). + +## Choosing a location + +### Option 1: Your personal ~/.config folder + +If you're managing a system yourself and only you will be using it, one possibility is to put the files in ~/.config/system-manager. + +This approach keeps everything scoped to you and avoids having to place files under /etc and, perhaps most importantly, avoids have to use sudo. Here's an example layout: + +``` +~/.config/system-manager/ + flake.nix + modules/ + default.nix +``` + +!!! Tip + Avoid this location if multiple people use the machine or if this configuration is meant to be shared with a team. Home-directory paths are user-specific and may not make sense across machines. + +### Option 2: A shared /etc/system-manager folder (Recommended for multi-user or organizational setups) + +If you are: + +* managing multiple machines, + +* part of a team, + +* deploying configurations in a corporate or server environment, + +* or simply want a clear system-level location, + +then /etc/system-manager is a great choice. Among the advantages are consistency across all machines; standard within an organization; and treating system manager as a system-level tool rather than a personal configuration. Here's an example layout: + +``` +/etc/system-manager/ + flake.nix + modules/ + default.nix + services.nix +``` + +## Choosing a file structure + +After choosing where your configuration lives, you must decide how to structure the files inside it. And note that while system-manager does not enforce any rules, we do recommend you maintain consistency, especially if you have multiple locations on your computer where you store system manager .nix files. + +Essentially, you have two options: + +* A single flake.nix file + +* A reusable flake.nix file with one or more separate configuration files that describe what the system will look like. + +Within Option B, you can also use our open-source Blueprint product to help you manage your files, which we'll cover shortly. + +### Option A: Single flake.nix file + +This configuration is ideal for: + +* Small, simple setups + +* Demos and experiments + +* One-off configurations + +Drawback: This approach doesn’t scale well once you need multiple services, multiple hosts, or reusable modules. + +### Option B: Flake file with one or more configuration files + +This is the structure used by most production setups and by NixOS itself. Your arrangement might look like: + +``` +system-manager/ + flake.nix + modules/ + default.nix + services.nix + users.nix +``` + +Or, perhaps you might have separate services, one per file: + +``` +system-manager/ + flake.nix + modules/ + default.nix + service-1.nix + service-2.nix + users.nix +``` + +This also lends itself well to having multiple "recipes". For example, you might want to add nginx and postgres to your system. You might have them preconfigured somewhere, and simply "drop" them in like so: + +``` +system-manager/ + flake.nix + modules/ + default.nix + service-1.nix + service-2.nix + users.nix + nginx.nix + postgres.nix +``` + +!!! Tip + This is the approach we use in our examples in this document. That way each isolated "recipe" is repeatable and can be re-used across multiple systems. + + +### Dealing with conflicting .nix files + +If you have multiple flakes throughout your computer, you can run into a situation where one might install some software, and the other might install a different software -- but uninstall what was in the other configuration. + +For example; suppose you have one configuration file that includes this list of apps: + +```nix + environment = { + systemPackages = [ + pkgs.bat + pkgs.nginx + pkgs.mysql84 + ]; +``` + +And you run System Manager, which installs the three apps. + +Then you separately in another folder have another flake with a different configuration and set of apps: + +```nix + environment = { + systemPackages = [ + pkgs.hello + pkgs.postgresql_18 + pkgs.vscode + ]; +``` + +Then in this folder your run System Manager. + +System Manager does not track files, and see this as a changed configuration: + +* The configuration **no longer has** bat, nginx, and mysql84. +* The configuration does have hello, postgresql_18, and vscode. + +The end result is that System Manager will **remove** bat, nginx, and mysql84, and install hello, postgresql_18, and vscode. + +The fix to this problem is to instead have a single main flake.nix file, which loads all of the different .nix files, allowing you to run System Manager from a single location. + +This is because Nix has the ability to merge together objects in separate files into a single object; the above would then merge into: + +```nix + systemPackages = [ + pkgs.bat + pkgs.nginx + pkgs.mysql84 + pkgs.hello + pkgs.postgresql_18 + pkgs.vscode + ]; +``` + +We describe this technique in [Building System Manager .nix Files](#building-system-manager-nix-files). + +# Letting System Manager manage `/etc/nix/nix.conf` + +System Manager can optionally manage your `/etc/nix/nix.conf` file for you. + +If you have an existing `/etc/nix/nix.conf` file, you'll need to delete it if you want System Manager to manage the file; then run System Manager again. From that point on System Manager will manage the file for you, and you should not make changes to it. + +Instead, you'll put the changes in one of your .nix files you'll be building to configure System Manager. + +# Recommended Workflow for Starting Out + +As described previously, System Manager wants to manage your /etc/nix.conf file for you, after which you can instead place your configurations directly in the flake.nix file, including specifying experimental features. + +To do so requires a careful set of steps. Follow these steps precisely when starting out with a fresh system. + +!!! Note + We will first run System Manager to create an initial flake.nix and system.nix file; we will then delete the `/etc/nix/nix.conf` file, and instead add the flags to the flake.nix file. Then we will run System Manager again to start managing your system, inluding the `/etc/nix/nix.conf` file. + +1. Temporarily run Run system manager init with experimental features enabled by including the following line in /etc/nix/nix.conf; this way `init` will generate a `flake.nix` file: + +``` +experimental-features = nix-command flakes +``` + +And then running System Manager with the init subcommand: + +``` +nix run 'github:numtide/system-manager' -- init +``` + +(For this step, do not simply add the flag for experimental features; otherwise init won't create the flake.nix file.) + +2. Under ~/.config/system-manager, edit the `flake.nix` file, replacing this line: + +``` + modules = [ ./system.nix ]; +``` + +with this: + +```nix +modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + } + ./system.nix +]; +``` + +3. Delete the /etc/nix.conf file, optionally backing it up first: + +``` +sudo cp /etc/nix/nix.conf /etc/nix/nix_old # optional +sudo rm /etc/nix/nix.conf +``` + +4. Run System Manager to initialize your system, with the experimental flags set this one time in the command-line: + +[Need updated --sudo command] +``` +cd ~/.config/system-manager +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +System Manager is now managing your system for you, including the /etc/nix/nix.conf file. And experimental features are required and turned on through the flake.nix file, meaning you do not need to include the --extra-experimental-features flag when you run System Manager: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +Next, if you want to make sure experimental features are always on, you can add it to your flake. + +[TODO: Another example here] + +# Using System Manager in a non-Interactive Setting + +If you're running System Manager in a non-interative script, you might run into a problem with the four questions presented when you first run it: + +* Do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.numtide.com' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + +* Do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + +The reason for these questions is Numtide has made pre-built binary versions of System Manager available from our cache, which speeds up performance since your system doesn't have to build System Manager from source. However, this triggers Nix to ask these four questions. You'll most likely want to answer "y" to all four. + +But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the --accept-flake-config option like so: + +``` +nix run 'github:numtide/system-manager' --accept-flake-config --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +If you like, you can add these settings into your flake file, such as in the following: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + nix.settings.extra-substituters = https://cache.numtide.com; + nix.settings.extra-trusted-public-keys = niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=; + } + ./glow.nix + ]; + }; + }; +} +``` + +Remember, however, the flake shows what the system looks like *after* System Manager runs. That means these changes won't affect the first run of System Manager, which in this case is likely through a script. As such, the first time you run System Manager, you'll still need the `--accept-flake-config` flag. Then on subsequent runs you don't need the `--accept-flake-config flag`. + +# Recommended Workflow if You Already Have Your Nix Files + +If you already have your .nix files, you don't need to run the init subcommand. Instead, we recommend the following if you're starting out on a clean system: + +1. Remove the /etc/nix/nix.conf file. Then, when you run your System Manager the first time, System Manager will take control managing this file for you. You can then place any configuration you previously had in the /etc/etc/nix.conf file in your .nix files. + +2. Run System Manager the first time, and you'll be ready to go. + +As an example, here's a starting nix.flake file: + +**flake.nix** +``` +{ + description = "Standalone System Manager configuration"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + } + ./glow.nix + ]; + }; + }; +} +``` + +Notice that we've included in the modules list an object that sets experimental features, turning on flakes. + +Now here's the glow.nix file referenced above; it simply installs the `glow` command, which is for displaying markdown files in a shell: + +**glow.nix** + +```nix +{ pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + glow + ]; + }; +} +``` + +go ahead and delete /etc/nix/nix.conf: + +``` +sudo rm /etc/nix/nix.conf +``` + +And now run System Manager. Because you removed nix.conf, you'll need to turn on experimental features as a command-line option. + +``` +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can sipmly run: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + + +# 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`, and `system.nix` + +Note that we'll be using the files generated by the 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ ./system.nix ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +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 as nixpkgs.hostPlatform = "x86_64-linux"; +* `environment`, consisting of + * systemPackages + * etc +* `systemd.services` +* `systemd.tmpfiles` + +For example, you could then replace the + +``` +modules = [ ./system.nix ]; +``` + +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: + +```nix +modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + } + ./modules/tree.nix + ./modules/bat.nix +]; +``` + +Then here are the individual "recipe" files. + +**modules/bat.nix** + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.bat + ]; + }; + }; +} +``` + +**modules/tree.nix** + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tree + ]; + }; + }; +} +``` + +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](#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](#the-main-flakenix-file). + + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + systemd.services.say-hello = { + description = "say-hello"; + enable = true; + wantedBy = [ "system-manager.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + ${lib.getBin pkgs.hello}/bin/hello + ''; + }; + }; +} +``` + +Note: + +This line is required in the above example: + +``` +wantedBy = [ "system-manager.target" ]; +``` + +(There are other options for wantedBy; we discuss it in full in our Reference Guide under [Specifying WantedBy Setting](./reference-guide.md#specifying-the-wantedby-setting)) + +Activate it using the same nix command as earlier: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +This will create a system service called `say-hello` (the name comes from the line `config.systemd.services.say-hello`) in a unit file at `/etc/systemd/system/say-hello.service` with the following inside it: + +``` +[Unit] +Description=say-hello + +[Service] +Environment="PATH=/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/bin:/nix/store/qqvfnxa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/bin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/bin: +/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/bin:/nix/store/g48529av5z0vcsyl4d2wbh9kl58c7p73-systemd-minimal-258/bin:/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/sbin:/nix/store/qqvfn +xa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/sbin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/sbin:/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/sbin:/nix/store/g48529av5z0vcsyl4d2wbh9 +kl58c7p73-systemd-minimal-258/sbin" +ExecStart=/nix/store/d8rjglbhinylg8v6s780byaa60k6jpz1-unit-script-say-hello-start/bin/say-hello-start +RemainAfterExit=true +Type=oneshot + +[Install] +WantedBy=system-manager.target +``` + +!!! 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: + +``` +journalctl -n 20 +``` + +and you can find the following output in it: + +``` +Nov 18 12:12:51 my-ubuntu systemd[1]: Starting say-hello.service - say-hello... +Nov 18 12:12:51 my-ubuntu say-hello-start[3488278]: Hello, world! +Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. +``` + +!!! Note + If you remove the `./apps.nix` line from the `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: + +``` +wantedBy = [ "system-manager.target" ]; +``` + +(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: + +``` +wantedBy = [ "timers.target" ] +``` + +# 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ + ./apps.nix + ]; + }; + }; +} +``` + +Notice this flake references a file called apps.nix. In that file we'll add to the systemPackages attribute set. Here's the apps.nix file: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.hello + pkgs.bat + ]; + }; + }; +} +``` + +When you run System Manager, you should have the packages called `hello` and `bat` available. + +``` +$ which hello +/run/system-manager/sw/bin//hello +$ which bat +/run/system-manager/sw/bin//bat +``` + +!!! Note + The first time you install an app through System Manager, System Manager will add a file inside `/etc/profile.d`. This file adds on the `/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: + +```nix +{ + description = "Standalone System Manager configuration"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + ({ lib, pkgs, ... }: { + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment.systemPackages = [ + pkgs.hello + pkgs.bat + ]; + }; + }) + ]; + }; + }; +} +``` + +# 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + ./files1.nix + ]; + }; + }; +} +``` + +Notice this references a file called `files1.nix`. To create files, you add attributes to the config.environment.etc attribute set as follows: + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + etc = { + "test/test2/something.txt" = { + text = '' + This is just a test!! + ''; + mode = "0755"; + user = "ubuntu"; + group = "ubuntu"; + }; + }; + }; + }; +} +``` + +This creates a single file inside the folder `/etc/test/test2/` and the file is called `something.txt`. + +After running the above with System Manager, you can verify the file exists: + +``` +$ cat /etc/test/test2/something.txt +This is just a test!! +``` + +Note that if you prefer, you can combine the above flake and separate .nix file into a single flake like so: + +```nix +{ + description = "Standalone System Manager configuration"; + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + config.nixpkgs.hostPlatform = "x86_64-linux"; + config.environment.etc."test/test2/something.txt" = { + text = '' + This is just a test!!! + ''; + mode = "0755"; + user = "ubuntu"; + group = "ubuntu"; + }; + } + ]; + }; + }; +} +``` + +## 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`): + +``` +with_ownership = { + text = '' + This is just a test! + ''; + mode = "0755"; + uid = 5; + gid = 6; +}; +``` + +And here's an example that uses named user and group (notice we set `user` and `group`): + +``` +with_ownership2 = { + text = '' + This is just a test! + ''; + mode = "0755"; + user = "nobody"; + group = "users"; +}; +``` + +!!! Tip + This use of `uid`/`gid` for 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: + +```nix + # Configure systemd tmpfile settings + systemd.tmpfiles = { + rules = [ + "D /var/tmp/system-manager 0755 root root -" + ]; + + settings.sample = { + "/var/tmp/sample".d = { + mode = "0755"; + }; + }; + }; +``` + +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 aged-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. + +# Working with remote flakes + +Instead of saving your System Manager configuration files locally, you can optionally keep them in a remote Git repository, such as on GitHub. + +!!! Note + This is a great option if you plan to use the files on multiple machines. + +In order to store them on a remote repo, it's imperative that you keep your flake.lock file up to date. + +## What's a flake.lock file? + +A flake.lock file is a JSON file that stores the exact versions of all the inputs your flake file depends on, including things like nixpkgs, System Manager itself, and anything else you might import. Instead of pulling the latest version every time you build, the lock file ensures that the same inputs are used consistently across machines and over time. This makes your configuration reproducible, stable, and rollback-friendly. When you do want to update to new versions, you run a command like nix flake update, which refreshes the lock file in a controlled way. + +## Setting up your project for remote hosting + +As you create your flake.nix and set up any supporting files, you'll want to test it out thoroughly before pushing it up to a remote repo. + +For this you have a couple options; one is to test it out on the machine you're currently using. However, we recommend against this, as there might be artifacts on your computer that can interfere with the configuration. + +Instead, we recommend starting with a fresh machine. One option is to spin up an EC2 instance on AWS; another is to open up a Virtual Box session on your computer. + +!!! Important + You'll need to ensure you have at least 16GB of disk space on the virtual machine. If you go with 8GB, you're going to run out of space. + +After starting with a fresh machine, install Nix, copy over your flake.nix and supporting files, and test it out. Once you're ready, make sure your flake.lock file is up to date. You can create or update the flake.nix file by typing: + +``` +nix flake update +``` + +And make sure you've pushed it up to the repo. (If you don't do this step, nix will try to build a flake.lock, but will be unable to write it to the same location as the other files, and will error out.) + +[todo: Let's create a repo under numtide for this instead of using mine --jeffrey] + +```b +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/frecklefacelabs/system-manager-test#default --sudo +``` + +### When should you update your flake.nix file? + +Generally, you only need to update your flake.nix file when you want newer versions of your inputs (nixpkgs, etc). Updating isn't necessary for daily use; your configuration will continue to work with the locked versions. But you will want to update your flake.lock file in cases such as: + +* You want newer package versions (e.g. newer `btop`, etc.) +* You want security patches +* You've added new imputs to your flakes (in which case you'll be required to update `flake.lock`) +* You're preparing a fresh install and decide this is a good time to upgrade everything + +### Can't System Manager build flake.lock for me? + +Yes, but only if the flake.nix file is local to your machine. The problem is System Manager will try to write a flake.lock file in the same location as the flake.nix file, which isn't possible (at this time) with a GitHub repo. + + + +### Ensuring success + +In order to ensure System Manager retrieves the correct .nix files from your repo, we recommend including either a branch or a tag along with your repo. + + + +## Running System Manager with a remote flake + +!!! Tip + Before you run this command, we recommend that you nevertheless create a folder to run it from, such as ~/.config/system-manager. + + +# Using Blueprint with System Manager + +Blueprint is an opinionated library that maps a standard folder structure to flake outputs, allowing you to divide up your flake into individual files across these folders. This allows you to modularize and isolate these files so that they can be maintained individually and even shared across multiple projects. + +Blueprint has bulit-in support for System Manager, which means: + +* You do not need to call system-manager.lib.makeSystemConfig; Blueprint calls this for you +* You must follow Blueprint's folder structure by placing your files under the hosts folder, and you must name your files `system-configuration.nix`. +* You can have multiple folders under the `hosts` folder (but one level deep), and you can access these using the standard nix specifier, e.g. `.#folder-name.` + +In this section we should you how to use Blueprint with System Manager. + +Blueprint provides its own initialization that you can start with if you don't already have a flake.nix file using Blueprint. The command to type is: + +``` +nix flake init -t github:numtide/blueprint +``` + +This results in the following flake: + +```nix +{ + description = "Simple flake with a devshell"; + + # Add all your dependencies here + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + blueprint.url = "github:numtide/blueprint"; + blueprint.inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Load the blueprint + outputs = inputs: inputs.blueprint { inherit inputs; }; +} +``` + +Now add System Manager to its inputs section: + +``` + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; +``` + +Next, create a folder called hosts, and under that a folder called default: + +``` +mkdir hosts +cd hosts +mkdir default +cd default +``` + +Inside `default` is where you'll put your configuration file. + +**This configuration file must be named `system-configuration.nix**`. + +For example, here's a configuration file that installs `bat`: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.bat + ]; + }; + }; +} +``` + +!!! Note + Notice that we need to include nixpkgs.hostPlatform in this file, as there's no place to include it in the parent `flake.nix` file. + +Now return to the folder two levels up (the one containing flake.nix) and you can run System Manager: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +!!! Remember + As mentioned elsewhere, if this is the first time running System Manager on this computer, you'll need to log out and log back in to pick up the new path. + +Then you should find `bat` on your path: + +``` +$ which bat +/run/system-manager/sw/bin//bat +``` + +The default folder is called default; you can also refer to folders by name as mentioned earlier. + +If, for example, under the `hosts` folder you have a folder called `tree`, and inside `tree` create a file called `system-configuration.nix` with the following contents: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tree + ]; + }; + }; +} +``` + +Then you can choose to install tree by specifying the tree folder like so: + +``` +nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo +``` + +## Using multiple configuration files with Blueprint + +If you want to load multiple configuration files at once, you can create a special system-configuration.nix file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under hosts; for example, you might name it cli-tools. Starting in the folder with flake.nix: + +``` +cd hosts +mkdir cli-tools +cd cli-tools +mkdir modules +``` + +Then inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: + +``` +{ config, lib, pkgs, ... }: +{ + # Import all your modular configs - they auto-merge! ✨ + imports = [ + ./modules/tldr.nix + ./modules/cowsay.nix + ]; + + # Base configuration that applies to everything + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + }; +} +``` + +(Notice this time we can put the nixpkgs.hostPlatform in a single place. As such we won't need it in the configuration files.) + +Now move into the `modules` folder: + +``` +cd modules +``` + +And create two files here: + +tldr.nix: + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tldr + ]; + }; + }; +} +``` + +cowsay.nix: +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.cowsay + ]; + }; + }; +} +``` + +Now you can return to the top level where you flake.nix file is and run these two configuration files: + +``` +nix run 'github:numtide/system-manager' -- switch --flake '.#cli-tools' --sudo +``` + +This means if you want to include various recipes, you can easily do so. + + +# Full Examples + +## Full Example: Installing PostgreSQL + +Here's a .nix file that installs PostgreSQL. + +Note: System Manager is still in its early state, and doesn't yet have user management, which is a planned feature that will be here soon. As such, for now, before you run this, you'll need to manually create the postgres user. Additionally, go ahead and create two directories and grant the postgres user access to them: + +```bash +# Create postgres user and group +sudo groupadd -r postgres +sudo useradd -r -g postgres -d /var/lib/postgresql -s /bin/bash postgres + +# Create directories with proper permissions +sudo mkdir -p /var/lib/postgresql +sudo chown postgres:postgres /var/lib/postgresql + +sudo mkdir -p /run/postgresql +sudo chown postgres:postgres /run/postgresql +``` + +Here, then, is the .nix file. + +```nix +{ config, lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + postgresql_16 + ]; + + # PostgreSQL service + systemd.services.postgresql = { + description = "PostgreSQL database server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "notify"; + User = "postgres"; + Group = "postgres"; + ExecStart = "${pkgs.postgresql_16}/bin/postgres -D /var/lib/postgresql/16"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + KillMode = "mixed"; + KillSignal = "SIGINT"; + TimeoutSec = 120; + + # Create directories and initialize database + ExecStartPre = [ + "${pkgs.coreutils}/bin/mkdir -p /var/lib/postgresql/16" + "${pkgs.bash}/bin/bash -c 'if [ ! -d /var/lib/postgresql/16/base ]; then ${pkgs.postgresql_16}/bin/initdb -D /var/lib/postgresql/16; fi'" + ]; + }; + + environment = { + PGDATA = "/var/lib/postgresql/16"; + }; + }; + + # Initialize database and user + systemd.services.postgresql-init = { + description = "Initialize PostgreSQL database for myapp"; + after = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "postgres"; + }; + script = '' + # Wait for PostgreSQL to be ready + until ${pkgs.postgresql_16}/bin/pg_isready; do + echo "Waiting for PostgreSQL..." + sleep 2 + done + + # Optional: Create database if it doesn't exist + ${pkgs.postgresql_16}/bin/psql -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw myapp || \ + ${pkgs.postgresql_16}/bin/createdb myapp + + # Optional: Create user if it doesn't exist + ${pkgs.postgresql_16}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='myapp'" | ${pkgs.gnugrep}/bin/grep -q 1 || \ + ${pkgs.postgresql_16}/bin/createuser myapp + + # Grant database privileges + ${pkgs.postgresql_16}/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp" + + # Grant schema privileges (allows creating tables!) + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON SCHEMA public TO myapp" + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL TABLES IN SCHEMA public TO myapp" + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO myapp" + + echo "PostgreSQL is ready and configured!" + ''; + }; + }; +} +``` + +## Full Example: Installing Nginx + +Here's a .nix file that installs and configures nginx as a system service. Note that this version only supports HTTP and not HTTPS; later we provide an example that includes HTTPS. + +!!! Tip + This is simply an example to help you learn how to use System Manager. The usual way to install nginx under Nix is to use the [nginx package](https://search.nixos.org/packages?channel=25.11&show=nginx&query=nginx). + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable and configure services + services = { + nginx.enable = true; + }; + + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.hello + pkgs.mariadb + pkgs.nginx + ]; + + # Add directories and files to `/etc` and set their permissions + etc = { + "nginx/nginx.conf"= { + + user = "root"; + group = "root"; + mode = "0644"; + + text = '' +# The user/group is often set to 'nginx' or 'www-data', +# but for a simple root-only demo, we'll keep the default. +# user nginx; +worker_processes auto; + +# NGINX looks for modules relative to the install prefix, +# but we explicitly point to the Nix store path to be safe. +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include ${pkgs.nginx}/conf/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + # Basic default server block + server { + listen 80; + server_name localhost; + + # Point the root directory to a standard location or a Nix store path + root ${pkgs.nginx}/html; + + location / { + index index.html; + } + + # Example log files + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + } +} + ''; + + + }; + }; + }; + + # Enable and configure systemd services + systemd.services = { + nginx = { + enable = true; + description = "A high performance web server and reverse proxy server"; + wantedBy = [ "system-manager.target" ]; + preStart = '' + mkdir -p /var/log/nginx + chown -R root:root /var/log/nginx # Ensure permissions are right for root user + ''; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/nginx.pid"; + + # The main binary execution command, pointing to the Nix store path + ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; + + # The command to stop the service gracefully + ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; + + # NGINX needs to run as root to bind to port 80/443 + User = "root"; + Group = "root"; + + # Restart policy for robustness + Restart = "on-failure"; + }; + }; + }; + + + }; +} + +``` + +## Full Exapmle: Installing Nginx with for HTTPS with a Secure Certificate + +Here's an example that installs nginx. This exapmle shows places where you would copy in your own secure certificate information. + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable and configure services + # Commenting this out -- apparently this loads a bunch of nginx service files we don't need or want + #services = { + # nginx.enable = true; + #}; + + environment = { + systemPackages = [ + pkgs.hello + pkgs.mariadb + pkgs.nginx + ]; + + # Add SSL certificate files to /etc + etc = { + # SSL Certificate + "ssl/certs/your-domain.crt" = { + user = "root"; + group = "root"; + mode = "0644"; + # Option 1: Embed the certificate directly + text = '' +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUXbQ2ie2/2pxLH/okEB4KEbVDqjEwDQYJKoZIhvcNAQEL... +-----END CERTIFICATE----- + ''; + # Option 2: Or reference a file from your repo + # source = ./certs/your-domain.crt; + }; + + # SSL Private Key + "ssl/private/your-domain.key" = { + user = "root"; + group = "root"; + mode = "0600"; # Restrict access to private key! + # Option 1: Embed the key directly + text = '' +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5gQjZxG7rYPub.... +-----END PRIVATE KEY----- + ''; + # Option 2: Or reference a file from your repo + # source = ./certs/your-domain.key; + }; + + # Optional: Certificate chain/intermediate certificates + # For this demo we're using a self-signed cert; for a real + # one, uncomment below and add your + "ssl/certs/chain.pem" = { + user = "root"; + group = "root"; + mode = "0644"; + text = '' + -----BEGIN CERTIFICATE----- +YOUR_CHAIN_CERTIFICATE_HERE... + -----END CERTIFICATE----- + ''; + #}; + + # Nginx configuration with HTTPS + "nginx/nginx.conf" = { + user = "root"; + group = "root"; + mode = "0644"; + text = '' +worker_processes auto; + +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include ${pkgs.nginx}/conf/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + # SSL Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; + + # HTTP Server - Redirect to HTTPS + server { + listen 80; + server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; + + # Redirect all HTTP to HTTPS + return 301 https://$server_name$request_uri; + } + + # HTTPS Server + server { + listen 443 ssl; + server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; + + # SSL Certificate files + ssl_certificate /etc/ssl/certs/your-domain.crt; + ssl_certificate_key /etc/ssl/private/your-domain.key; + + # Optional: Certificate chain + # ssl_trusted_certificate /etc/ssl/certs/chain.pem; + + # Optional: Enable OCSP stapling + ssl_stapling on; + ssl_stapling_verify on; + + # Optional: Enable HSTS (HTTP Strict Transport Security) + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + root ${pkgs.nginx}/html; + + location / { + index index.html; + } + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + } +} + ''; + }; + }; + }; + + systemd.services = { + nginx = { + enable = true; + #description = "A high performance web server and reverse proxy server"; + wantedBy = [ "system-manager.target" ]; + preStart = '' + mkdir -p /var/log/nginx + chown -R root:root /var/log/nginx + + # Verify SSL certificate files exist + if [ ! -f /etc/ssl/certs/your-domain.crt ]; then + echo "ERROR: SSL certificate not found!" + exit 1 + fi + if [ ! -f /etc/ssl/private/your-domain.key ]; then + echo "ERROR: SSL private key not found!" + exit 1 + fi + ''; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/nginx.pid"; + ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; + ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; + User = "root"; + Group = "root"; + Restart = "on-failure"; + }; + }; + }; + }; +} + +``` + +## Full Example: Managing a System that runs Custom Software + +Here's an example where you might have custom web software living in a repository and you want to run the software on a system behind nginx. + +## Live example + +We have a complete example live that you can try out. All you need is a fresh server (such as on Amazon EC2) with at least 16GB memory. (We recommend the latest Ubuntu, with a t3Large instance, with 16GB RAM. Then allow SSH, HTTP traffic, and HTTPS traffic if you plan to build on these examples.) We have two repos: + +1. The sample application + +2. The configuration files + +The configuration files install both nginx and the sample app. + +After you spin up an instance, install nix for all users: + +``` +sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon +``` + +Next, log out and log back in so that nix is available in the system path. + +And then you can run System Manager and deploy the app with one command: + +``` +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake github:frecklefacelabs/system-manager-custom-app-deploy/v1.0.0#default --sudo +``` + +(Remember, the first time System Manager runs, it takes up to five minutes or so to compile everything.) + +!!! Tip + We're specifying a tag in our URL. This is good practice to make sure you get the right version of your flakes. Also, modern Nix supports the use of a protocol called "github", and when you use that protocol, you can specify the tag behind a slash symbol, as we did here for tag v1.0.0. + +!!! Tip + If you make changes to your flakes, be sure to create a new tag. Without it, Nix sometimes refuses to load the "latest version" of the repo, and will insist on using whatever version of your repo it used first. + +Then, the app should be installed, with nginx sitting in front of it, and you should be able to run: + +``` +curl localhost +``` +And it will print out a friendly JSON message such as: + +``` +{"message":"Welcome to the Bun API!","status":"running","endpoints":["/","/health","/random","/cowsay"]} +``` + +We even included cowsay in this sample, which you can try at `curl localhost/cowsay`. Now even though cowsay is meant for fun, the primary reason is this is a TypeScript app that uses `bun`, and we wanted to demonstrate how easy it is to include `npm` libraries. `bun` includes a feature whereby it will install dependency packages from `package.json` automatically the first time it runs, greatly simplifying the setup. + +One thing about the .nix files in this repo is that they in turn pull code (our TypeScript app) from another remote repo. Using this approach, you can separate concerns, placing the deployment .nix files in one repo, and the source app in a separate repo. + +Here are further details on the individual nix files. + +First we have a flake much like the usual starting point: + +```nix +# flake.nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ + + { + nix.settings.experimental-features = "nix-command flakes"; + services.myapp.enable = true; + } + ./system.nix + ./nginx.nix + ./bun-app.nix + ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +Next is the .nix configuration that installs and configures nginx. This is a simple ngnix configuration, as it simply routes incoming HTTP traffic directly to the app: + +``` +# nginx.nix +{ config, lib, pkgs, ... }: +{ + config = { + services.nginx = { + enable = true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + virtualHosts."_" = { + default = true; + + locations."/" = { + proxyPass = "http://127.0.0.1:3000"; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + + locations."/health" = { + proxyPass = "http://127.0.0.1:3000/health"; + extraConfig = '' + access_log off; + ''; + }; + }; + }; + }; +} +``` + +Next, here's the .nix configuration that creates a service that runs the app. + +```nix +# bun-app.nix +{ config, lib, pkgs, ... }: +let + # Fetch the app from GitHub + appSource = pkgs.fetchFromGitHub { + owner = "frecklefacelabs"; + repo = "typescript_app_for_system_manager"; + rev = "v1.0.0"; # Use a tag + sha256 = "sha256-TWt/Y2B7cGxjB9pxMOApt83P29uiCBv5nVT3KyycYEA="; + }; +in +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Install Bun + environment.systemPackages = with pkgs; [ + bun + ]; + + # Simple systemd service - runs Bun directly from Nix store! + systemd.services.bunapp = { + description = "Bun TypeScript Application"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + User = "ubuntu"; + Group = "ubuntu"; + WorkingDirectory = "${appSource}"; + # Bun will auto-install dependencies from package.json on first run + ExecStart = "${pkgs.bun}/bin/bun run index.ts"; + Restart = "always"; + RestartSec = "10s"; + }; + + environment = { + NODE_ENV = "production"; + }; + }; + }; +} + +``` + +And finally, here's the `index.ts` file; it's just a simple REST app that also makes use of one third-party `npm` library. + +``` +import cowsay from "cowsay"; + +const messages = [ + "Hello from System Manager!", + "Bun is blazingly fast! ?", + "Nix + Bun = Easy deployments", + "Making it happen!", + "Nix rocks!" +]; + +const server = Bun.serve({ + port: 3000, + fetch(req) { + const url = new URL(req.url); + + if (url.pathname === "/") { + return new Response(JSON.stringify({ + message: "Welcome to the Bun API!", + status: "running", + endpoints: ["/", "/health", "/random", "/cowsay"] + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/health") { + return new Response(JSON.stringify({ + status: "healthy" + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/random") { + const randomMessage = messages[Math.floor(Math.random() * messages.length)]; + return new Response(JSON.stringify({ + message: randomMessage, + timestamp: new Date().toISOString() + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/cowsay") { + const cow = cowsay.say({ + text: "Deployed with System Manager and Nix!" + }); + return new Response(cow, { + headers: { "Content-Type": "text/plain" } + }); + } + + return new Response("Not Found", { status: 404 }); + }, +}); + +console.log(`? Server running on http://localhost:${server.port}`); +``` + + +# Optional: Installing System Manager Locally + +Nix allows you to run code that's stored remotely in a repo, such as in GitHub. As such, you don't have to install System Manager locally to use it. However, if you want to install locally, you can do so with the following `nix profile` command. + +``` +nix profile add 'github:numtide/system-manager' +``` + +Or, if you don't have the optional features set in `/opt/nix/nix.conf`, you can provide them through the command line: + +``` +nix profile add 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' +``` + +!!! Tip + After System Manager is installed locally, you no longer need to worry about whether you have experimental features installed. You will simply pass the --flake option to System Manager. + +When you install System Manager, you might get some warnings about trusted user; this simply means you're not in the trusted user list of nix. But System Manager will still install and work fine. + +Then you can find System Manager: + +``` +$ which system-manager +/home/ubuntu/.nix-profile/bin/system-manager +``` + +And you can run System Manager: + +``` +system-manager switch --flake . --sudo +``` + + +!!! Tip + System Manager is still in an early state and undergoing active development. Installing locally will not immediately pick up new changes. If you decide to install locally, you'll want to periodically check our GitHub repo for changes, and re-install it if necessary by using `nix profile upgrade`. + + +# More stuff, possibly: + +Inspecting /var/lib/system-manager/state/system-manager-state.json + +Troubleshooting Guide + +Recipes (individual software packages, etc.) + +Package overlays + +Managing a Remote System with System Manager + +Working with Timers + +Managing Users + diff --git a/docs/site/stylesheets/extra.css b/docs/stylesheets/extra.css similarity index 100% rename from docs/site/stylesheets/extra.css rename to docs/stylesheets/extra.css From e5b18c7749893513c1a01b313b7f8a1fb3c400a4 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 10:15:14 +0100 Subject: [PATCH 02/22] fix file endings with dos2unix --- docs/site/reference-guide.md | 4120 +++++++++++++++++----------------- 1 file changed, 2060 insertions(+), 2060 deletions(-) diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md index cd8ceb4..d36ab5e 100644 --- a/docs/site/reference-guide.md +++ b/docs/site/reference-guide.md @@ -1,2060 +1,2060 @@ -# Reference Guide - -[PLEASE NOTE AS YOU EDIT THIS: I KNOW I MISSED BACKTICKS IN A LOT OF PLACES. I'LL GO THROUGH IT CAREFULLY AND ADD THEM ALL IN, SO DON'T WORRY ABOUT ADDING COMMENTS ABOUT THEM.] - -To get the most out of System Manager, we're offering this guide to help you make the best decisions based on your particular situation. - -# Table of Contents - -- [System Requirements](#system-requirements) -- [Installation](#installation) - - [Regarding Experimental Features](#regarding-experimental-features) - - [Running under sudo](#running-under-sudo) - - [Command Line Usage](#command-line-usage) -- [Command-line Options](#command-line-options) - - [init](#init) - - [switch](#switch) - - [register](#register) - - [build](#build) - - [deactivate](#deactivate) - - [pre-populate](#pre-populate) - - [sudo](#sudo) -- [Setting up a folder and file structure](#setting-up-a-folder-and-file-structure) - - [Deciding on a folder structure](#deciding-on-a-folder-structure) - - [Choosing a location](#choosing-a-location) - - [Choosing a file structure](#choosing-a-file-structure) - - [Dealing with conflicting .nix files](#dealing-with-conflicting-nix-files) -- [Letting System Manager manage `/etc/nix/nix.conf`](#letting-system-manager-manage-etcnixnixconf) -- [Recommended Workflow for Starting Out](#recommended-workflow-for-starting-out) -- [Using System Manager in a non-Interactive Setting](#using-system-manager-in-a-non-interactive-setting) -- [Recommended Workflow if You Already Have Your Nix Files](#recommended-workflow-if-you-already-have-your-nix-files) -- [Building system-manager .nix files](#building-system-manager-nix-files) - - [The Main flake.nix File](#the-main-flakenix-file) -- [Managing System Services](#managing-system-services) - - [Specifying the wantedBy Setting](#specifying-the-wantedby-setting) -- [Managing Software Installations](#managing-software-installations) - - [Example: Installing a couple apps](#example-installing-a-couple-apps) -- [Working With /etc Files Declaratively](#working-with-etc-files-declaratively) - - [Example: Creating a file in /etc](#example-creating-a-file-in-etc) - - [Permissions](#permissions) - - [Users and Groups](#users-and-groups) -- [Supporting System Services with tmp files and folders](#supporting-system-services-with-tmp-files-and-folders) -- [Working with remote flakes](#working-with-remote-flakes) - - [What's a flake.lock file?](#whats-a-flakelock-file) - - [Setting up your project for remote hosting](#setting-up-your-project-for-remote-hosting) - - [When should you update your flake.nix file?](#when-should-you-update-your-flakenix-file) - - [Can't System Manager build flake.lock for me?](#cant-system-manager-build-flakelock-for-me) - - [Ensuring success](#ensuring-success) - - [Running System Manager with a remote flake](#running-system-manager-with-a-remote-flake) -- [Using Blueprint with System Manager](#using-blueprint-with-system-manager) - - [Using multiple configuration files with Blueprint](#using-multiple-configuration-files-with-blueprint) -- [Full Examples](#full-examples) - - [Full Example: Installing PostgreSQL](#full-example-installing-postgresql) - - [Full Example: Installing Nginx](#full-example-installing-nginx) - - [Full Example: Installing Nginx for HTTPS with a Secure Certificate](#full-example-installing-nginx-for-https-with-a-secure-certificate) - - [Full Example: Managing a System that runs Custom Software](#full-example-managing-a-system-that-runs-custom-software) - - [Live example](#live-example) -- [Optional: Installing System Manager Locally](#optional-installing-system-manager-locally) - -- FAQ (Maybe put in its own document) - -# Command Line Usage - -The basic command looks like this: - -```nix -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -This is the most common scenario you'll use. -## Command-line Options - -### init - -This subcommand creates two initial files for use with system manager, a fully-functional flake.nix, and a system.nix file that contains skeleton code. - -#### Command line options - -**path:** The path where to create the files. If the path doesn't exist, it will be created. - -#### Example - -``` -nix run 'github:numtide/system-manager' -- init -``` - -This will create the initial files in `~/.config/system-manager`. - -``` -nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-manager' -``` - -This will create the initial files in `/home/ubuntu/system-manager`. - -!!! Note - Presently, System Manager requires Flakes to be active. If you choose to not include the experimental features line in /etc/nix/nix.conf (and instead use the experimental features command line option), then init will only create a system.nix file, rather than both a flake.nix file and system.nix file. - -### switch - -The `switch` subcommand builds and activates your configuration immediately, making it both the current running configuration and the default for future boots. Use it whenever you want to apply your changes. - -**Note: Rollbacks are not yet implemented.** - -The following two parameters are currently both required: - -**--flake**: Specifies a flake to use for configuration. - -**--sudo**: Specifies that System Manager can use sudo. - -### register - -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - -The `register` subcommand builds and registers a System Manager configuration, but does not activate it. Compare this to `switch`, which does everything register does, but then activates it. - -### build - -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - -The `build` subcommand builds everything needed for a switch, but does not register it. - -### deactivate - -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - -The `deactivate` deactivates System Manager. - -### pre-populate - -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - -The `prepopulate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. - -### sudo - -The sudo subcommand grants sudo access to System Manager, while running under the current user. All created files with be owned by the current user. - -# Setting up a folder and file structure - -Before you begin with System Manager, you'll need to decide on your folder structure. - -!!! Note - If you prefer, you can host all your System Manager configuration files on a remote Git repo (such as GitHub), and then you don't need to worry about where on your computer to store the files. For more info, see [Working with Remote Flakes](#working-with-remote-flakes). - -Technically, you are free to set up your folders and files however you like; System Manager does not enforce any rules, thus allowing you full flexibility. Below are simply some options that we recommend. - -!!! Tip - While you are free to have different system manager .nix files scattered throughout your system, we recommend, if possible, keeping them in a single location simply for organizational purposes. But again, this is just a recommendation and you're not bound by any such rules. - -## Deciding on a folder structure - -You’ll need to choose where your System Manager configuration will live. Here are two main organizational patterns we recommend. - -* **Option A**: A single folder for all your configuration - -A single folder keeps everything together. This offers a clean long-term solution, along with easy version control. It's also convenient for replicating between machines. - -* **Option B**: A separate folder for each use case - -While not as common, it’s entirely possible to organize your System Manager configuration into multiple independent folders, each focused on a specific use case. In this model, you treat each configuration as its own standalone unit, often stored in its own Git repository. - -For example, you might keep: - -* a dedicated configuration folder strictly for managing nginx, - -* another for custom systemd services, - -* another for developer tools, - -* and yet another for monitoring and observability packages. - -In this manner, you can then build up a system by picking and choosing which services you need for a particular machine, and pull each one down from GitHub. - -To make this happen, however, requires careful consideration [as we discuss later](#dealing-with-conflicting-nix-files). - -## Choosing a location - -### Option 1: Your personal ~/.config folder - -If you're managing a system yourself and only you will be using it, one possibility is to put the files in ~/.config/system-manager. - -This approach keeps everything scoped to you and avoids having to place files under /etc and, perhaps most importantly, avoids have to use sudo. Here's an example layout: - -``` -~/.config/system-manager/ - flake.nix - modules/ - default.nix -``` - -!!! Tip - Avoid this location if multiple people use the machine or if this configuration is meant to be shared with a team. Home-directory paths are user-specific and may not make sense across machines. - -### Option 2: A shared /etc/system-manager folder (Recommended for multi-user or organizational setups) - -If you are: - -* managing multiple machines, - -* part of a team, - -* deploying configurations in a corporate or server environment, - -* or simply want a clear system-level location, - -then /etc/system-manager is a great choice. Among the advantages are consistency across all machines; standard within an organization; and treating system manager as a system-level tool rather than a personal configuration. Here's an example layout: - -``` -/etc/system-manager/ - flake.nix - modules/ - default.nix - services.nix -``` - -## Choosing a file structure - -After choosing where your configuration lives, you must decide how to structure the files inside it. And note that while system-manager does not enforce any rules, we do recommend you maintain consistency, especially if you have multiple locations on your computer where you store system manager .nix files. - -Essentially, you have two options: - -* A single flake.nix file - -* A reusable flake.nix file with one or more separate configuration files that describe what the system will look like. - -Within Option B, you can also use our open-source Blueprint product to help you manage your files, which we'll cover shortly. - -### Option A: Single flake.nix file - -This configuration is ideal for: - -* Small, simple setups - -* Demos and experiments - -* One-off configurations - -Drawback: This approach doesn’t scale well once you need multiple services, multiple hosts, or reusable modules. - -### Option B: Flake file with one or more configuration files - -This is the structure used by most production setups and by NixOS itself. Your arrangement might look like: - -``` -system-manager/ - flake.nix - modules/ - default.nix - services.nix - users.nix -``` - -Or, perhaps you might have separate services, one per file: - -``` -system-manager/ - flake.nix - modules/ - default.nix - service-1.nix - service-2.nix - users.nix -``` - -This also lends itself well to having multiple "recipes". For example, you might want to add nginx and postgres to your system. You might have them preconfigured somewhere, and simply "drop" them in like so: - -``` -system-manager/ - flake.nix - modules/ - default.nix - service-1.nix - service-2.nix - users.nix - nginx.nix - postgres.nix -``` - -!!! Tip - This is the approach we use in our examples in this document. That way each isolated "recipe" is repeatable and can be re-used across multiple systems. - - -### Dealing with conflicting .nix files - -If you have multiple flakes throughout your computer, you can run into a situation where one might install some software, and the other might install a different software -- but uninstall what was in the other configuration. - -For example; suppose you have one configuration file that includes this list of apps: - -```nix - environment = { - systemPackages = [ - pkgs.bat - pkgs.nginx - pkgs.mysql84 - ]; -``` - -And you run System Manager, which installs the three apps. - -Then you separately in another folder have another flake with a different configuration and set of apps: - -```nix - environment = { - systemPackages = [ - pkgs.hello - pkgs.postgresql_18 - pkgs.vscode - ]; -``` - -Then in this folder your run System Manager. - -System Manager does not track files, and see this as a changed configuration: - -* The configuration **no longer has** bat, nginx, and mysql84. -* The configuration does have hello, postgresql_18, and vscode. - -The end result is that System Manager will **remove** bat, nginx, and mysql84, and install hello, postgresql_18, and vscode. - -The fix to this problem is to instead have a single main flake.nix file, which loads all of the different .nix files, allowing you to run System Manager from a single location. - -This is because Nix has the ability to merge together objects in separate files into a single object; the above would then merge into: - -```nix - systemPackages = [ - pkgs.bat - pkgs.nginx - pkgs.mysql84 - pkgs.hello - pkgs.postgresql_18 - pkgs.vscode - ]; -``` - -We describe this technique in [Building System Manager .nix Files](#building-system-manager-nix-files). - -# Letting System Manager manage `/etc/nix/nix.conf` - -System Manager can optionally manage your `/etc/nix/nix.conf` file for you. - -If you have an existing `/etc/nix/nix.conf` file, you'll need to delete it if you want System Manager to manage the file; then run System Manager again. From that point on System Manager will manage the file for you, and you should not make changes to it. - -Instead, you'll put the changes in one of your .nix files you'll be building to configure System Manager. - -# Recommended Workflow for Starting Out - -As described previously, System Manager wants to manage your /etc/nix.conf file for you, after which you can instead place your configurations directly in the flake.nix file, including specifying experimental features. - -To do so requires a careful set of steps. Follow these steps precisely when starting out with a fresh system. - -!!! Note - We will first run System Manager to create an initial flake.nix and system.nix file; we will then delete the `/etc/nix/nix.conf` file, and instead add the flags to the flake.nix file. Then we will run System Manager again to start managing your system, inluding the `/etc/nix/nix.conf` file. - -1. Temporarily run Run system manager init with experimental features enabled by including the following line in /etc/nix/nix.conf; this way `init` will generate a `flake.nix` file: - -``` -experimental-features = nix-command flakes -``` - -And then running System Manager with the init subcommand: - -``` -nix run 'github:numtide/system-manager' -- init -``` - -(For this step, do not simply add the flag for experimental features; otherwise init won't create the flake.nix file.) - -2. Under ~/.config/system-manager, edit the `flake.nix` file, replacing this line: - -``` - modules = [ ./system.nix ]; -``` - -with this: - -```nix -modules = [ - { - nix.settings.experimental-features = "nix-command flakes"; - } - ./system.nix -]; -``` - -3. Delete the /etc/nix.conf file, optionally backing it up first: - -``` -sudo cp /etc/nix/nix.conf /etc/nix/nix_old # optional -sudo rm /etc/nix/nix.conf -``` - -4. Run System Manager to initialize your system, with the experimental flags set this one time in the command-line: - -[Need updated --sudo command] -``` -cd ~/.config/system-manager -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo -``` - -System Manager is now managing your system for you, including the /etc/nix/nix.conf file. And experimental features are required and turned on through the flake.nix file, meaning you do not need to include the --extra-experimental-features flag when you run System Manager: - -``` -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -Next, if you want to make sure experimental features are always on, you can add it to your flake. - -[TODO: Another example here] - -# Using System Manager in a non-Interactive Setting - -If you're running System Manager in a non-interative script, you might run into a problem with the four questions presented when you first run it: - -* Do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.numtide.com' (y/N)? - -* Do you want to permanently mark this value as trusted (y/N)? - -* Do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=' (y/N)? - -* Do you want to permanently mark this value as trusted (y/N)? - -The reason for these questions is Numtide has made pre-built binary versions of System Manager available from our cache, which speeds up performance since your system doesn't have to build System Manager from source. However, this triggers Nix to ask these four questions. You'll most likely want to answer "y" to all four. - -But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the --accept-flake-config option like so: - -``` -nix run 'github:numtide/system-manager' --accept-flake-config --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo -``` - -If you like, you can add these settings into your flake file, such as in the following: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nix.settings.experimental-features = "nix-command flakes"; - nix.settings.extra-substituters = https://cache.numtide.com; - nix.settings.extra-trusted-public-keys = niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=; - } - ./glow.nix - ]; - }; - }; -} -``` - -Remember, however, the flake shows what the system looks like *after* System Manager runs. That means these changes won't affect the first run of System Manager, which in this case is likely through a script. As such, the first time you run System Manager, you'll still need the `--accept-flake-config` flag. Then on subsequent runs you don't need the `--accept-flake-config flag`. - -# Recommended Workflow if You Already Have Your Nix Files - -If you already have your .nix files, you don't need to run the init subcommand. Instead, we recommend the following if you're starting out on a clean system: - -1. Remove the /etc/nix/nix.conf file. Then, when you run your System Manager the first time, System Manager will take control managing this file for you. You can then place any configuration you previously had in the /etc/etc/nix.conf file in your .nix files. - -2. Run System Manager the first time, and you'll be ready to go. - -As an example, here's a starting nix.flake file: - -**flake.nix** -``` -{ - description = "Standalone System Manager configuration"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nix.settings.experimental-features = "nix-command flakes"; - } - ./glow.nix - ]; - }; - }; -} -``` - -Notice that we've included in the modules list an object that sets experimental features, turning on flakes. - -Now here's the glow.nix file referenced above; it simply installs the `glow` command, which is for displaying markdown files in a shell: - -**glow.nix** - -```nix -{ pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - environment.systemPackages = with pkgs; [ - glow - ]; - }; -} -``` - -go ahead and delete /etc/nix/nix.conf: - -``` -sudo rm /etc/nix/nix.conf -``` - -And now run System Manager. Because you removed nix.conf, you'll need to turn on experimental features as a command-line option. - -``` -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo -``` - -After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can sipmly run: - -``` -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - - -# 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`, and `system.nix` - -Note that we'll be using the files generated by the 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: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ ./system.nix ]; - - # Optionally specify extraSpecialArgs and overlays - }; - }; -} -``` - -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 as nixpkgs.hostPlatform = "x86_64-linux"; -* `environment`, consisting of - * systemPackages - * etc -* `systemd.services` -* `systemd.tmpfiles` - -For example, you could then replace the - -``` -modules = [ ./system.nix ]; -``` - -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: - -```nix -modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - } - ./modules/tree.nix - ./modules/bat.nix -]; -``` - -Then here are the individual "recipe" files. - -**modules/bat.nix** - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.bat - ]; - }; - }; -} -``` - -**modules/tree.nix** - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.tree - ]; - }; - }; -} -``` - -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](#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](#the-main-flakenix-file). - - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - systemd.services.say-hello = { - description = "say-hello"; - enable = true; - wantedBy = [ "system-manager.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - script = '' - ${lib.getBin pkgs.hello}/bin/hello - ''; - }; - }; -} -``` - -Note: - -This line is required in the above example: - -``` -wantedBy = [ "system-manager.target" ]; -``` - -(There are other options for wantedBy; we discuss it in full in our Reference Guide under [Specifying WantedBy Setting](./reference-guide.md#specifying-the-wantedby-setting)) - -Activate it using the same nix command as earlier: - -``` -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -This will create a system service called `say-hello` (the name comes from the line `config.systemd.services.say-hello`) in a unit file at `/etc/systemd/system/say-hello.service` with the following inside it: - -``` -[Unit] -Description=say-hello - -[Service] -Environment="PATH=/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/bin:/nix/store/qqvfnxa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/bin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/bin: -/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/bin:/nix/store/g48529av5z0vcsyl4d2wbh9kl58c7p73-systemd-minimal-258/bin:/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/sbin:/nix/store/qqvfn -xa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/sbin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/sbin:/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/sbin:/nix/store/g48529av5z0vcsyl4d2wbh9 -kl58c7p73-systemd-minimal-258/sbin" -ExecStart=/nix/store/d8rjglbhinylg8v6s780byaa60k6jpz1-unit-script-say-hello-start/bin/say-hello-start -RemainAfterExit=true -Type=oneshot - -[Install] -WantedBy=system-manager.target -``` - -!!! 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: - -``` -journalctl -n 20 -``` - -and you can find the following output in it: - -``` -Nov 18 12:12:51 my-ubuntu systemd[1]: Starting say-hello.service - say-hello... -Nov 18 12:12:51 my-ubuntu say-hello-start[3488278]: Hello, world! -Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. -``` - -!!! Note - If you remove the `./apps.nix` line from the `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: - -``` -wantedBy = [ "system-manager.target" ]; -``` - -(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: - -``` -wantedBy = [ "timers.target" ] -``` - -# 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: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ - ./apps.nix - ]; - }; - }; -} -``` - -Notice this flake references a file called apps.nix. In that file we'll add to the systemPackages attribute set. Here's the apps.nix file: - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.hello - pkgs.bat - ]; - }; - }; -} -``` - -When you run System Manager, you should have the packages called `hello` and `bat` available. - -``` -$ which hello -/run/system-manager/sw/bin//hello -$ which bat -/run/system-manager/sw/bin//bat -``` - -!!! Note - The first time you install an app through System Manager, System Manager will add a file inside `/etc/profile.d`. This file adds on the `/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: - -```nix -{ - description = "Standalone System Manager configuration"; - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - ({ lib, pkgs, ... }: { - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - environment.systemPackages = [ - pkgs.hello - pkgs.bat - ]; - }; - }) - ]; - }; - }; -} -``` - -# 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: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - ./files1.nix - ]; - }; - }; -} -``` - -Notice this references a file called `files1.nix`. To create files, you add attributes to the config.environment.etc attribute set as follows: - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - etc = { - "test/test2/something.txt" = { - text = '' - This is just a test!! - ''; - mode = "0755"; - user = "ubuntu"; - group = "ubuntu"; - }; - }; - }; - }; -} -``` - -This creates a single file inside the folder `/etc/test/test2/` and the file is called `something.txt`. - -After running the above with System Manager, you can verify the file exists: - -``` -$ cat /etc/test/test2/something.txt -This is just a test!! -``` - -Note that if you prefer, you can combine the above flake and separate .nix file into a single flake like so: - -```nix -{ - description = "Standalone System Manager configuration"; - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - config.nixpkgs.hostPlatform = "x86_64-linux"; - config.environment.etc."test/test2/something.txt" = { - text = '' - This is just a test!!! - ''; - mode = "0755"; - user = "ubuntu"; - group = "ubuntu"; - }; - } - ]; - }; - }; -} -``` - -## 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`): - -``` -with_ownership = { - text = '' - This is just a test! - ''; - mode = "0755"; - uid = 5; - gid = 6; -}; -``` - -And here's an example that uses named user and group (notice we set `user` and `group`): - -``` -with_ownership2 = { - text = '' - This is just a test! - ''; - mode = "0755"; - user = "nobody"; - group = "users"; -}; -``` - -!!! Tip - This use of `uid`/`gid` for 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: - -```nix - # Configure systemd tmpfile settings - systemd.tmpfiles = { - rules = [ - "D /var/tmp/system-manager 0755 root root -" - ]; - - settings.sample = { - "/var/tmp/sample".d = { - mode = "0755"; - }; - }; - }; -``` - -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 aged-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. - -# Working with remote flakes - -Instead of saving your System Manager configuration files locally, you can optionally keep them in a remote Git repository, such as on GitHub. - -!!! Note - This is a great option if you plan to use the files on multiple machines. - -In order to store them on a remote repo, it's imperative that you keep your flake.lock file up to date. - -## What's a flake.lock file? - -A flake.lock file is a JSON file that stores the exact versions of all the inputs your flake file depends on, including things like nixpkgs, System Manager itself, and anything else you might import. Instead of pulling the latest version every time you build, the lock file ensures that the same inputs are used consistently across machines and over time. This makes your configuration reproducible, stable, and rollback-friendly. When you do want to update to new versions, you run a command like nix flake update, which refreshes the lock file in a controlled way. - -## Setting up your project for remote hosting - -As you create your flake.nix and set up any supporting files, you'll want to test it out thoroughly before pushing it up to a remote repo. - -For this you have a couple options; one is to test it out on the machine you're currently using. However, we recommend against this, as there might be artifacts on your computer that can interfere with the configuration. - -Instead, we recommend starting with a fresh machine. One option is to spin up an EC2 instance on AWS; another is to open up a Virtual Box session on your computer. - -!!! Important - You'll need to ensure you have at least 16GB of disk space on the virtual machine. If you go with 8GB, you're going to run out of space. - -After starting with a fresh machine, install Nix, copy over your flake.nix and supporting files, and test it out. Once you're ready, make sure your flake.lock file is up to date. You can create or update the flake.nix file by typing: - -``` -nix flake update -``` - -And make sure you've pushed it up to the repo. (If you don't do this step, nix will try to build a flake.lock, but will be unable to write it to the same location as the other files, and will error out.) - -[todo: Let's create a repo under numtide for this instead of using mine --jeffrey] - -```b -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/frecklefacelabs/system-manager-test#default --sudo -``` - -### When should you update your flake.nix file? - -Generally, you only need to update your flake.nix file when you want newer versions of your inputs (nixpkgs, etc). Updating isn't necessary for daily use; your configuration will continue to work with the locked versions. But you will want to update your flake.lock file in cases such as: - -* You want newer package versions (e.g. newer `btop`, etc.) -* You want security patches -* You've added new imputs to your flakes (in which case you'll be required to update `flake.lock`) -* You're preparing a fresh install and decide this is a good time to upgrade everything - -### Can't System Manager build flake.lock for me? - -Yes, but only if the flake.nix file is local to your machine. The problem is System Manager will try to write a flake.lock file in the same location as the flake.nix file, which isn't possible (at this time) with a GitHub repo. - - - -### Ensuring success - -In order to ensure System Manager retrieves the correct .nix files from your repo, we recommend including either a branch or a tag along with your repo. - - - -## Running System Manager with a remote flake - -!!! Tip - Before you run this command, we recommend that you nevertheless create a folder to run it from, such as ~/.config/system-manager. - - -# Using Blueprint with System Manager - -Blueprint is an opinionated library that maps a standard folder structure to flake outputs, allowing you to divide up your flake into individual files across these folders. This allows you to modularize and isolate these files so that they can be maintained individually and even shared across multiple projects. - -Blueprint has bulit-in support for System Manager, which means: - -* You do not need to call system-manager.lib.makeSystemConfig; Blueprint calls this for you -* You must follow Blueprint's folder structure by placing your files under the hosts folder, and you must name your files `system-configuration.nix`. -* You can have multiple folders under the `hosts` folder (but one level deep), and you can access these using the standard nix specifier, e.g. `.#folder-name.` - -In this section we should you how to use Blueprint with System Manager. - -Blueprint provides its own initialization that you can start with if you don't already have a flake.nix file using Blueprint. The command to type is: - -``` -nix flake init -t github:numtide/blueprint -``` - -This results in the following flake: - -```nix -{ - description = "Simple flake with a devshell"; - - # Add all your dependencies here - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; - blueprint.url = "github:numtide/blueprint"; - blueprint.inputs.nixpkgs.follows = "nixpkgs"; - }; - - # Load the blueprint - outputs = inputs: inputs.blueprint { inherit inputs; }; -} -``` - -Now add System Manager to its inputs section: - -``` - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; -``` - -Next, create a folder called hosts, and under that a folder called default: - -``` -mkdir hosts -cd hosts -mkdir default -cd default -``` - -Inside `default` is where you'll put your configuration file. - -**This configuration file must be named `system-configuration.nix**`. - -For example, here's a configuration file that installs `bat`: - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.bat - ]; - }; - }; -} -``` - -!!! Note - Notice that we need to include nixpkgs.hostPlatform in this file, as there's no place to include it in the parent `flake.nix` file. - -Now return to the folder two levels up (the one containing flake.nix) and you can run System Manager: - -``` -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -!!! Remember - As mentioned elsewhere, if this is the first time running System Manager on this computer, you'll need to log out and log back in to pick up the new path. - -Then you should find `bat` on your path: - -``` -$ which bat -/run/system-manager/sw/bin//bat -``` - -The default folder is called default; you can also refer to folders by name as mentioned earlier. - -If, for example, under the `hosts` folder you have a folder called `tree`, and inside `tree` create a file called `system-configuration.nix` with the following contents: - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.tree - ]; - }; - }; -} -``` - -Then you can choose to install tree by specifying the tree folder like so: - -``` -nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo -``` - -## Using multiple configuration files with Blueprint - -If you want to load multiple configuration files at once, you can create a special system-configuration.nix file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under hosts; for example, you might name it cli-tools. Starting in the folder with flake.nix: - -``` -cd hosts -mkdir cli-tools -cd cli-tools -mkdir modules -``` - -Then inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: - -``` -{ config, lib, pkgs, ... }: -{ - # Import all your modular configs - they auto-merge! ✨ - imports = [ - ./modules/tldr.nix - ./modules/cowsay.nix - ]; - - # Base configuration that applies to everything - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - }; -} -``` - -(Notice this time we can put the nixpkgs.hostPlatform in a single place. As such we won't need it in the configuration files.) - -Now move into the `modules` folder: - -``` -cd modules -``` - -And create two files here: - -tldr.nix: - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.tldr - ]; - }; - }; -} -``` - -cowsay.nix: -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.cowsay - ]; - }; - }; -} -``` - -Now you can return to the top level where you flake.nix file is and run these two configuration files: - -``` -nix run 'github:numtide/system-manager' -- switch --flake '.#cli-tools' --sudo -``` - -This means if you want to include various recipes, you can easily do so. - - -# Full Examples - -## Full Example: Installing PostgreSQL - -Here's a .nix file that installs PostgreSQL. - -Note: System Manager is still in its early state, and doesn't yet have user management, which is a planned feature that will be here soon. As such, for now, before you run this, you'll need to manually create the postgres user. Additionally, go ahead and create two directories and grant the postgres user access to them: - -```bash -# Create postgres user and group -sudo groupadd -r postgres -sudo useradd -r -g postgres -d /var/lib/postgresql -s /bin/bash postgres - -# Create directories with proper permissions -sudo mkdir -p /var/lib/postgresql -sudo chown postgres:postgres /var/lib/postgresql - -sudo mkdir -p /run/postgresql -sudo chown postgres:postgres /run/postgresql -``` - -Here, then, is the .nix file. - -```nix -{ config, lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - environment.systemPackages = with pkgs; [ - postgresql_16 - ]; - - # PostgreSQL service - systemd.services.postgresql = { - description = "PostgreSQL database server"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - - serviceConfig = { - Type = "notify"; - User = "postgres"; - Group = "postgres"; - ExecStart = "${pkgs.postgresql_16}/bin/postgres -D /var/lib/postgresql/16"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - KillMode = "mixed"; - KillSignal = "SIGINT"; - TimeoutSec = 120; - - # Create directories and initialize database - ExecStartPre = [ - "${pkgs.coreutils}/bin/mkdir -p /var/lib/postgresql/16" - "${pkgs.bash}/bin/bash -c 'if [ ! -d /var/lib/postgresql/16/base ]; then ${pkgs.postgresql_16}/bin/initdb -D /var/lib/postgresql/16; fi'" - ]; - }; - - environment = { - PGDATA = "/var/lib/postgresql/16"; - }; - }; - - # Initialize database and user - systemd.services.postgresql-init = { - description = "Initialize PostgreSQL database for myapp"; - after = [ "postgresql.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - User = "postgres"; - }; - script = '' - # Wait for PostgreSQL to be ready - until ${pkgs.postgresql_16}/bin/pg_isready; do - echo "Waiting for PostgreSQL..." - sleep 2 - done - - # Optional: Create database if it doesn't exist - ${pkgs.postgresql_16}/bin/psql -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw myapp || \ - ${pkgs.postgresql_16}/bin/createdb myapp - - # Optional: Create user if it doesn't exist - ${pkgs.postgresql_16}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='myapp'" | ${pkgs.gnugrep}/bin/grep -q 1 || \ - ${pkgs.postgresql_16}/bin/createuser myapp - - # Grant database privileges - ${pkgs.postgresql_16}/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp" - - # Grant schema privileges (allows creating tables!) - ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON SCHEMA public TO myapp" - ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL TABLES IN SCHEMA public TO myapp" - ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO myapp" - - echo "PostgreSQL is ready and configured!" - ''; - }; - }; -} -``` - -## Full Example: Installing Nginx - -Here's a .nix file that installs and configures nginx as a system service. Note that this version only supports HTTP and not HTTPS; later we provide an example that includes HTTPS. - -!!! Tip - This is simply an example to help you learn how to use System Manager. The usual way to install nginx under Nix is to use the [nginx package](https://search.nixos.org/packages?channel=25.11&show=nginx&query=nginx). - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Enable and configure services - services = { - nginx.enable = true; - }; - - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.hello - pkgs.mariadb - pkgs.nginx - ]; - - # Add directories and files to `/etc` and set their permissions - etc = { - "nginx/nginx.conf"= { - - user = "root"; - group = "root"; - mode = "0644"; - - text = '' -# The user/group is often set to 'nginx' or 'www-data', -# but for a simple root-only demo, we'll keep the default. -# user nginx; -worker_processes auto; - -# NGINX looks for modules relative to the install prefix, -# but we explicitly point to the Nix store path to be safe. -error_log /var/log/nginx/error.log; -pid /run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include ${pkgs.nginx}/conf/mime.types; - default_type application/octet-stream; - - sendfile on; - keepalive_timeout 65; - - # Basic default server block - server { - listen 80; - server_name localhost; - - # Point the root directory to a standard location or a Nix store path - root ${pkgs.nginx}/html; - - location / { - index index.html; - } - - # Example log files - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - } -} - ''; - - - }; - }; - }; - - # Enable and configure systemd services - systemd.services = { - nginx = { - enable = true; - description = "A high performance web server and reverse proxy server"; - wantedBy = [ "system-manager.target" ]; - preStart = '' - mkdir -p /var/log/nginx - chown -R root:root /var/log/nginx # Ensure permissions are right for root user - ''; - serviceConfig = { - Type = "forking"; - PIDFile = "/run/nginx.pid"; - - # The main binary execution command, pointing to the Nix store path - ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; - - # The command to stop the service gracefully - ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; - - # NGINX needs to run as root to bind to port 80/443 - User = "root"; - Group = "root"; - - # Restart policy for robustness - Restart = "on-failure"; - }; - }; - }; - - - }; -} - -``` - -## Full Exapmle: Installing Nginx with for HTTPS with a Secure Certificate - -Here's an example that installs nginx. This exapmle shows places where you would copy in your own secure certificate information. - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Enable and configure services - # Commenting this out -- apparently this loads a bunch of nginx service files we don't need or want - #services = { - # nginx.enable = true; - #}; - - environment = { - systemPackages = [ - pkgs.hello - pkgs.mariadb - pkgs.nginx - ]; - - # Add SSL certificate files to /etc - etc = { - # SSL Certificate - "ssl/certs/your-domain.crt" = { - user = "root"; - group = "root"; - mode = "0644"; - # Option 1: Embed the certificate directly - text = '' ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIUXbQ2ie2/2pxLH/okEB4KEbVDqjEwDQYJKoZIhvcNAQEL... ------END CERTIFICATE----- - ''; - # Option 2: Or reference a file from your repo - # source = ./certs/your-domain.crt; - }; - - # SSL Private Key - "ssl/private/your-domain.key" = { - user = "root"; - group = "root"; - mode = "0600"; # Restrict access to private key! - # Option 1: Embed the key directly - text = '' ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5gQjZxG7rYPub.... ------END PRIVATE KEY----- - ''; - # Option 2: Or reference a file from your repo - # source = ./certs/your-domain.key; - }; - - # Optional: Certificate chain/intermediate certificates - # For this demo we're using a self-signed cert; for a real - # one, uncomment below and add your - "ssl/certs/chain.pem" = { - user = "root"; - group = "root"; - mode = "0644"; - text = '' - -----BEGIN CERTIFICATE----- -YOUR_CHAIN_CERTIFICATE_HERE... - -----END CERTIFICATE----- - ''; - #}; - - # Nginx configuration with HTTPS - "nginx/nginx.conf" = { - user = "root"; - group = "root"; - mode = "0644"; - text = '' -worker_processes auto; - -error_log /var/log/nginx/error.log; -pid /run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include ${pkgs.nginx}/conf/mime.types; - default_type application/octet-stream; - - sendfile on; - keepalive_timeout 65; - - # SSL Settings - ssl_protocols TLSv1.2 TLSv1.3; - ssl_prefer_server_ciphers on; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; - - # HTTP Server - Redirect to HTTPS - server { - listen 80; - server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; - - # Redirect all HTTP to HTTPS - return 301 https://$server_name$request_uri; - } - - # HTTPS Server - server { - listen 443 ssl; - server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; - - # SSL Certificate files - ssl_certificate /etc/ssl/certs/your-domain.crt; - ssl_certificate_key /etc/ssl/private/your-domain.key; - - # Optional: Certificate chain - # ssl_trusted_certificate /etc/ssl/certs/chain.pem; - - # Optional: Enable OCSP stapling - ssl_stapling on; - ssl_stapling_verify on; - - # Optional: Enable HSTS (HTTP Strict Transport Security) - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - - root ${pkgs.nginx}/html; - - location / { - index index.html; - } - - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - } -} - ''; - }; - }; - }; - - systemd.services = { - nginx = { - enable = true; - #description = "A high performance web server and reverse proxy server"; - wantedBy = [ "system-manager.target" ]; - preStart = '' - mkdir -p /var/log/nginx - chown -R root:root /var/log/nginx - - # Verify SSL certificate files exist - if [ ! -f /etc/ssl/certs/your-domain.crt ]; then - echo "ERROR: SSL certificate not found!" - exit 1 - fi - if [ ! -f /etc/ssl/private/your-domain.key ]; then - echo "ERROR: SSL private key not found!" - exit 1 - fi - ''; - serviceConfig = { - Type = "forking"; - PIDFile = "/run/nginx.pid"; - ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; - ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; - User = "root"; - Group = "root"; - Restart = "on-failure"; - }; - }; - }; - }; -} - -``` - -## Full Example: Managing a System that runs Custom Software - -Here's an example where you might have custom web software living in a repository and you want to run the software on a system behind nginx. - -## Live example - -We have a complete example live that you can try out. All you need is a fresh server (such as on Amazon EC2) with at least 16GB memory. (We recommend the latest Ubuntu, with a t3Large instance, with 16GB RAM. Then allow SSH, HTTP traffic, and HTTPS traffic if you plan to build on these examples.) We have two repos: - -1. The sample application - -2. The configuration files - -The configuration files install both nginx and the sample app. - -After you spin up an instance, install nix for all users: - -``` -sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon -``` - -Next, log out and log back in so that nix is available in the system path. - -And then you can run System Manager and deploy the app with one command: - -``` -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake github:frecklefacelabs/system-manager-custom-app-deploy/v1.0.0#default --sudo -``` - -(Remember, the first time System Manager runs, it takes up to five minutes or so to compile everything.) - -!!! Tip - We're specifying a tag in our URL. This is good practice to make sure you get the right version of your flakes. Also, modern Nix supports the use of a protocol called "github", and when you use that protocol, you can specify the tag behind a slash symbol, as we did here for tag v1.0.0. - -!!! Tip - If you make changes to your flakes, be sure to create a new tag. Without it, Nix sometimes refuses to load the "latest version" of the repo, and will insist on using whatever version of your repo it used first. - -Then, the app should be installed, with nginx sitting in front of it, and you should be able to run: - -``` -curl localhost -``` -And it will print out a friendly JSON message such as: - -``` -{"message":"Welcome to the Bun API!","status":"running","endpoints":["/","/health","/random","/cowsay"]} -``` - -We even included cowsay in this sample, which you can try at `curl localhost/cowsay`. Now even though cowsay is meant for fun, the primary reason is this is a TypeScript app that uses `bun`, and we wanted to demonstrate how easy it is to include `npm` libraries. `bun` includes a feature whereby it will install dependency packages from `package.json` automatically the first time it runs, greatly simplifying the setup. - -One thing about the .nix files in this repo is that they in turn pull code (our TypeScript app) from another remote repo. Using this approach, you can separate concerns, placing the deployment .nix files in one repo, and the source app in a separate repo. - -Here are further details on the individual nix files. - -First we have a flake much like the usual starting point: - -```nix -# flake.nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ - - { - nix.settings.experimental-features = "nix-command flakes"; - services.myapp.enable = true; - } - ./system.nix - ./nginx.nix - ./bun-app.nix - ]; - - # Optionally specify extraSpecialArgs and overlays - }; - }; -} -``` - -Next is the .nix configuration that installs and configures nginx. This is a simple ngnix configuration, as it simply routes incoming HTTP traffic directly to the app: - -``` -# nginx.nix -{ config, lib, pkgs, ... }: -{ - config = { - services.nginx = { - enable = true; - - recommendedGzipSettings = true; - recommendedOptimisation = true; - recommendedProxySettings = true; - recommendedTlsSettings = true; - - virtualHosts."_" = { - default = true; - - locations."/" = { - proxyPass = "http://127.0.0.1:3000"; - extraConfig = '' - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - ''; - }; - - locations."/health" = { - proxyPass = "http://127.0.0.1:3000/health"; - extraConfig = '' - access_log off; - ''; - }; - }; - }; - }; -} -``` - -Next, here's the .nix configuration that creates a service that runs the app. - -```nix -# bun-app.nix -{ config, lib, pkgs, ... }: -let - # Fetch the app from GitHub - appSource = pkgs.fetchFromGitHub { - owner = "frecklefacelabs"; - repo = "typescript_app_for_system_manager"; - rev = "v1.0.0"; # Use a tag - sha256 = "sha256-TWt/Y2B7cGxjB9pxMOApt83P29uiCBv5nVT3KyycYEA="; - }; -in -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Install Bun - environment.systemPackages = with pkgs; [ - bun - ]; - - # Simple systemd service - runs Bun directly from Nix store! - systemd.services.bunapp = { - description = "Bun TypeScript Application"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Type = "simple"; - User = "ubuntu"; - Group = "ubuntu"; - WorkingDirectory = "${appSource}"; - # Bun will auto-install dependencies from package.json on first run - ExecStart = "${pkgs.bun}/bin/bun run index.ts"; - Restart = "always"; - RestartSec = "10s"; - }; - - environment = { - NODE_ENV = "production"; - }; - }; - }; -} - -``` - -And finally, here's the `index.ts` file; it's just a simple REST app that also makes use of one third-party `npm` library. - -``` -import cowsay from "cowsay"; - -const messages = [ - "Hello from System Manager!", - "Bun is blazingly fast! ?", - "Nix + Bun = Easy deployments", - "Making it happen!", - "Nix rocks!" -]; - -const server = Bun.serve({ - port: 3000, - fetch(req) { - const url = new URL(req.url); - - if (url.pathname === "/") { - return new Response(JSON.stringify({ - message: "Welcome to the Bun API!", - status: "running", - endpoints: ["/", "/health", "/random", "/cowsay"] - }), { - headers: { "Content-Type": "application/json" } - }); - } - - if (url.pathname === "/health") { - return new Response(JSON.stringify({ - status: "healthy" - }), { - headers: { "Content-Type": "application/json" } - }); - } - - if (url.pathname === "/random") { - const randomMessage = messages[Math.floor(Math.random() * messages.length)]; - return new Response(JSON.stringify({ - message: randomMessage, - timestamp: new Date().toISOString() - }), { - headers: { "Content-Type": "application/json" } - }); - } - - if (url.pathname === "/cowsay") { - const cow = cowsay.say({ - text: "Deployed with System Manager and Nix!" - }); - return new Response(cow, { - headers: { "Content-Type": "text/plain" } - }); - } - - return new Response("Not Found", { status: 404 }); - }, -}); - -console.log(`? Server running on http://localhost:${server.port}`); -``` - - -# Optional: Installing System Manager Locally - -Nix allows you to run code that's stored remotely in a repo, such as in GitHub. As such, you don't have to install System Manager locally to use it. However, if you want to install locally, you can do so with the following `nix profile` command. - -``` -nix profile add 'github:numtide/system-manager' -``` - -Or, if you don't have the optional features set in `/opt/nix/nix.conf`, you can provide them through the command line: - -``` -nix profile add 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -``` - -!!! Tip - After System Manager is installed locally, you no longer need to worry about whether you have experimental features installed. You will simply pass the --flake option to System Manager. - -When you install System Manager, you might get some warnings about trusted user; this simply means you're not in the trusted user list of nix. But System Manager will still install and work fine. - -Then you can find System Manager: - -``` -$ which system-manager -/home/ubuntu/.nix-profile/bin/system-manager -``` - -And you can run System Manager: - -``` -system-manager switch --flake . --sudo -``` - - -!!! Tip - System Manager is still in an early state and undergoing active development. Installing locally will not immediately pick up new changes. If you decide to install locally, you'll want to periodically check our GitHub repo for changes, and re-install it if necessary by using `nix profile upgrade`. - - -# More stuff, possibly: - -Inspecting /var/lib/system-manager/state/system-manager-state.json - -Troubleshooting Guide - -Recipes (individual software packages, etc.) - -Package overlays - -Managing a Remote System with System Manager - -Working with Timers - -Managing Users - +# Reference Guide + +[PLEASE NOTE AS YOU EDIT THIS: I KNOW I MISSED BACKTICKS IN A LOT OF PLACES. I'LL GO THROUGH IT CAREFULLY AND ADD THEM ALL IN, SO DON'T WORRY ABOUT ADDING COMMENTS ABOUT THEM.] + +To get the most out of System Manager, we're offering this guide to help you make the best decisions based on your particular situation. + +# Table of Contents + +- [System Requirements](#system-requirements) +- [Installation](#installation) + - [Regarding Experimental Features](#regarding-experimental-features) + - [Running under sudo](#running-under-sudo) + - [Command Line Usage](#command-line-usage) +- [Command-line Options](#command-line-options) + - [init](#init) + - [switch](#switch) + - [register](#register) + - [build](#build) + - [deactivate](#deactivate) + - [pre-populate](#pre-populate) + - [sudo](#sudo) +- [Setting up a folder and file structure](#setting-up-a-folder-and-file-structure) + - [Deciding on a folder structure](#deciding-on-a-folder-structure) + - [Choosing a location](#choosing-a-location) + - [Choosing a file structure](#choosing-a-file-structure) + - [Dealing with conflicting .nix files](#dealing-with-conflicting-nix-files) +- [Letting System Manager manage `/etc/nix/nix.conf`](#letting-system-manager-manage-etcnixnixconf) +- [Recommended Workflow for Starting Out](#recommended-workflow-for-starting-out) +- [Using System Manager in a non-Interactive Setting](#using-system-manager-in-a-non-interactive-setting) +- [Recommended Workflow if You Already Have Your Nix Files](#recommended-workflow-if-you-already-have-your-nix-files) +- [Building system-manager .nix files](#building-system-manager-nix-files) + - [The Main flake.nix File](#the-main-flakenix-file) +- [Managing System Services](#managing-system-services) + - [Specifying the wantedBy Setting](#specifying-the-wantedby-setting) +- [Managing Software Installations](#managing-software-installations) + - [Example: Installing a couple apps](#example-installing-a-couple-apps) +- [Working With /etc Files Declaratively](#working-with-etc-files-declaratively) + - [Example: Creating a file in /etc](#example-creating-a-file-in-etc) + - [Permissions](#permissions) + - [Users and Groups](#users-and-groups) +- [Supporting System Services with tmp files and folders](#supporting-system-services-with-tmp-files-and-folders) +- [Working with remote flakes](#working-with-remote-flakes) + - [What's a flake.lock file?](#whats-a-flakelock-file) + - [Setting up your project for remote hosting](#setting-up-your-project-for-remote-hosting) + - [When should you update your flake.nix file?](#when-should-you-update-your-flakenix-file) + - [Can't System Manager build flake.lock for me?](#cant-system-manager-build-flakelock-for-me) + - [Ensuring success](#ensuring-success) + - [Running System Manager with a remote flake](#running-system-manager-with-a-remote-flake) +- [Using Blueprint with System Manager](#using-blueprint-with-system-manager) + - [Using multiple configuration files with Blueprint](#using-multiple-configuration-files-with-blueprint) +- [Full Examples](#full-examples) + - [Full Example: Installing PostgreSQL](#full-example-installing-postgresql) + - [Full Example: Installing Nginx](#full-example-installing-nginx) + - [Full Example: Installing Nginx for HTTPS with a Secure Certificate](#full-example-installing-nginx-for-https-with-a-secure-certificate) + - [Full Example: Managing a System that runs Custom Software](#full-example-managing-a-system-that-runs-custom-software) + - [Live example](#live-example) +- [Optional: Installing System Manager Locally](#optional-installing-system-manager-locally) + +- FAQ (Maybe put in its own document) + +# Command Line Usage + +The basic command looks like this: + +```nix +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +This is the most common scenario you'll use. +## Command-line Options + +### init + +This subcommand creates two initial files for use with system manager, a fully-functional flake.nix, and a system.nix file that contains skeleton code. + +#### Command line options + +**path:** The path where to create the files. If the path doesn't exist, it will be created. + +#### Example + +``` +nix run 'github:numtide/system-manager' -- init +``` + +This will create the initial files in `~/.config/system-manager`. + +``` +nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-manager' +``` + +This will create the initial files in `/home/ubuntu/system-manager`. + +!!! Note + Presently, System Manager requires Flakes to be active. If you choose to not include the experimental features line in /etc/nix/nix.conf (and instead use the experimental features command line option), then init will only create a system.nix file, rather than both a flake.nix file and system.nix file. + +### switch + +The `switch` subcommand builds and activates your configuration immediately, making it both the current running configuration and the default for future boots. Use it whenever you want to apply your changes. + +**Note: Rollbacks are not yet implemented.** + +The following two parameters are currently both required: + +**--flake**: Specifies a flake to use for configuration. + +**--sudo**: Specifies that System Manager can use sudo. + +### register + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `register` subcommand builds and registers a System Manager configuration, but does not activate it. Compare this to `switch`, which does everything register does, but then activates it. + +### build + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `build` subcommand builds everything needed for a switch, but does not register it. + +### deactivate + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `deactivate` deactivates System Manager. + +### pre-populate + +[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] + +The `prepopulate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. + +### sudo + +The sudo subcommand grants sudo access to System Manager, while running under the current user. All created files with be owned by the current user. + +# Setting up a folder and file structure + +Before you begin with System Manager, you'll need to decide on your folder structure. + +!!! Note + If you prefer, you can host all your System Manager configuration files on a remote Git repo (such as GitHub), and then you don't need to worry about where on your computer to store the files. For more info, see [Working with Remote Flakes](#working-with-remote-flakes). + +Technically, you are free to set up your folders and files however you like; System Manager does not enforce any rules, thus allowing you full flexibility. Below are simply some options that we recommend. + +!!! Tip + While you are free to have different system manager .nix files scattered throughout your system, we recommend, if possible, keeping them in a single location simply for organizational purposes. But again, this is just a recommendation and you're not bound by any such rules. + +## Deciding on a folder structure + +You’ll need to choose where your System Manager configuration will live. Here are two main organizational patterns we recommend. + +* **Option A**: A single folder for all your configuration + +A single folder keeps everything together. This offers a clean long-term solution, along with easy version control. It's also convenient for replicating between machines. + +* **Option B**: A separate folder for each use case + +While not as common, it’s entirely possible to organize your System Manager configuration into multiple independent folders, each focused on a specific use case. In this model, you treat each configuration as its own standalone unit, often stored in its own Git repository. + +For example, you might keep: + +* a dedicated configuration folder strictly for managing nginx, + +* another for custom systemd services, + +* another for developer tools, + +* and yet another for monitoring and observability packages. + +In this manner, you can then build up a system by picking and choosing which services you need for a particular machine, and pull each one down from GitHub. + +To make this happen, however, requires careful consideration [as we discuss later](#dealing-with-conflicting-nix-files). + +## Choosing a location + +### Option 1: Your personal ~/.config folder + +If you're managing a system yourself and only you will be using it, one possibility is to put the files in ~/.config/system-manager. + +This approach keeps everything scoped to you and avoids having to place files under /etc and, perhaps most importantly, avoids have to use sudo. Here's an example layout: + +``` +~/.config/system-manager/ + flake.nix + modules/ + default.nix +``` + +!!! Tip + Avoid this location if multiple people use the machine or if this configuration is meant to be shared with a team. Home-directory paths are user-specific and may not make sense across machines. + +### Option 2: A shared /etc/system-manager folder (Recommended for multi-user or organizational setups) + +If you are: + +* managing multiple machines, + +* part of a team, + +* deploying configurations in a corporate or server environment, + +* or simply want a clear system-level location, + +then /etc/system-manager is a great choice. Among the advantages are consistency across all machines; standard within an organization; and treating system manager as a system-level tool rather than a personal configuration. Here's an example layout: + +``` +/etc/system-manager/ + flake.nix + modules/ + default.nix + services.nix +``` + +## Choosing a file structure + +After choosing where your configuration lives, you must decide how to structure the files inside it. And note that while system-manager does not enforce any rules, we do recommend you maintain consistency, especially if you have multiple locations on your computer where you store system manager .nix files. + +Essentially, you have two options: + +* A single flake.nix file + +* A reusable flake.nix file with one or more separate configuration files that describe what the system will look like. + +Within Option B, you can also use our open-source Blueprint product to help you manage your files, which we'll cover shortly. + +### Option A: Single flake.nix file + +This configuration is ideal for: + +* Small, simple setups + +* Demos and experiments + +* One-off configurations + +Drawback: This approach doesn’t scale well once you need multiple services, multiple hosts, or reusable modules. + +### Option B: Flake file with one or more configuration files + +This is the structure used by most production setups and by NixOS itself. Your arrangement might look like: + +``` +system-manager/ + flake.nix + modules/ + default.nix + services.nix + users.nix +``` + +Or, perhaps you might have separate services, one per file: + +``` +system-manager/ + flake.nix + modules/ + default.nix + service-1.nix + service-2.nix + users.nix +``` + +This also lends itself well to having multiple "recipes". For example, you might want to add nginx and postgres to your system. You might have them preconfigured somewhere, and simply "drop" them in like so: + +``` +system-manager/ + flake.nix + modules/ + default.nix + service-1.nix + service-2.nix + users.nix + nginx.nix + postgres.nix +``` + +!!! Tip + This is the approach we use in our examples in this document. That way each isolated "recipe" is repeatable and can be re-used across multiple systems. + + +### Dealing with conflicting .nix files + +If you have multiple flakes throughout your computer, you can run into a situation where one might install some software, and the other might install a different software -- but uninstall what was in the other configuration. + +For example; suppose you have one configuration file that includes this list of apps: + +```nix + environment = { + systemPackages = [ + pkgs.bat + pkgs.nginx + pkgs.mysql84 + ]; +``` + +And you run System Manager, which installs the three apps. + +Then you separately in another folder have another flake with a different configuration and set of apps: + +```nix + environment = { + systemPackages = [ + pkgs.hello + pkgs.postgresql_18 + pkgs.vscode + ]; +``` + +Then in this folder your run System Manager. + +System Manager does not track files, and see this as a changed configuration: + +* The configuration **no longer has** bat, nginx, and mysql84. +* The configuration does have hello, postgresql_18, and vscode. + +The end result is that System Manager will **remove** bat, nginx, and mysql84, and install hello, postgresql_18, and vscode. + +The fix to this problem is to instead have a single main flake.nix file, which loads all of the different .nix files, allowing you to run System Manager from a single location. + +This is because Nix has the ability to merge together objects in separate files into a single object; the above would then merge into: + +```nix + systemPackages = [ + pkgs.bat + pkgs.nginx + pkgs.mysql84 + pkgs.hello + pkgs.postgresql_18 + pkgs.vscode + ]; +``` + +We describe this technique in [Building System Manager .nix Files](#building-system-manager-nix-files). + +# Letting System Manager manage `/etc/nix/nix.conf` + +System Manager can optionally manage your `/etc/nix/nix.conf` file for you. + +If you have an existing `/etc/nix/nix.conf` file, you'll need to delete it if you want System Manager to manage the file; then run System Manager again. From that point on System Manager will manage the file for you, and you should not make changes to it. + +Instead, you'll put the changes in one of your .nix files you'll be building to configure System Manager. + +# Recommended Workflow for Starting Out + +As described previously, System Manager wants to manage your /etc/nix.conf file for you, after which you can instead place your configurations directly in the flake.nix file, including specifying experimental features. + +To do so requires a careful set of steps. Follow these steps precisely when starting out with a fresh system. + +!!! Note + We will first run System Manager to create an initial flake.nix and system.nix file; we will then delete the `/etc/nix/nix.conf` file, and instead add the flags to the flake.nix file. Then we will run System Manager again to start managing your system, inluding the `/etc/nix/nix.conf` file. + +1. Temporarily run Run system manager init with experimental features enabled by including the following line in /etc/nix/nix.conf; this way `init` will generate a `flake.nix` file: + +``` +experimental-features = nix-command flakes +``` + +And then running System Manager with the init subcommand: + +``` +nix run 'github:numtide/system-manager' -- init +``` + +(For this step, do not simply add the flag for experimental features; otherwise init won't create the flake.nix file.) + +2. Under ~/.config/system-manager, edit the `flake.nix` file, replacing this line: + +``` + modules = [ ./system.nix ]; +``` + +with this: + +```nix +modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + } + ./system.nix +]; +``` + +3. Delete the /etc/nix.conf file, optionally backing it up first: + +``` +sudo cp /etc/nix/nix.conf /etc/nix/nix_old # optional +sudo rm /etc/nix/nix.conf +``` + +4. Run System Manager to initialize your system, with the experimental flags set this one time in the command-line: + +[Need updated --sudo command] +``` +cd ~/.config/system-manager +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +System Manager is now managing your system for you, including the /etc/nix/nix.conf file. And experimental features are required and turned on through the flake.nix file, meaning you do not need to include the --extra-experimental-features flag when you run System Manager: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +Next, if you want to make sure experimental features are always on, you can add it to your flake. + +[TODO: Another example here] + +# Using System Manager in a non-Interactive Setting + +If you're running System Manager in a non-interative script, you might run into a problem with the four questions presented when you first run it: + +* Do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.numtide.com' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + +* Do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + +The reason for these questions is Numtide has made pre-built binary versions of System Manager available from our cache, which speeds up performance since your system doesn't have to build System Manager from source. However, this triggers Nix to ask these four questions. You'll most likely want to answer "y" to all four. + +But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the --accept-flake-config option like so: + +``` +nix run 'github:numtide/system-manager' --accept-flake-config --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +If you like, you can add these settings into your flake file, such as in the following: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + nix.settings.extra-substituters = https://cache.numtide.com; + nix.settings.extra-trusted-public-keys = niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=; + } + ./glow.nix + ]; + }; + }; +} +``` + +Remember, however, the flake shows what the system looks like *after* System Manager runs. That means these changes won't affect the first run of System Manager, which in this case is likely through a script. As such, the first time you run System Manager, you'll still need the `--accept-flake-config` flag. Then on subsequent runs you don't need the `--accept-flake-config flag`. + +# Recommended Workflow if You Already Have Your Nix Files + +If you already have your .nix files, you don't need to run the init subcommand. Instead, we recommend the following if you're starting out on a clean system: + +1. Remove the /etc/nix/nix.conf file. Then, when you run your System Manager the first time, System Manager will take control managing this file for you. You can then place any configuration you previously had in the /etc/etc/nix.conf file in your .nix files. + +2. Run System Manager the first time, and you'll be ready to go. + +As an example, here's a starting nix.flake file: + +**flake.nix** +``` +{ + description = "Standalone System Manager configuration"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + } + ./glow.nix + ]; + }; + }; +} +``` + +Notice that we've included in the modules list an object that sets experimental features, turning on flakes. + +Now here's the glow.nix file referenced above; it simply installs the `glow` command, which is for displaying markdown files in a shell: + +**glow.nix** + +```nix +{ pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + glow + ]; + }; +} +``` + +go ahead and delete /etc/nix/nix.conf: + +``` +sudo rm /etc/nix/nix.conf +``` + +And now run System Manager. Because you removed nix.conf, you'll need to turn on experimental features as a command-line option. + +``` +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can sipmly run: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + + +# 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`, and `system.nix` + +Note that we'll be using the files generated by the 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ ./system.nix ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +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 as nixpkgs.hostPlatform = "x86_64-linux"; +* `environment`, consisting of + * systemPackages + * etc +* `systemd.services` +* `systemd.tmpfiles` + +For example, you could then replace the + +``` +modules = [ ./system.nix ]; +``` + +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: + +```nix +modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + } + ./modules/tree.nix + ./modules/bat.nix +]; +``` + +Then here are the individual "recipe" files. + +**modules/bat.nix** + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.bat + ]; + }; + }; +} +``` + +**modules/tree.nix** + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tree + ]; + }; + }; +} +``` + +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](#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](#the-main-flakenix-file). + + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + systemd.services.say-hello = { + description = "say-hello"; + enable = true; + wantedBy = [ "system-manager.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + ${lib.getBin pkgs.hello}/bin/hello + ''; + }; + }; +} +``` + +Note: + +This line is required in the above example: + +``` +wantedBy = [ "system-manager.target" ]; +``` + +(There are other options for wantedBy; we discuss it in full in our Reference Guide under [Specifying WantedBy Setting](./reference-guide.md#specifying-the-wantedby-setting)) + +Activate it using the same nix command as earlier: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +This will create a system service called `say-hello` (the name comes from the line `config.systemd.services.say-hello`) in a unit file at `/etc/systemd/system/say-hello.service` with the following inside it: + +``` +[Unit] +Description=say-hello + +[Service] +Environment="PATH=/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/bin:/nix/store/qqvfnxa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/bin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/bin: +/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/bin:/nix/store/g48529av5z0vcsyl4d2wbh9kl58c7p73-systemd-minimal-258/bin:/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/sbin:/nix/store/qqvfn +xa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/sbin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/sbin:/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/sbin:/nix/store/g48529av5z0vcsyl4d2wbh9 +kl58c7p73-systemd-minimal-258/sbin" +ExecStart=/nix/store/d8rjglbhinylg8v6s780byaa60k6jpz1-unit-script-say-hello-start/bin/say-hello-start +RemainAfterExit=true +Type=oneshot + +[Install] +WantedBy=system-manager.target +``` + +!!! 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: + +``` +journalctl -n 20 +``` + +and you can find the following output in it: + +``` +Nov 18 12:12:51 my-ubuntu systemd[1]: Starting say-hello.service - say-hello... +Nov 18 12:12:51 my-ubuntu say-hello-start[3488278]: Hello, world! +Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. +``` + +!!! Note + If you remove the `./apps.nix` line from the `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: + +``` +wantedBy = [ "system-manager.target" ]; +``` + +(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: + +``` +wantedBy = [ "timers.target" ] +``` + +# 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ + ./apps.nix + ]; + }; + }; +} +``` + +Notice this flake references a file called apps.nix. In that file we'll add to the systemPackages attribute set. Here's the apps.nix file: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.hello + pkgs.bat + ]; + }; + }; +} +``` + +When you run System Manager, you should have the packages called `hello` and `bat` available. + +``` +$ which hello +/run/system-manager/sw/bin//hello +$ which bat +/run/system-manager/sw/bin//bat +``` + +!!! Note + The first time you install an app through System Manager, System Manager will add a file inside `/etc/profile.d`. This file adds on the `/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: + +```nix +{ + description = "Standalone System Manager configuration"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + ({ lib, pkgs, ... }: { + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment.systemPackages = [ + pkgs.hello + pkgs.bat + ]; + }; + }) + ]; + }; + }; +} +``` + +# 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + ./files1.nix + ]; + }; + }; +} +``` + +Notice this references a file called `files1.nix`. To create files, you add attributes to the config.environment.etc attribute set as follows: + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + etc = { + "test/test2/something.txt" = { + text = '' + This is just a test!! + ''; + mode = "0755"; + user = "ubuntu"; + group = "ubuntu"; + }; + }; + }; + }; +} +``` + +This creates a single file inside the folder `/etc/test/test2/` and the file is called `something.txt`. + +After running the above with System Manager, you can verify the file exists: + +``` +$ cat /etc/test/test2/something.txt +This is just a test!! +``` + +Note that if you prefer, you can combine the above flake and separate .nix file into a single flake like so: + +```nix +{ + description = "Standalone System Manager configuration"; + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + config.nixpkgs.hostPlatform = "x86_64-linux"; + config.environment.etc."test/test2/something.txt" = { + text = '' + This is just a test!!! + ''; + mode = "0755"; + user = "ubuntu"; + group = "ubuntu"; + }; + } + ]; + }; + }; +} +``` + +## 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`): + +``` +with_ownership = { + text = '' + This is just a test! + ''; + mode = "0755"; + uid = 5; + gid = 6; +}; +``` + +And here's an example that uses named user and group (notice we set `user` and `group`): + +``` +with_ownership2 = { + text = '' + This is just a test! + ''; + mode = "0755"; + user = "nobody"; + group = "users"; +}; +``` + +!!! Tip + This use of `uid`/`gid` for 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: + +```nix + # Configure systemd tmpfile settings + systemd.tmpfiles = { + rules = [ + "D /var/tmp/system-manager 0755 root root -" + ]; + + settings.sample = { + "/var/tmp/sample".d = { + mode = "0755"; + }; + }; + }; +``` + +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 aged-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. + +# Working with remote flakes + +Instead of saving your System Manager configuration files locally, you can optionally keep them in a remote Git repository, such as on GitHub. + +!!! Note + This is a great option if you plan to use the files on multiple machines. + +In order to store them on a remote repo, it's imperative that you keep your flake.lock file up to date. + +## What's a flake.lock file? + +A flake.lock file is a JSON file that stores the exact versions of all the inputs your flake file depends on, including things like nixpkgs, System Manager itself, and anything else you might import. Instead of pulling the latest version every time you build, the lock file ensures that the same inputs are used consistently across machines and over time. This makes your configuration reproducible, stable, and rollback-friendly. When you do want to update to new versions, you run a command like nix flake update, which refreshes the lock file in a controlled way. + +## Setting up your project for remote hosting + +As you create your flake.nix and set up any supporting files, you'll want to test it out thoroughly before pushing it up to a remote repo. + +For this you have a couple options; one is to test it out on the machine you're currently using. However, we recommend against this, as there might be artifacts on your computer that can interfere with the configuration. + +Instead, we recommend starting with a fresh machine. One option is to spin up an EC2 instance on AWS; another is to open up a Virtual Box session on your computer. + +!!! Important + You'll need to ensure you have at least 16GB of disk space on the virtual machine. If you go with 8GB, you're going to run out of space. + +After starting with a fresh machine, install Nix, copy over your flake.nix and supporting files, and test it out. Once you're ready, make sure your flake.lock file is up to date. You can create or update the flake.nix file by typing: + +``` +nix flake update +``` + +And make sure you've pushed it up to the repo. (If you don't do this step, nix will try to build a flake.lock, but will be unable to write it to the same location as the other files, and will error out.) + +[todo: Let's create a repo under numtide for this instead of using mine --jeffrey] + +```b +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/frecklefacelabs/system-manager-test#default --sudo +``` + +### When should you update your flake.nix file? + +Generally, you only need to update your flake.nix file when you want newer versions of your inputs (nixpkgs, etc). Updating isn't necessary for daily use; your configuration will continue to work with the locked versions. But you will want to update your flake.lock file in cases such as: + +* You want newer package versions (e.g. newer `btop`, etc.) +* You want security patches +* You've added new imputs to your flakes (in which case you'll be required to update `flake.lock`) +* You're preparing a fresh install and decide this is a good time to upgrade everything + +### Can't System Manager build flake.lock for me? + +Yes, but only if the flake.nix file is local to your machine. The problem is System Manager will try to write a flake.lock file in the same location as the flake.nix file, which isn't possible (at this time) with a GitHub repo. + + + +### Ensuring success + +In order to ensure System Manager retrieves the correct .nix files from your repo, we recommend including either a branch or a tag along with your repo. + + + +## Running System Manager with a remote flake + +!!! Tip + Before you run this command, we recommend that you nevertheless create a folder to run it from, such as ~/.config/system-manager. + + +# Using Blueprint with System Manager + +Blueprint is an opinionated library that maps a standard folder structure to flake outputs, allowing you to divide up your flake into individual files across these folders. This allows you to modularize and isolate these files so that they can be maintained individually and even shared across multiple projects. + +Blueprint has bulit-in support for System Manager, which means: + +* You do not need to call system-manager.lib.makeSystemConfig; Blueprint calls this for you +* You must follow Blueprint's folder structure by placing your files under the hosts folder, and you must name your files `system-configuration.nix`. +* You can have multiple folders under the `hosts` folder (but one level deep), and you can access these using the standard nix specifier, e.g. `.#folder-name.` + +In this section we should you how to use Blueprint with System Manager. + +Blueprint provides its own initialization that you can start with if you don't already have a flake.nix file using Blueprint. The command to type is: + +``` +nix flake init -t github:numtide/blueprint +``` + +This results in the following flake: + +```nix +{ + description = "Simple flake with a devshell"; + + # Add all your dependencies here + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + blueprint.url = "github:numtide/blueprint"; + blueprint.inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Load the blueprint + outputs = inputs: inputs.blueprint { inherit inputs; }; +} +``` + +Now add System Manager to its inputs section: + +``` + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; +``` + +Next, create a folder called hosts, and under that a folder called default: + +``` +mkdir hosts +cd hosts +mkdir default +cd default +``` + +Inside `default` is where you'll put your configuration file. + +**This configuration file must be named `system-configuration.nix**`. + +For example, here's a configuration file that installs `bat`: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.bat + ]; + }; + }; +} +``` + +!!! Note + Notice that we need to include nixpkgs.hostPlatform in this file, as there's no place to include it in the parent `flake.nix` file. + +Now return to the folder two levels up (the one containing flake.nix) and you can run System Manager: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +!!! Remember + As mentioned elsewhere, if this is the first time running System Manager on this computer, you'll need to log out and log back in to pick up the new path. + +Then you should find `bat` on your path: + +``` +$ which bat +/run/system-manager/sw/bin//bat +``` + +The default folder is called default; you can also refer to folders by name as mentioned earlier. + +If, for example, under the `hosts` folder you have a folder called `tree`, and inside `tree` create a file called `system-configuration.nix` with the following contents: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tree + ]; + }; + }; +} +``` + +Then you can choose to install tree by specifying the tree folder like so: + +``` +nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo +``` + +## Using multiple configuration files with Blueprint + +If you want to load multiple configuration files at once, you can create a special system-configuration.nix file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under hosts; for example, you might name it cli-tools. Starting in the folder with flake.nix: + +``` +cd hosts +mkdir cli-tools +cd cli-tools +mkdir modules +``` + +Then inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: + +``` +{ config, lib, pkgs, ... }: +{ + # Import all your modular configs - they auto-merge! ✨ + imports = [ + ./modules/tldr.nix + ./modules/cowsay.nix + ]; + + # Base configuration that applies to everything + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + }; +} +``` + +(Notice this time we can put the nixpkgs.hostPlatform in a single place. As such we won't need it in the configuration files.) + +Now move into the `modules` folder: + +``` +cd modules +``` + +And create two files here: + +tldr.nix: + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tldr + ]; + }; + }; +} +``` + +cowsay.nix: +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.cowsay + ]; + }; + }; +} +``` + +Now you can return to the top level where you flake.nix file is and run these two configuration files: + +``` +nix run 'github:numtide/system-manager' -- switch --flake '.#cli-tools' --sudo +``` + +This means if you want to include various recipes, you can easily do so. + + +# Full Examples + +## Full Example: Installing PostgreSQL + +Here's a .nix file that installs PostgreSQL. + +Note: System Manager is still in its early state, and doesn't yet have user management, which is a planned feature that will be here soon. As such, for now, before you run this, you'll need to manually create the postgres user. Additionally, go ahead and create two directories and grant the postgres user access to them: + +```bash +# Create postgres user and group +sudo groupadd -r postgres +sudo useradd -r -g postgres -d /var/lib/postgresql -s /bin/bash postgres + +# Create directories with proper permissions +sudo mkdir -p /var/lib/postgresql +sudo chown postgres:postgres /var/lib/postgresql + +sudo mkdir -p /run/postgresql +sudo chown postgres:postgres /run/postgresql +``` + +Here, then, is the .nix file. + +```nix +{ config, lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + postgresql_16 + ]; + + # PostgreSQL service + systemd.services.postgresql = { + description = "PostgreSQL database server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "notify"; + User = "postgres"; + Group = "postgres"; + ExecStart = "${pkgs.postgresql_16}/bin/postgres -D /var/lib/postgresql/16"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + KillMode = "mixed"; + KillSignal = "SIGINT"; + TimeoutSec = 120; + + # Create directories and initialize database + ExecStartPre = [ + "${pkgs.coreutils}/bin/mkdir -p /var/lib/postgresql/16" + "${pkgs.bash}/bin/bash -c 'if [ ! -d /var/lib/postgresql/16/base ]; then ${pkgs.postgresql_16}/bin/initdb -D /var/lib/postgresql/16; fi'" + ]; + }; + + environment = { + PGDATA = "/var/lib/postgresql/16"; + }; + }; + + # Initialize database and user + systemd.services.postgresql-init = { + description = "Initialize PostgreSQL database for myapp"; + after = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "postgres"; + }; + script = '' + # Wait for PostgreSQL to be ready + until ${pkgs.postgresql_16}/bin/pg_isready; do + echo "Waiting for PostgreSQL..." + sleep 2 + done + + # Optional: Create database if it doesn't exist + ${pkgs.postgresql_16}/bin/psql -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw myapp || \ + ${pkgs.postgresql_16}/bin/createdb myapp + + # Optional: Create user if it doesn't exist + ${pkgs.postgresql_16}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='myapp'" | ${pkgs.gnugrep}/bin/grep -q 1 || \ + ${pkgs.postgresql_16}/bin/createuser myapp + + # Grant database privileges + ${pkgs.postgresql_16}/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp" + + # Grant schema privileges (allows creating tables!) + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON SCHEMA public TO myapp" + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL TABLES IN SCHEMA public TO myapp" + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO myapp" + + echo "PostgreSQL is ready and configured!" + ''; + }; + }; +} +``` + +## Full Example: Installing Nginx + +Here's a .nix file that installs and configures nginx as a system service. Note that this version only supports HTTP and not HTTPS; later we provide an example that includes HTTPS. + +!!! Tip + This is simply an example to help you learn how to use System Manager. The usual way to install nginx under Nix is to use the [nginx package](https://search.nixos.org/packages?channel=25.11&show=nginx&query=nginx). + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable and configure services + services = { + nginx.enable = true; + }; + + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.hello + pkgs.mariadb + pkgs.nginx + ]; + + # Add directories and files to `/etc` and set their permissions + etc = { + "nginx/nginx.conf"= { + + user = "root"; + group = "root"; + mode = "0644"; + + text = '' +# The user/group is often set to 'nginx' or 'www-data', +# but for a simple root-only demo, we'll keep the default. +# user nginx; +worker_processes auto; + +# NGINX looks for modules relative to the install prefix, +# but we explicitly point to the Nix store path to be safe. +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include ${pkgs.nginx}/conf/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + # Basic default server block + server { + listen 80; + server_name localhost; + + # Point the root directory to a standard location or a Nix store path + root ${pkgs.nginx}/html; + + location / { + index index.html; + } + + # Example log files + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + } +} + ''; + + + }; + }; + }; + + # Enable and configure systemd services + systemd.services = { + nginx = { + enable = true; + description = "A high performance web server and reverse proxy server"; + wantedBy = [ "system-manager.target" ]; + preStart = '' + mkdir -p /var/log/nginx + chown -R root:root /var/log/nginx # Ensure permissions are right for root user + ''; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/nginx.pid"; + + # The main binary execution command, pointing to the Nix store path + ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; + + # The command to stop the service gracefully + ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; + + # NGINX needs to run as root to bind to port 80/443 + User = "root"; + Group = "root"; + + # Restart policy for robustness + Restart = "on-failure"; + }; + }; + }; + + + }; +} + +``` + +## Full Exapmle: Installing Nginx with for HTTPS with a Secure Certificate + +Here's an example that installs nginx. This exapmle shows places where you would copy in your own secure certificate information. + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable and configure services + # Commenting this out -- apparently this loads a bunch of nginx service files we don't need or want + #services = { + # nginx.enable = true; + #}; + + environment = { + systemPackages = [ + pkgs.hello + pkgs.mariadb + pkgs.nginx + ]; + + # Add SSL certificate files to /etc + etc = { + # SSL Certificate + "ssl/certs/your-domain.crt" = { + user = "root"; + group = "root"; + mode = "0644"; + # Option 1: Embed the certificate directly + text = '' +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUXbQ2ie2/2pxLH/okEB4KEbVDqjEwDQYJKoZIhvcNAQEL... +-----END CERTIFICATE----- + ''; + # Option 2: Or reference a file from your repo + # source = ./certs/your-domain.crt; + }; + + # SSL Private Key + "ssl/private/your-domain.key" = { + user = "root"; + group = "root"; + mode = "0600"; # Restrict access to private key! + # Option 1: Embed the key directly + text = '' +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5gQjZxG7rYPub.... +-----END PRIVATE KEY----- + ''; + # Option 2: Or reference a file from your repo + # source = ./certs/your-domain.key; + }; + + # Optional: Certificate chain/intermediate certificates + # For this demo we're using a self-signed cert; for a real + # one, uncomment below and add your + "ssl/certs/chain.pem" = { + user = "root"; + group = "root"; + mode = "0644"; + text = '' + -----BEGIN CERTIFICATE----- +YOUR_CHAIN_CERTIFICATE_HERE... + -----END CERTIFICATE----- + ''; + #}; + + # Nginx configuration with HTTPS + "nginx/nginx.conf" = { + user = "root"; + group = "root"; + mode = "0644"; + text = '' +worker_processes auto; + +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include ${pkgs.nginx}/conf/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + # SSL Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; + + # HTTP Server - Redirect to HTTPS + server { + listen 80; + server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; + + # Redirect all HTTP to HTTPS + return 301 https://$server_name$request_uri; + } + + # HTTPS Server + server { + listen 443 ssl; + server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; + + # SSL Certificate files + ssl_certificate /etc/ssl/certs/your-domain.crt; + ssl_certificate_key /etc/ssl/private/your-domain.key; + + # Optional: Certificate chain + # ssl_trusted_certificate /etc/ssl/certs/chain.pem; + + # Optional: Enable OCSP stapling + ssl_stapling on; + ssl_stapling_verify on; + + # Optional: Enable HSTS (HTTP Strict Transport Security) + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + root ${pkgs.nginx}/html; + + location / { + index index.html; + } + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + } +} + ''; + }; + }; + }; + + systemd.services = { + nginx = { + enable = true; + #description = "A high performance web server and reverse proxy server"; + wantedBy = [ "system-manager.target" ]; + preStart = '' + mkdir -p /var/log/nginx + chown -R root:root /var/log/nginx + + # Verify SSL certificate files exist + if [ ! -f /etc/ssl/certs/your-domain.crt ]; then + echo "ERROR: SSL certificate not found!" + exit 1 + fi + if [ ! -f /etc/ssl/private/your-domain.key ]; then + echo "ERROR: SSL private key not found!" + exit 1 + fi + ''; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/nginx.pid"; + ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; + ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; + User = "root"; + Group = "root"; + Restart = "on-failure"; + }; + }; + }; + }; +} + +``` + +## Full Example: Managing a System that runs Custom Software + +Here's an example where you might have custom web software living in a repository and you want to run the software on a system behind nginx. + +## Live example + +We have a complete example live that you can try out. All you need is a fresh server (such as on Amazon EC2) with at least 16GB memory. (We recommend the latest Ubuntu, with a t3Large instance, with 16GB RAM. Then allow SSH, HTTP traffic, and HTTPS traffic if you plan to build on these examples.) We have two repos: + +1. The sample application + +2. The configuration files + +The configuration files install both nginx and the sample app. + +After you spin up an instance, install nix for all users: + +``` +sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon +``` + +Next, log out and log back in so that nix is available in the system path. + +And then you can run System Manager and deploy the app with one command: + +``` +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake github:frecklefacelabs/system-manager-custom-app-deploy/v1.0.0#default --sudo +``` + +(Remember, the first time System Manager runs, it takes up to five minutes or so to compile everything.) + +!!! Tip + We're specifying a tag in our URL. This is good practice to make sure you get the right version of your flakes. Also, modern Nix supports the use of a protocol called "github", and when you use that protocol, you can specify the tag behind a slash symbol, as we did here for tag v1.0.0. + +!!! Tip + If you make changes to your flakes, be sure to create a new tag. Without it, Nix sometimes refuses to load the "latest version" of the repo, and will insist on using whatever version of your repo it used first. + +Then, the app should be installed, with nginx sitting in front of it, and you should be able to run: + +``` +curl localhost +``` +And it will print out a friendly JSON message such as: + +``` +{"message":"Welcome to the Bun API!","status":"running","endpoints":["/","/health","/random","/cowsay"]} +``` + +We even included cowsay in this sample, which you can try at `curl localhost/cowsay`. Now even though cowsay is meant for fun, the primary reason is this is a TypeScript app that uses `bun`, and we wanted to demonstrate how easy it is to include `npm` libraries. `bun` includes a feature whereby it will install dependency packages from `package.json` automatically the first time it runs, greatly simplifying the setup. + +One thing about the .nix files in this repo is that they in turn pull code (our TypeScript app) from another remote repo. Using this approach, you can separate concerns, placing the deployment .nix files in one repo, and the source app in a separate repo. + +Here are further details on the individual nix files. + +First we have a flake much like the usual starting point: + +```nix +# flake.nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ + + { + nix.settings.experimental-features = "nix-command flakes"; + services.myapp.enable = true; + } + ./system.nix + ./nginx.nix + ./bun-app.nix + ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +Next is the .nix configuration that installs and configures nginx. This is a simple ngnix configuration, as it simply routes incoming HTTP traffic directly to the app: + +``` +# nginx.nix +{ config, lib, pkgs, ... }: +{ + config = { + services.nginx = { + enable = true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + virtualHosts."_" = { + default = true; + + locations."/" = { + proxyPass = "http://127.0.0.1:3000"; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + + locations."/health" = { + proxyPass = "http://127.0.0.1:3000/health"; + extraConfig = '' + access_log off; + ''; + }; + }; + }; + }; +} +``` + +Next, here's the .nix configuration that creates a service that runs the app. + +```nix +# bun-app.nix +{ config, lib, pkgs, ... }: +let + # Fetch the app from GitHub + appSource = pkgs.fetchFromGitHub { + owner = "frecklefacelabs"; + repo = "typescript_app_for_system_manager"; + rev = "v1.0.0"; # Use a tag + sha256 = "sha256-TWt/Y2B7cGxjB9pxMOApt83P29uiCBv5nVT3KyycYEA="; + }; +in +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Install Bun + environment.systemPackages = with pkgs; [ + bun + ]; + + # Simple systemd service - runs Bun directly from Nix store! + systemd.services.bunapp = { + description = "Bun TypeScript Application"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + User = "ubuntu"; + Group = "ubuntu"; + WorkingDirectory = "${appSource}"; + # Bun will auto-install dependencies from package.json on first run + ExecStart = "${pkgs.bun}/bin/bun run index.ts"; + Restart = "always"; + RestartSec = "10s"; + }; + + environment = { + NODE_ENV = "production"; + }; + }; + }; +} + +``` + +And finally, here's the `index.ts` file; it's just a simple REST app that also makes use of one third-party `npm` library. + +``` +import cowsay from "cowsay"; + +const messages = [ + "Hello from System Manager!", + "Bun is blazingly fast! ?", + "Nix + Bun = Easy deployments", + "Making it happen!", + "Nix rocks!" +]; + +const server = Bun.serve({ + port: 3000, + fetch(req) { + const url = new URL(req.url); + + if (url.pathname === "/") { + return new Response(JSON.stringify({ + message: "Welcome to the Bun API!", + status: "running", + endpoints: ["/", "/health", "/random", "/cowsay"] + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/health") { + return new Response(JSON.stringify({ + status: "healthy" + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/random") { + const randomMessage = messages[Math.floor(Math.random() * messages.length)]; + return new Response(JSON.stringify({ + message: randomMessage, + timestamp: new Date().toISOString() + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/cowsay") { + const cow = cowsay.say({ + text: "Deployed with System Manager and Nix!" + }); + return new Response(cow, { + headers: { "Content-Type": "text/plain" } + }); + } + + return new Response("Not Found", { status: 404 }); + }, +}); + +console.log(`? Server running on http://localhost:${server.port}`); +``` + + +# Optional: Installing System Manager Locally + +Nix allows you to run code that's stored remotely in a repo, such as in GitHub. As such, you don't have to install System Manager locally to use it. However, if you want to install locally, you can do so with the following `nix profile` command. + +``` +nix profile add 'github:numtide/system-manager' +``` + +Or, if you don't have the optional features set in `/opt/nix/nix.conf`, you can provide them through the command line: + +``` +nix profile add 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' +``` + +!!! Tip + After System Manager is installed locally, you no longer need to worry about whether you have experimental features installed. You will simply pass the --flake option to System Manager. + +When you install System Manager, you might get some warnings about trusted user; this simply means you're not in the trusted user list of nix. But System Manager will still install and work fine. + +Then you can find System Manager: + +``` +$ which system-manager +/home/ubuntu/.nix-profile/bin/system-manager +``` + +And you can run System Manager: + +``` +system-manager switch --flake . --sudo +``` + + +!!! Tip + System Manager is still in an early state and undergoing active development. Installing locally will not immediately pick up new changes. If you decide to install locally, you'll want to periodically check our GitHub repo for changes, and re-install it if necessary by using `nix profile upgrade`. + + +# More stuff, possibly: + +Inspecting /var/lib/system-manager/state/system-manager-state.json + +Troubleshooting Guide + +Recipes (individual software packages, etc.) + +Package overlays + +Managing a Remote System with System Manager + +Working with Timers + +Managing Users + From a6c78504f2f33c53cefbb28a441aeb39df173d3d Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 10:27:00 +0100 Subject: [PATCH 03/22] refs: add syntax on fence quotes --- docs/site/reference-guide.md | 119 +++++++++++++++++------------------ 1 file changed, 56 insertions(+), 63 deletions(-) diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md index d36ab5e..5647c76 100644 --- a/docs/site/reference-guide.md +++ b/docs/site/reference-guide.md @@ -1,7 +1,5 @@ # Reference Guide -[PLEASE NOTE AS YOU EDIT THIS: I KNOW I MISSED BACKTICKS IN A LOT OF PLACES. I'LL GO THROUGH IT CAREFULLY AND ADD THEM ALL IN, SO DON'T WORRY ABOUT ADDING COMMENTS ABOUT THEM.] - To get the most out of System Manager, we're offering this guide to help you make the best decisions based on your particular situation. # Table of Contents @@ -67,6 +65,7 @@ nix run 'github:numtide/system-manager' -- switch --flake . --sudo ``` This is the most common scenario you'll use. + ## Command-line Options ### init @@ -79,13 +78,13 @@ This subcommand creates two initial files for use with system manager, a fully-f #### Example -``` +```sh nix run 'github:numtide/system-manager' -- init ``` This will create the initial files in `~/.config/system-manager`. -``` +```sh nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-manager' ``` @@ -352,13 +351,13 @@ To do so requires a careful set of steps. Follow these steps precisely when star 1. Temporarily run Run system manager init with experimental features enabled by including the following line in /etc/nix/nix.conf; this way `init` will generate a `flake.nix` file: -``` +```ini experimental-features = nix-command flakes ``` And then running System Manager with the init subcommand: -``` +```sh nix run 'github:numtide/system-manager' -- init ``` @@ -366,8 +365,8 @@ nix run 'github:numtide/system-manager' -- init 2. Under ~/.config/system-manager, edit the `flake.nix` file, replacing this line: -``` - modules = [ ./system.nix ]; +```nix + modules = [ ./system.nix ]; ``` with this: @@ -383,15 +382,14 @@ modules = [ 3. Delete the /etc/nix.conf file, optionally backing it up first: -``` +```sh sudo cp /etc/nix/nix.conf /etc/nix/nix_old # optional sudo rm /etc/nix/nix.conf ``` 4. Run System Manager to initialize your system, with the experimental flags set this one time in the command-line: -[Need updated --sudo command] -``` +```sh cd ~/.config/system-manager nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo ``` @@ -404,7 +402,7 @@ nix run 'github:numtide/system-manager' -- switch --flake . --sudo Next, if you want to make sure experimental features are always on, you can add it to your flake. -[TODO: Another example here] + # Using System Manager in a non-Interactive Setting @@ -422,7 +420,7 @@ The reason for these questions is Numtide has made pre-built binary versions of But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the --accept-flake-config option like so: -``` +```sh nix run 'github:numtide/system-manager' --accept-flake-config --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo ``` @@ -475,7 +473,7 @@ If you already have your .nix files, you don't need to run the init subcommand. As an example, here's a starting nix.flake file: **flake.nix** -``` +```nix { description = "Standalone System Manager configuration"; @@ -528,19 +526,19 @@ Now here's the glow.nix file referenced above; it simply installs the `glow` com go ahead and delete /etc/nix/nix.conf: -``` +```sh sudo rm /etc/nix/nix.conf ``` And now run System Manager. Because you removed nix.conf, you'll need to turn on experimental features as a command-line option. -``` +```sh nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo ``` After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can sipmly run: -``` +```sh nix run 'github:numtide/system-manager' -- switch --flake . --sudo ``` @@ -613,7 +611,7 @@ Your `config` attribute set can have: For example, you could then replace the -``` +```nix modules = [ ./system.nix ]; ``` @@ -709,7 +707,7 @@ Note: This line is required in the above example: -``` +```nix wantedBy = [ "system-manager.target" ]; ``` @@ -717,13 +715,13 @@ wantedBy = [ "system-manager.target" ]; Activate it using the same nix command as earlier: -``` +```sh nix run 'github:numtide/system-manager' -- switch --flake . --sudo ``` This will create a system service called `say-hello` (the name comes from the line `config.systemd.services.say-hello`) in a unit file at `/etc/systemd/system/say-hello.service` with the following inside it: -``` +```systemd [Unit] Description=say-hello @@ -745,13 +743,13 @@ WantedBy=system-manager.target You can verify that it ran by running journalctl: -``` +```sh journalctl -n 20 ``` and you can find the following output in it: -``` +```log Nov 18 12:12:51 my-ubuntu systemd[1]: Starting say-hello.service - say-hello... Nov 18 12:12:51 my-ubuntu say-hello-start[3488278]: Hello, world! Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. @@ -765,7 +763,7 @@ Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. 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: -``` +```nix wantedBy = [ "system-manager.target" ]; ``` @@ -773,7 +771,7 @@ wantedBy = [ "system-manager.target" ]; 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: -``` +```nix wantedBy = [ "timers.target" ] ``` @@ -844,7 +842,7 @@ Notice this flake references a file called apps.nix. In that file we'll add to t When you run System Manager, you should have the packages called `hello` and `bat` available. -``` +```console $ which hello /run/system-manager/sw/bin//hello $ which bat @@ -966,7 +964,7 @@ This creates a single file inside the folder `/etc/test/test2/` and the file is After running the above with System Manager, you can verify the file exists: -``` +```console $ cat /etc/test/test2/something.txt This is just a test!! ``` @@ -1044,7 +1042,7 @@ Common examples: 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`): -``` +```nix with_ownership = { text = '' This is just a test! @@ -1057,7 +1055,7 @@ with_ownership = { And here's an example that uses named user and group (notice we set `user` and `group`): -``` +```nix with_ownership2 = { text = '' This is just a test! @@ -1124,7 +1122,7 @@ Instead, we recommend starting with a fresh machine. One option is to spin up an After starting with a fresh machine, install Nix, copy over your flake.nix and supporting files, and test it out. Once you're ready, make sure your flake.lock file is up to date. You can create or update the flake.nix file by typing: -``` +```sh nix flake update ``` @@ -1132,7 +1130,7 @@ And make sure you've pushed it up to the repo. (If you don't do this step, nix w [todo: Let's create a repo under numtide for this instead of using mine --jeffrey] -```b +```sh nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/frecklefacelabs/system-manager-test#default --sudo ``` @@ -1177,7 +1175,7 @@ In this section we should you how to use Blueprint with System Manager. Blueprint provides its own initialization that you can start with if you don't already have a flake.nix file using Blueprint. The command to type is: -``` +```sh nix flake init -t github:numtide/blueprint ``` @@ -1201,7 +1199,7 @@ This results in the following flake: Now add System Manager to its inputs section: -``` +```nix system-manager = { url = "github:numtide/system-manager"; inputs.nixpkgs.follows = "nixpkgs"; @@ -1210,16 +1208,14 @@ Now add System Manager to its inputs section: Next, create a folder called hosts, and under that a folder called default: -``` -mkdir hosts -cd hosts -mkdir default -cd default +```sh +mkdir -p hosts/default +cd hosts/default ``` Inside `default` is where you'll put your configuration file. -**This configuration file must be named `system-configuration.nix**`. +**This configuration file must be named `system-configuration.nix**. For example, here's a configuration file that installs `bat`: @@ -1243,7 +1239,7 @@ For example, here's a configuration file that installs `bat`: Now return to the folder two levels up (the one containing flake.nix) and you can run System Manager: -``` +```sh nix run 'github:numtide/system-manager' -- switch --flake . --sudo ``` @@ -1252,7 +1248,7 @@ nix run 'github:numtide/system-manager' -- switch --flake . --sudo Then you should find `bat` on your path: -``` +```console $ which bat /run/system-manager/sw/bin//bat ``` @@ -1278,7 +1274,7 @@ If, for example, under the `hosts` folder you have a folder called `tree`, and i Then you can choose to install tree by specifying the tree folder like so: -``` +```sh nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo ``` @@ -1286,16 +1282,13 @@ nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo If you want to load multiple configuration files at once, you can create a special system-configuration.nix file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under hosts; for example, you might name it cli-tools. Starting in the folder with flake.nix: -``` -cd hosts -mkdir cli-tools -cd cli-tools -mkdir modules +```sh +mkdir -p hosts/cli-tools/modules ``` Then inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: -``` +```nix { config, lib, pkgs, ... }: { # Import all your modular configs - they auto-merge! ✨ @@ -1315,7 +1308,7 @@ Then inside the `cli-tools` folder, create a `system-configuration.nix` file wit Now move into the `modules` folder: -``` +```sh cd modules ``` @@ -1354,7 +1347,7 @@ cowsay.nix: Now you can return to the top level where you flake.nix file is and run these two configuration files: -``` +```sh nix run 'github:numtide/system-manager' -- switch --flake '.#cli-tools' --sudo ``` @@ -1369,7 +1362,7 @@ Here's a .nix file that installs PostgreSQL. Note: System Manager is still in its early state, and doesn't yet have user management, which is a planned feature that will be here soon. As such, for now, before you run this, you'll need to manually create the postgres user. Additionally, go ahead and create two directories and grant the postgres user access to them: -```bash +```sh # Create postgres user and group sudo groupadd -r postgres sudo useradd -r -g postgres -d /var/lib/postgresql -s /bin/bash postgres @@ -1634,8 +1627,8 @@ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5gQjZxG7rYPub.... }; # Optional: Certificate chain/intermediate certificates - # For this demo we're using a self-signed cert; for a real - # one, uncomment below and add your + # For this demo we're using a self-signed cert; for a real + # one, uncomment below and add your "ssl/certs/chain.pem" = { user = "root"; group = "root"; @@ -1768,7 +1761,7 @@ The configuration files install both nginx and the sample app. After you spin up an instance, install nix for all users: -``` +```sh sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon ``` @@ -1776,7 +1769,7 @@ Next, log out and log back in so that nix is available in the system path. And then you can run System Manager and deploy the app with one command: -``` +```sh nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake github:frecklefacelabs/system-manager-custom-app-deploy/v1.0.0#default --sudo ``` @@ -1790,12 +1783,12 @@ nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-comma Then, the app should be installed, with nginx sitting in front of it, and you should be able to run: -``` +```sh curl localhost ``` And it will print out a friendly JSON message such as: -``` +```json {"message":"Welcome to the Bun API!","status":"running","endpoints":["/","/health","/random","/cowsay"]} ``` @@ -1855,7 +1848,7 @@ First we have a flake much like the usual starting point: Next is the .nix configuration that installs and configures nginx. This is a simple ngnix configuration, as it simply routes incoming HTTP traffic directly to the app: -``` +```nix # nginx.nix { config, lib, pkgs, ... }: { @@ -1944,7 +1937,7 @@ in And finally, here's the `index.ts` file; it's just a simple REST app that also makes use of one third-party `npm` library. -``` +```typescript import cowsay from "cowsay"; const messages = [ @@ -2009,13 +2002,13 @@ console.log(`? Server running on http://localhost:${server.port}`); Nix allows you to run code that's stored remotely in a repo, such as in GitHub. As such, you don't have to install System Manager locally to use it. However, if you want to install locally, you can do so with the following `nix profile` command. -``` +```sh nix profile add 'github:numtide/system-manager' ``` Or, if you don't have the optional features set in `/opt/nix/nix.conf`, you can provide them through the command line: -``` +```sh nix profile add 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' ``` @@ -2026,14 +2019,14 @@ When you install System Manager, you might get some warnings about trusted user; Then you can find System Manager: -``` +```console $ which system-manager /home/ubuntu/.nix-profile/bin/system-manager ``` And you can run System Manager: -``` +```sh system-manager switch --flake . --sudo ``` From bb656c50bec33f558210dae191634105eb1be56c Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 10:27:36 +0100 Subject: [PATCH 04/22] refs: move repo to numtide --- docs/site/reference-guide.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md index 5647c76..709fcf1 100644 --- a/docs/site/reference-guide.md +++ b/docs/site/reference-guide.md @@ -1128,10 +1128,8 @@ nix flake update And make sure you've pushed it up to the repo. (If you don't do this step, nix will try to build a flake.lock, but will be unable to write it to the same location as the other files, and will error out.) -[todo: Let's create a repo under numtide for this instead of using mine --jeffrey] - ```sh -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/frecklefacelabs/system-manager-test#default --sudo +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/numtide/system-manager-test#default --sudo ``` ### When should you update your flake.nix file? From 5dbf10c2d8df0630f0d8d7b35ac94b3400406f15 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 10:33:17 +0100 Subject: [PATCH 05/22] docs: remove WIP notes and fix outdated link - Remove author WIP/TODO notes from examples.md, install.md, reference-guide.md - Update documentation link in faq.md to point to docs/ instead of manual/ --- docs/site/examples.md | 2 -- docs/site/faq.md | 2 +- docs/site/install.md | 2 -- docs/site/reference-guide.md | 10 ---------- 4 files changed, 1 insertion(+), 15 deletions(-) diff --git a/docs/site/examples.md b/docs/site/examples.md index 18147db..f82eeb5 100644 --- a/docs/site/examples.md +++ b/docs/site/examples.md @@ -1,7 +1,5 @@ # System Manager Examples -[Note: This is a WIP -- I will be updating these samples to be more consistent with the recent samples in the README.] - This document provides practical examples of using system-manager to manage system configurations on any Linux distribution. Each example demonstrates different capabilities and use cases. ## Table of Contents diff --git a/docs/site/faq.md b/docs/site/faq.md index 4fa75d1..6f27e3b 100644 --- a/docs/site/faq.md +++ b/docs/site/faq.md @@ -138,7 +138,7 @@ nix log /nix/store/ ## Additional Resources - [System Manager GitHub Repository](https://github.com/numtide/system-manager) -- [System Manager Documentation](https://github.com/numtide/system-manager/tree/main/manual) +- [System Manager Documentation](https://github.com/numtide/system-manager/tree/main/docs) - [NixOS Module Options](https://search.nixos.org/options) - [Nix Package Search](https://search.nixos.org/packages) - [PR #266: User Management with Userborn](https://github.com/numtide/system-manager/pull/266) diff --git a/docs/site/install.md b/docs/site/install.md index 24484b2..947a01b 100644 --- a/docs/site/install.md +++ b/docs/site/install.md @@ -1,7 +1,5 @@ # System Requirements -[TODO: I realized I wrote this section twice, here and in the getting-started guide. I'll combine them and provide the same in both, since some people might just read one doc or the other.] - In order to use System Manager, you need: * **A Linux machine.** We've tested System Manager with Ubuntu both as standalone and under Windows Subsystem for Linux (WSL). diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md index 709fcf1..f07c123 100644 --- a/docs/site/reference-guide.md +++ b/docs/site/reference-guide.md @@ -107,26 +107,18 @@ The following two parameters are currently both required: ### register -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - The `register` subcommand builds and registers a System Manager configuration, but does not activate it. Compare this to `switch`, which does everything register does, but then activates it. ### build -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - The `build` subcommand builds everything needed for a switch, but does not register it. ### deactivate -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - The `deactivate` deactivates System Manager. ### pre-populate -[I'm basing the following strictly on the comments in main.rs. Let me know if it needs further work, and I'm open to suggestions to how to improve it. --jeffrey] - The `prepopulate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. ### sudo @@ -402,8 +394,6 @@ nix run 'github:numtide/system-manager' -- switch --flake . --sudo Next, if you want to make sure experimental features are always on, you can add it to your flake. - - # Using System Manager in a non-Interactive Setting If you're running System Manager in a non-interative script, you might run into a problem with the four questions presented when you first run it: From 73d88510f6825d2b029298e4b9e09575aab3c047 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 10:35:31 +0100 Subject: [PATCH 06/22] docs: fix broken anchor links - Fix heading typo "Exapmle" -> "Example" for HTTPS nginx section - Remove TOC entries for sections that don't exist in reference-guide.md - Fix cross-page link in install.md to reference-guide.md - Remove stale "FAQ" TODO note from TOC --- docs/site/install.md | 2 +- docs/site/reference-guide.md | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/docs/site/install.md b/docs/site/install.md index 947a01b..8d9ba28 100644 --- a/docs/site/install.md +++ b/docs/site/install.md @@ -40,7 +40,7 @@ Note, however, that if you use the `init` subcommand to initialize an environmen If you need to run the init subcommand, but prefer to pass the `--extra-experimental-features` option to the command line, we recommend at least temporarily adding the aforementioned line to the `nix.conf` file. !!! Important - This is optional, but ultimately System Manager prefers to manage the nix.conf file for you, after which you can declare experimental features inside the flake.nix file as shown later in [Letting System Manager manage /etc/nix/nix.conf](#letting-system-manager-manage-etcnixnixconf). + This is optional, but ultimately System Manager prefers to manage the nix.conf file for you, after which you can declare experimental features inside the flake.nix file as shown later in [Letting System Manager manage /etc/nix/nix.conf](reference-guide.md#letting-system-manager-manage-etcnixnixconf). ## Running under sudo diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md index f07c123..2b6df8c 100644 --- a/docs/site/reference-guide.md +++ b/docs/site/reference-guide.md @@ -4,11 +4,7 @@ To get the most out of System Manager, we're offering this guide to help you mak # Table of Contents -- [System Requirements](#system-requirements) -- [Installation](#installation) - - [Regarding Experimental Features](#regarding-experimental-features) - - [Running under sudo](#running-under-sudo) - - [Command Line Usage](#command-line-usage) +- [Command Line Usage](#command-line-usage) - [Command-line Options](#command-line-options) - [init](#init) - [switch](#switch) @@ -54,8 +50,6 @@ To get the most out of System Manager, we're offering this guide to help you mak - [Live example](#live-example) - [Optional: Installing System Manager Locally](#optional-installing-system-manager-locally) -- FAQ (Maybe put in its own document) - # Command Line Usage The basic command looks like this: @@ -1559,9 +1553,9 @@ http { ``` -## Full Exapmle: Installing Nginx with for HTTPS with a Secure Certificate +## Full Example: Installing Nginx for HTTPS with a Secure Certificate -Here's an example that installs nginx. This exapmle shows places where you would copy in your own secure certificate information. +Here's an example that installs nginx. This example shows places where you would copy in your own secure certificate information. ```nix { lib, pkgs, ... }: From 7659a2ec3f271d719c3b50514c0c3bb48c83ec06 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 10:38:04 +0100 Subject: [PATCH 07/22] docs: add nav section to control page ordering --- docs/mkdocs.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 04e0303..bf94bc1 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -119,3 +119,11 @@ plugins: type: date fallback_to_build_date: true - tags + +nav: + - Home: index.md + - Getting Started: getting-started.md + - System Requirements: install.md + - Reference Guide: reference-guide.md + - Examples: examples.md + - FAQ: faq.md From 05bdd9cfa7b0926842a3f7bb85618e058aba24fe Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 11:19:30 +0100 Subject: [PATCH 08/22] docs: add homepage introduction --- docs/site/index.md | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/site/index.md b/docs/site/index.md index 6bd609c..a3b2ff4 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -4,4 +4,30 @@ search: exclude: true --- -# Home +# System Manager + +**Declarative system configuration for any Linux distribution.** + +System Manager brings the power of NixOS-style declarative configuration to any Linux system running systemd. Define your packages, services, and /etc files in Nix, and System Manager ensures your system matches that configuration. + +## Why System Manager? + +- **No reinstall required** - Keep your existing distro (Ubuntu, Debian, Fedora, etc.) while gaining declarative configuration +- **Reproducible systems** - Your configuration files fully describe your system state +- **Safe rollbacks** - Built on Nix, with generation-based rollback support +- **Familiar to NixOS users** - Uses the same module system and configuration patterns + +## Who is it for? + +System Manager is ideal for: + +- Developers who want reproducible development environments +- Sysadmins managing fleets of non-NixOS Linux servers +- NixOS users who need to configure systems where NixOS isn't an option +- Anyone tired of imperative configuration drift + +## Get Started + +Ready to try it? Head to the [Getting Started](getting-started.md) guide to configure your first system in minutes. + +Need to check requirements first? See the [System Requirements](install.md) page. From 41a9be391e09f5fb9ab3b9683276e743e437009d Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 11:19:30 +0100 Subject: [PATCH 09/22] docs: fix Example 1 timer system.nix code block --- docs/site/examples.md | 45 ++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 26 deletions(-) diff --git a/docs/site/examples.md b/docs/site/examples.md index f82eeb5..709f08a 100644 --- a/docs/site/examples.md +++ b/docs/site/examples.md @@ -55,37 +55,30 @@ This example demonstrates how to install a timer that runs every one minute. Fir And then here's a system.nix file referenced from within `flake.nix`: ```nix +{ pkgs, ... }: { - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; + nixpkgs.hostPlatform = "x86_64-linux"; + + # Define the timer that fires every minute + systemd.timers.simple-timer = { + enable = true; + description = "Simple timer that runs every minute"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "minutely"; + Persistent = true; }; }; - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ ./system.nix ]; - - # Optionally specify extraSpecialArgs and overlays - }; + # Define the service that the timer triggers + systemd.services.simple-timer = { + enable = true; + description = "Simple timer service"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.bash}/bin/bash -c 'echo \"Timer fired at $(date)\" >> /tmp/simple-timer.log'"; }; + }; } ``` From d7092804159039c2bd24921dadd747eba2a2a254 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 11:19:31 +0100 Subject: [PATCH 10/22] docs: add rollback limitation warning --- docs/site/install.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/site/install.md b/docs/site/install.md index 8d9ba28..f021997 100644 --- a/docs/site/install.md +++ b/docs/site/install.md @@ -13,6 +13,9 @@ In order to use System Manager, you need: !!! Important At this time, System Manager requires flakes to be enabled. +!!! Warning + Rollback functionality is not yet fully implemented. While you can list and switch between generations manually, automatic rollback on failure is not available. Always test configuration changes in a VM or non-production environment first. + # Installation Because Nix can load code (called "flakes") remotely, you don't need to download System Manager. Simply running it the first time will automatically install it in what's called the Nix Store, which is a special directory on your system (typically /nix/store) where Nix keeps all packages and their dependencies in isolation. From 07237bbfe1e3005ccbf33fde260160e64a14fbf7 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 11:22:07 +0100 Subject: [PATCH 11/22] Revert "docs: add homepage introduction" This reverts commit 05bdd9cfa7b0926842a3f7bb85618e058aba24fe. --- docs/site/index.md | 28 +--------------------------- 1 file changed, 1 insertion(+), 27 deletions(-) diff --git a/docs/site/index.md b/docs/site/index.md index a3b2ff4..6bd609c 100644 --- a/docs/site/index.md +++ b/docs/site/index.md @@ -4,30 +4,4 @@ search: exclude: true --- -# System Manager - -**Declarative system configuration for any Linux distribution.** - -System Manager brings the power of NixOS-style declarative configuration to any Linux system running systemd. Define your packages, services, and /etc files in Nix, and System Manager ensures your system matches that configuration. - -## Why System Manager? - -- **No reinstall required** - Keep your existing distro (Ubuntu, Debian, Fedora, etc.) while gaining declarative configuration -- **Reproducible systems** - Your configuration files fully describe your system state -- **Safe rollbacks** - Built on Nix, with generation-based rollback support -- **Familiar to NixOS users** - Uses the same module system and configuration patterns - -## Who is it for? - -System Manager is ideal for: - -- Developers who want reproducible development environments -- Sysadmins managing fleets of non-NixOS Linux servers -- NixOS users who need to configure systems where NixOS isn't an option -- Anyone tired of imperative configuration drift - -## Get Started - -Ready to try it? Head to the [Getting Started](getting-started.md) guide to configure your first system in minutes. - -Need to check requirements first? See the [System Requirements](install.md) page. +# Home From 8c99c7c4d5bb035546a7bdc5fe938349d7eb1d64 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 11:44:58 +0100 Subject: [PATCH 12/22] docs: add introduction page --- docs/mkdocs.yml | 1 + docs/site/introduction.md | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 docs/site/introduction.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index bf94bc1..9414047 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -122,6 +122,7 @@ plugins: nav: - Home: index.md + - Introduction: introduction.md - Getting Started: getting-started.md - System Requirements: install.md - Reference Guide: reference-guide.md diff --git a/docs/site/introduction.md b/docs/site/introduction.md new file mode 100644 index 0000000..c90396f --- /dev/null +++ b/docs/site/introduction.md @@ -0,0 +1,27 @@ +# Introduction + +**Declarative system configuration for non-NixOS Linux distributions.** + +System Manager brings the power of NixOS-style declarative configuration to other Linux distributions running systemd. Define your packages, services, and /etc files in Nix, and System Manager ensures your system matches that configuration. + +## Why System Manager? + +- **No reinstall required** - Keep your existing distro (Ubuntu, Debian, Fedora, etc.) while gaining declarative configuration +- **Reproducible systems** - Your configuration files fully describe your system state +- **Generational rollback** - Switch between previous configurations when needed +- **Familiar to NixOS users** - Uses the same module system and configuration patterns + +## Who is it for? + +System Manager is ideal for: + +- Developers who want reproducible development environments +- Sysadmins managing fleets of non-NixOS Linux servers +- NixOS users who need to configure systems where NixOS isn't an option +- Anyone tired of imperative configuration drift + +## Next Steps + +- [Getting Started](getting-started.md) - Configure your first system +- [System Requirements](install.md) - Check prerequisites +- [Examples](examples.md) - See practical use cases From 5904dd43bd40d7cab13079bb4a5eab6bace2de02 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 11:55:55 +0100 Subject: [PATCH 13/22] docs: rename install.md to requirements.md, remove duplicate content --- docs/mkdocs.yml | 2 +- docs/site/getting-started.md | 22 ++-------------------- docs/site/introduction.md | 2 +- docs/site/{install.md => requirements.md} | 0 4 files changed, 4 insertions(+), 22 deletions(-) rename docs/site/{install.md => requirements.md} (100%) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 9414047..0cc9cb4 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -124,7 +124,7 @@ nav: - Home: index.md - Introduction: introduction.md - Getting Started: getting-started.md - - System Requirements: install.md + - System Requirements: requirements.md - Reference Guide: reference-guide.md - Examples: examples.md - FAQ: faq.md diff --git a/docs/site/getting-started.md b/docs/site/getting-started.md index d116903..9546d86 100644 --- a/docs/site/getting-started.md +++ b/docs/site/getting-started.md @@ -4,29 +4,11 @@ If you've heard of NixOS, you've probably heard that it lets you define your ent *Presently System Manager is only tested on Ubuntu, and is limited to only Linux distributions based on systemd. -# Installation - -See [Install.md](install.md) for information on installing System Manager. - # Initializing Your System -To get started with System Manager, you can run our init subcommand, which will create an initial set of files in the `~/.config/system-manager` folder. - -For this first step to work, you **must** enable experimental features in the nix.conf file. (Simply adding the flags to the nix command isn't enough in this step. Afterwards you can remove the setting from your nix.conf file.) - -In the shell prompt, use your favorite editor with sudo to open the following file: - -``` -vi /etc/nix/nix.conf -``` - -Add the following line if it isn't already present: - -``` -experimental-features = nix-command flakes -``` +Before proceeding, ensure you have met all [System Requirements](requirements.md), including enabling flakes. -Save the file and exit. Next, enter the following: +To get started with System Manager, run the init subcommand, which will create an initial set of files in the `~/.config/system-manager` folder: ``` nix run 'github:numtide/system-manager' -- init diff --git a/docs/site/introduction.md b/docs/site/introduction.md index c90396f..2d0e68a 100644 --- a/docs/site/introduction.md +++ b/docs/site/introduction.md @@ -23,5 +23,5 @@ System Manager is ideal for: ## Next Steps - [Getting Started](getting-started.md) - Configure your first system -- [System Requirements](install.md) - Check prerequisites +- [System Requirements](requirements.md) - Check prerequisites - [Examples](examples.md) - See practical use cases diff --git a/docs/site/install.md b/docs/site/requirements.md similarity index 100% rename from docs/site/install.md rename to docs/site/requirements.md From ab3dfa20014a123b5b5626ecc1032ab67fd01003 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 12:09:15 +0100 Subject: [PATCH 14/22] docs: standardize System Manager capitalization in prose --- docs/site/examples.md | 12 ++++++------ docs/site/faq.md | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/site/examples.md b/docs/site/examples.md index 709f08a..16a3880 100644 --- a/docs/site/examples.md +++ b/docs/site/examples.md @@ -1,6 +1,6 @@ # System Manager Examples -This document provides practical examples of using system-manager to manage system configurations on any Linux distribution. Each example demonstrates different capabilities and use cases. +This document provides practical examples of using System Manager to manage system configurations on any Linux distribution. Each example demonstrates different capabilities and use cases. ## Table of Contents @@ -498,7 +498,7 @@ cat /etc/installed-packages.txt ### What Actually Happens When You Remove a Package? -When you remove a package from your system-manager configuration and re-run it: +When you remove a package from your System Manager configuration and re-run it: 1. **The package is removed from the system PATH**: The symbolic links in `/nix/var/nix/profiles/system-manager-profiles/*/bin/` will no longer point to the removed package @@ -508,7 +508,7 @@ When you remove a package from your system-manager configuration and re-run it: 4. **The package becomes eligible for garbage collection**: Once it's not referenced by any profile, running `nix-collect-garbage` will remove it -5. **Configuration files are updated**: Any `/etc/` files managed by system-manager are updated to reflect the new state +5. **Configuration files are updated**: Any `/etc/` files managed by System Manager are updated to reflect the new state ### Demonstration Script @@ -586,7 +586,7 @@ echo "but the files remain for potential rollback until you garbage collect." ## Example 4: User Management with Userborn (PR #266) -This example demonstrates how to create and manage users using the userborn feature from PR #266. This is currently a work-in-progress feature but shows the future direction of user management in system-manager. +This example demonstrates how to create and manage users using the userborn feature from PR #266. This is currently a work-in-progress feature but shows the future direction of user management in System Manager. **Note**: This example is based on PR #266 which is still in draft status. The implementation may change before being merged. @@ -820,7 +820,7 @@ sudo chmod 600 /run/secrets/alice-password ### User Modification Example -To modify a user, simply update the configuration and re-run system-manager: +To modify a user, simply update the configuration and re-run System Manager: ```nix # Add alice to more groups @@ -958,4 +958,4 @@ echo "=== Test Complete ===" ## Contributing -If you have additional examples or improvements to these examples, please contribute to the [system-manager repository](https://github.com/numtide/system-manager) or this documentation repository. +If you have additional examples or improvements to these examples, please contribute to the [System Manager repository](https://github.com/numtide/system-manager) or this documentation repository. diff --git a/docs/site/faq.md b/docs/site/faq.md index 6f27e3b..1993bff 100644 --- a/docs/site/faq.md +++ b/docs/site/faq.md @@ -28,7 +28,7 @@ inputs = { }; ``` -By default, each flake input pins its own version of its dependencies, which means you could end up with multiple versions of nixpkgs. The `follows` directive tells Nix to use your nixpkgs instead of the one bundled with system-manager, ensuring consistent package versions across your entire configuration while reducing disk usage and evaluation time. +By default, each flake input pins its own version of its dependencies, which means you could end up with multiple versions of nixpkgs. The `follows` directive tells Nix to use your nixpkgs instead of the one bundled with System Manager, ensuring consistent package versions across your entire configuration while reducing disk usage and evaluation time. ### 3. Modular Configuration @@ -114,7 +114,7 @@ echo $PATH ### Permission Denied -Ensure you're running system-manager with sudo: +Ensure you're running System Manager with sudo: ```bash nix run 'github:numtide/system-manager' -- switch --flake . --sudo From 9830862e74433222f0ca862ba60540ab97f3e4c4 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 12:23:43 +0100 Subject: [PATCH 15/22] docs: add inline code formatting for paths, commands, and attributes Apply backticks to: - File paths (/etc/nix/nix.conf, ~/.config/system-manager, etc.) - File names (flake.nix, system.nix, .nix files) - Package names (bat, tree, hello, etc.) - Nix attributes (systemPackages, wantedBy, config, etc.) - Commands and options (--sudo, --flake, init, switch, etc.) - Environment variables (PATH, $PATH) Also fix several typos: "bulit-in" -> "built-in", "imputs" -> "inputs", "sipmly" -> "simply", "inluding" -> "including", "ngnix" -> "nginx", "have to" -> "having to", and fix /opt/nix/nix.conf to /etc/nix/nix.conf. --- docs/site/examples.md | 4 +- docs/site/faq.md | 6 +- docs/site/getting-started.md | 30 ++--- docs/site/introduction.md | 2 +- docs/site/reference-guide.md | 226 +++++++++++++++++------------------ docs/site/requirements.md | 16 +-- 6 files changed, 142 insertions(+), 142 deletions(-) diff --git a/docs/site/examples.md b/docs/site/examples.md index 16a3880..2025459 100644 --- a/docs/site/examples.md +++ b/docs/site/examples.md @@ -504,7 +504,7 @@ When you remove a package from your System Manager configuration and re-run it: 2. **The Nix store paths remain**: The actual package files stay in `/nix/store/` until garbage collection -3. **No files are deleted from /nix/store automatically**: System-manager doesn't immediately delete packages to allow rollbacks +3. **No files are deleted from `/nix/store` automatically**: System Manager doesn't immediately delete packages to allow rollbacks 4. **The package becomes eligible for garbage collection**: Once it's not referenced by any profile, running `nix-collect-garbage` will remove it @@ -580,7 +580,7 @@ echo "but the files remain for potential rollback until you garbage collect." - **The software IS effectively uninstalled** from a user perspective - **Store files persist for rollback capability** until garbage collection - **You can always rollback** to previous configurations that had those packages -- **Garbage collection is manual** - run `nix-collect-garbage` to reclaim disk space +- **Garbage collection is manual**: Run `nix-collect-garbage` to reclaim disk space --- diff --git a/docs/site/faq.md b/docs/site/faq.md index 1993bff..a6015c1 100644 --- a/docs/site/faq.md +++ b/docs/site/faq.md @@ -28,7 +28,7 @@ inputs = { }; ``` -By default, each flake input pins its own version of its dependencies, which means you could end up with multiple versions of nixpkgs. The `follows` directive tells Nix to use your nixpkgs instead of the one bundled with System Manager, ensuring consistent package versions across your entire configuration while reducing disk usage and evaluation time. +By default, each flake input pins its own version of its dependencies, which means you could end up with multiple versions of `nixpkgs`. The `follows` directive tells Nix to use your `nixpkgs` instead of the one bundled with System Manager, ensuring consistent package versions across your entire configuration while reducing disk usage and evaluation time. ### 3. Modular Configuration @@ -99,7 +99,7 @@ ls -la /etc/systemd/system/.service ### Package Not Found in PATH -If you just installed System Manager, and installed a package through it, try logging out and logging back in to pick up the path. +If you just installed System Manager, and installed a package through it, try logging out and logging back in to pick up the `PATH`. ```bash # Check if package is in the profile @@ -108,7 +108,7 @@ ls -la /nix/var/nix/profiles/system-manager-profiles/*/bin/ # Verify the package is in your config cat /etc/installed-packages.txt -# Check PATH +# Check $PATH echo $PATH ``` diff --git a/docs/site/getting-started.md b/docs/site/getting-started.md index 9546d86..8d6f811 100644 --- a/docs/site/getting-started.md +++ b/docs/site/getting-started.md @@ -36,7 +36,7 @@ After running the command you will have the following files in your `~/.config/s Because this is your first time running System Manager, Nix will download and build several files, which might take some time. This only happens once, and in the future, System Manager will run very quickly. !!! Note - If you activate flakes through the command-line, but not through your /etc/nix/nix.conf file, then System Manager won't create the initial flake.nix file for you. In that case, you can manually create it and paste in the code we provide below, or activate the experimental features (nix-command and flakes) in /etc/nix/nix.conf, and then re-run the System Manager init command. + If you activate flakes through the command-line, but not through your `/etc/nix/nix.conf` file, then System Manager won't create the initial `flake.nix` file for you. In that case, you can manually create it and paste in the code we provide below, or activate the experimental features (`nix-command` and `flakes`) in `/etc/nix/nix.conf`, and then re-run the System Manager `init` command. Here are the contents of the files that were created: @@ -146,7 +146,7 @@ First, let's build a configuration file that installs or uninstalls apps. !!! Tip The idea is that the configuration file describes what the system should look like. Keep that in mind, as opposed to thinking that the configuration file "installs software" or "uninstalls software." -To get started, we'll create another .nix file that will install a single app. Then we'll run System Manager, and verify it's installed. +To get started, we'll create another `.nix` file that will install a single app. Then we'll run System Manager, and verify it's installed. Then to demonstrate what System Manager can do, we'll add another line to the configuration file with another app; run System Manager again, and again verify its installation. @@ -154,7 +154,7 @@ Then after that we'll remove one of the apps from the configuration file, run Sy This will fully demonstrate the declarative nature of these configuration files. -First, in the ~/.config/system-manager folder, create a file apps.nix and place the following in it: +First, in the `~/.config/system-manager` folder, create a file `apps.nix` and place the following in it: ```nix { pkgs, ... }: @@ -167,9 +167,9 @@ First, in the ~/.config/system-manager folder, create a file apps.nix and place } ``` -This configuration states that the system being configured should have the `tldr` app present, and if isn't, System Manager will install it. (Notice how we phrased that! We didn't just say this file installs the app. With .nix files, it's important to get into the mindset that they state what the system should look like.) +This configuration states that the system being configured should have the `tldr` app present, and if isn't, System Manager will install it. (Notice how we phrased that! We didn't just say this file installs the app. With `.nix` files, it's important to get into the mindset that they state what the system should look like.) -Now add the file to the modules list in flake.nix by replacing this modules line: +Now add the file to the modules list in `flake.nix` by replacing this modules line: ```diff - modules = [ ./system.nix ]; @@ -179,7 +179,7 @@ Now add the file to the modules list in flake.nix by replacing this modules line + ]; ``` -Note: By default, system.nix includes starter code and some commented out examples, and nothing else. So you can leave it in the list; in its original state, it doesn't do anything. +Note: By default, `system.nix` includes starter code and some commented out examples, and nothing else. So you can leave it in the list; in its original state, it doesn't do anything. Next, we'll run System Manager. @@ -191,9 +191,9 @@ nix run 'github:numtide/system-manager' -- switch --flake . --sudo After a short moment, the `tldr` app should be installed on your system. !!! Tip - The first time you install software with System Manager, it adds a path to your $PATH variable by creating an entry in /etc/profile.d/. This won't take effect until you log out and back in; or you can source the file like so: `source /etc/profile.d/system-manager-path.sh` After that, you should find the tldr program: `which tldr` should yield `/run/system-manager/sw/bin//tldr`. + The first time you install software with System Manager, it adds a path to your `$PATH` variable by creating an entry in `/etc/profile.d/`. This won't take effect until you log out and back in; or you can source the file like so: `source /etc/profile.d/system-manager-path.sh` After that, you should find the `tldr` program: `which tldr` should yield `/run/system-manager/sw/bin//tldr`. -Now to demonstrate the declarative feature of System Manager, let's add another app to the list. Here's a fun app called cowsay. Add a single line "cowsay" to the list passed into systemPackages: +Now to demonstrate the declarative feature of System Manager, let's add another app to the list. Here's a fun app called `cowsay`. Add a single line `cowsay` to the list passed into `systemPackages`: ```nix { pkgs, ... }: @@ -225,7 +225,7 @@ Run System Manager again with the same command as above, and you should now have ~/.config/system-manager$ ``` -Remember, this is a declarative approach; System Manager did not re-install `tldr`. It looked at the list (tldr, cowsay) and compared it to what is currently installed. It saw that `tldr` is already installed, so it skipped that one. It saw `cowsay` is *not* installed, so it installed it, so that the system matches the configuration file. +Remember, this is a declarative approach; System Manager did not re-install `tldr`. It looked at the list (`tldr`, `cowsay`) and compared it to what is currently installed. It saw that `tldr` is already installed, so it skipped that one. It saw `cowsay` is *not* installed, so it installed it, so that the system matches the configuration file. Now let's remove `cowsay` from the list of installed software. To do so, simply remove the line (or comment it out): @@ -242,13 +242,13 @@ Now let's remove `cowsay` from the list of installed software. To do so, simply } ``` -Notice this file now looks exactly as it did before adding in cowsay, meaning System Manager the system will now look like it did before adding in `cowsay`. Re-run System Manager and you'll see that `cowsay` is no longer installed. +Notice this file now looks exactly as it did before adding in `cowsay`, meaning the system will now look like it did before adding in `cowsay`. Re-run System Manager and you'll see that `cowsay` is no longer installed. ## Understanding the config attribute set -In the above example, we added attributes to the systemPackages set, which is a part of the environment attribute set, which in turn is part of the config attribute set. +In the above example, we added attributes to the `systemPackages` set, which is a part of the `environment` attribute set, which in turn is part of the `config` attribute set. -When System Manager runs, with the help of the Nix language, you can have multiple config attribute sets, and System Manager combines them into a single attribute set. This allows you to have different setups in separate files, and simply combine them side by side, only having to add on to this line: +When System Manager runs, with the help of the Nix language, you can have multiple `config` attribute sets, and System Manager combines them into a single attribute set. This allows you to have different setups in separate files, and simply combine them side by side, only having to add on to this line: ```nix modules = [ @@ -259,7 +259,7 @@ When System Manager runs, with the help of the Nix language, you can have multip However, you need to be careful. Suppose you have a different set of software you want to install, and you create a flake in another area in the filesystem with that software. It might not work the way you intend. -With that second flake, System Manager will gather up any apps you have in the systemPackages attribute, and compare that to what it has already installed earlier. If the packages installed earlier aren't included, it will remove those packages. (That includes the apps you installed with the "other" flake.) +With that second flake, System Manager will gather up any apps you have in the `systemPackages` attribute, and compare that to what it has already installed earlier. If the packages installed earlier aren't included, it will remove those packages. (That includes the apps you installed with the "other" flake.) In other words, you cannot have two separate flakes, one for one set of software, the other for a different set of software, and bounce between those flakes. System Manager will treat the second as requesting to uninstall what it installed earlier. @@ -269,7 +269,7 @@ To make the above work, your best bet is to create a single flake and add in ind ## Understanding Imperative State vs Declarative State -Imperative state means you change the system by hand, step by step. You run commands like apt install, edit files under /etc with a text editor, toggle systemd services, and make changes as you think of them. You're telling the computer how to do something: +Imperative state means you change the system by hand, step by step. You run commands like `apt install`, edit files under `/etc` with a text editor, toggle systemd services, and make changes as you think of them. You're telling the computer how to do something: > "Install this package, then edit this file, then restart this service." @@ -279,7 +279,7 @@ Each action mutates the system in place, and over time the machine can drift int Declarative state, on the other hand, means you don't tell the system how to do the steps — you tell it what you want the final system to look like, and the tool (System Manager, NixOS, Home Manager, etc.) figures out the steps automatically. -> "This machine should have these packages, these /etc files, and these services enabled." +> "This machine should have these packages, these `/etc` files, and these services enabled." When you activate that configuration, the tool builds the desired end state and applies it in a predictable, repeatable way. Here's A simple analogy: diff --git a/docs/site/introduction.md b/docs/site/introduction.md index 2d0e68a..5734c3a 100644 --- a/docs/site/introduction.md +++ b/docs/site/introduction.md @@ -2,7 +2,7 @@ **Declarative system configuration for non-NixOS Linux distributions.** -System Manager brings the power of NixOS-style declarative configuration to other Linux distributions running systemd. Define your packages, services, and /etc files in Nix, and System Manager ensures your system matches that configuration. +System Manager brings the power of NixOS-style declarative configuration to other Linux distributions running systemd. Define your packages, services, and `/etc` files in Nix, and System Manager ensures your system matches that configuration. ## Why System Manager? diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md index 2b6df8c..5713ab1 100644 --- a/docs/site/reference-guide.md +++ b/docs/site/reference-guide.md @@ -64,7 +64,7 @@ This is the most common scenario you'll use. ### init -This subcommand creates two initial files for use with system manager, a fully-functional flake.nix, and a system.nix file that contains skeleton code. +This subcommand creates two initial files for use with System Manager, a fully-functional `flake.nix`, and a `system.nix` file that contains skeleton code. #### Command line options @@ -85,7 +85,7 @@ nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-mana This will create the initial files in `/home/ubuntu/system-manager`. !!! Note - Presently, System Manager requires Flakes to be active. If you choose to not include the experimental features line in /etc/nix/nix.conf (and instead use the experimental features command line option), then init will only create a system.nix file, rather than both a flake.nix file and system.nix file. + Presently, System Manager requires flakes to be active. If you choose to not include the experimental features line in `/etc/nix/nix.conf` (and instead use the experimental features command line option), then `init` will only create a `system.nix` file, rather than both a `flake.nix` file and `system.nix` file. ### switch @@ -113,11 +113,11 @@ The `deactivate` deactivates System Manager. ### pre-populate -The `prepopulate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. +The `pre-populate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. ### sudo -The sudo subcommand grants sudo access to System Manager, while running under the current user. All created files with be owned by the current user. +The `sudo` subcommand grants sudo access to System Manager, while running under the current user. All created files will be owned by the current user. # Setting up a folder and file structure @@ -129,7 +129,7 @@ Before you begin with System Manager, you'll need to decide on your folder struc Technically, you are free to set up your folders and files however you like; System Manager does not enforce any rules, thus allowing you full flexibility. Below are simply some options that we recommend. !!! Tip - While you are free to have different system manager .nix files scattered throughout your system, we recommend, if possible, keeping them in a single location simply for organizational purposes. But again, this is just a recommendation and you're not bound by any such rules. + While you are free to have different System Manager `.nix` files scattered throughout your system, we recommend, if possible, keeping them in a single location simply for organizational purposes. But again, this is just a recommendation and you're not bound by any such rules. ## Deciding on a folder structure @@ -161,9 +161,9 @@ To make this happen, however, requires careful consideration [as we discuss late ### Option 1: Your personal ~/.config folder -If you're managing a system yourself and only you will be using it, one possibility is to put the files in ~/.config/system-manager. +If you're managing a system yourself and only you will be using it, one possibility is to put the files in `~/.config/system-manager`. -This approach keeps everything scoped to you and avoids having to place files under /etc and, perhaps most importantly, avoids have to use sudo. Here's an example layout: +This approach keeps everything scoped to you and avoids having to place files under `/etc` and, perhaps most importantly, avoids having to use sudo. Here's an example layout: ``` ~/.config/system-manager/ @@ -175,7 +175,7 @@ This approach keeps everything scoped to you and avoids having to place files un !!! Tip Avoid this location if multiple people use the machine or if this configuration is meant to be shared with a team. Home-directory paths are user-specific and may not make sense across machines. -### Option 2: A shared /etc/system-manager folder (Recommended for multi-user or organizational setups) +### Option 2: A shared `/etc/system-manager` folder (Recommended for multi-user or organizational setups) If you are: @@ -187,7 +187,7 @@ If you are: * or simply want a clear system-level location, -then /etc/system-manager is a great choice. Among the advantages are consistency across all machines; standard within an organization; and treating system manager as a system-level tool rather than a personal configuration. Here's an example layout: +then `/etc/system-manager` is a great choice. Among the advantages are consistency across all machines; standard within an organization; and treating system manager as a system-level tool rather than a personal configuration. Here's an example layout: ``` /etc/system-manager/ @@ -199,17 +199,17 @@ then /etc/system-manager is a great choice. Among the advantages are consistency ## Choosing a file structure -After choosing where your configuration lives, you must decide how to structure the files inside it. And note that while system-manager does not enforce any rules, we do recommend you maintain consistency, especially if you have multiple locations on your computer where you store system manager .nix files. +After choosing where your configuration lives, you must decide how to structure the files inside it. And note that while System Manager does not enforce any rules, we do recommend you maintain consistency, especially if you have multiple locations on your computer where you store System Manager `.nix` files. Essentially, you have two options: -* A single flake.nix file +* A single `flake.nix` file -* A reusable flake.nix file with one or more separate configuration files that describe what the system will look like. +* A reusable `flake.nix` file with one or more separate configuration files that describe what the system will look like. Within Option B, you can also use our open-source Blueprint product to help you manage your files, which we'll cover shortly. -### Option A: Single flake.nix file +### Option A: Single `flake.nix` file This configuration is ideal for: @@ -264,7 +264,7 @@ system-manager/ This is the approach we use in our examples in this document. That way each isolated "recipe" is repeatable and can be re-used across multiple systems. -### Dealing with conflicting .nix files +### Dealing with conflicting `.nix` files If you have multiple flakes throughout your computer, you can run into a situation where one might install some software, and the other might install a different software -- but uninstall what was in the other configuration. @@ -296,12 +296,12 @@ Then in this folder your run System Manager. System Manager does not track files, and see this as a changed configuration: -* The configuration **no longer has** bat, nginx, and mysql84. -* The configuration does have hello, postgresql_18, and vscode. +* The configuration **no longer has** `bat`, `nginx`, and `mysql84`. +* The configuration does have `hello`, `postgresql_18`, and `vscode`. -The end result is that System Manager will **remove** bat, nginx, and mysql84, and install hello, postgresql_18, and vscode. +The end result is that System Manager will **remove** `bat`, `nginx`, and `mysql84`, and install `hello`, `postgresql_18`, and `vscode`. -The fix to this problem is to instead have a single main flake.nix file, which loads all of the different .nix files, allowing you to run System Manager from a single location. +The fix to this problem is to instead have a single main `flake.nix` file, which loads all of the different `.nix` files, allowing you to run System Manager from a single location. This is because Nix has the ability to merge together objects in separate files into a single object; the above would then merge into: @@ -316,7 +316,7 @@ This is because Nix has the ability to merge together objects in separate files ]; ``` -We describe this technique in [Building System Manager .nix Files](#building-system-manager-nix-files). +We describe this technique in [Building System Manager `.nix` Files](#building-system-manager-nix-files). # Letting System Manager manage `/etc/nix/nix.conf` @@ -324,18 +324,18 @@ System Manager can optionally manage your `/etc/nix/nix.conf` file for you. If you have an existing `/etc/nix/nix.conf` file, you'll need to delete it if you want System Manager to manage the file; then run System Manager again. From that point on System Manager will manage the file for you, and you should not make changes to it. -Instead, you'll put the changes in one of your .nix files you'll be building to configure System Manager. +Instead, you'll put the changes in one of your `.nix` files you'll be building to configure System Manager. # Recommended Workflow for Starting Out -As described previously, System Manager wants to manage your /etc/nix.conf file for you, after which you can instead place your configurations directly in the flake.nix file, including specifying experimental features. +As described previously, System Manager wants to manage your `/etc/nix/nix.conf` file for you, after which you can instead place your configurations directly in the `flake.nix` file, including specifying experimental features. To do so requires a careful set of steps. Follow these steps precisely when starting out with a fresh system. !!! Note - We will first run System Manager to create an initial flake.nix and system.nix file; we will then delete the `/etc/nix/nix.conf` file, and instead add the flags to the flake.nix file. Then we will run System Manager again to start managing your system, inluding the `/etc/nix/nix.conf` file. + We will first run System Manager to create an initial `flake.nix` and `system.nix` file; we will then delete the `/etc/nix/nix.conf` file, and instead add the flags to the `flake.nix` file. Then we will run System Manager again to start managing your system, including the `/etc/nix/nix.conf` file. -1. Temporarily run Run system manager init with experimental features enabled by including the following line in /etc/nix/nix.conf; this way `init` will generate a `flake.nix` file: +1. Temporarily run System Manager with `init` with experimental features enabled by including the following line in `/etc/nix/nix.conf`; this way `init` will generate a `flake.nix` file: ```ini experimental-features = nix-command flakes @@ -347,9 +347,9 @@ And then running System Manager with the init subcommand: nix run 'github:numtide/system-manager' -- init ``` -(For this step, do not simply add the flag for experimental features; otherwise init won't create the flake.nix file.) +(For this step, do not simply add the flag for experimental features; otherwise `init` won't create the `flake.nix` file.) -2. Under ~/.config/system-manager, edit the `flake.nix` file, replacing this line: +2. Under `~/.config/system-manager`, edit the `flake.nix` file, replacing this line: ```nix modules = [ ./system.nix ]; @@ -366,7 +366,7 @@ modules = [ ]; ``` -3. Delete the /etc/nix.conf file, optionally backing it up first: +3. Delete the `/etc/nix/nix.conf` file, optionally backing it up first: ```sh sudo cp /etc/nix/nix.conf /etc/nix/nix_old # optional @@ -380,7 +380,7 @@ cd ~/.config/system-manager nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo ``` -System Manager is now managing your system for you, including the /etc/nix/nix.conf file. And experimental features are required and turned on through the flake.nix file, meaning you do not need to include the --extra-experimental-features flag when you run System Manager: +System Manager is now managing your system for you, including the `/etc/nix/nix.conf` file. And experimental features are required and turned on through the `flake.nix` file, meaning you do not need to include the `--extra-experimental-features` option when you run System Manager: ``` nix run 'github:numtide/system-manager' -- switch --flake . --sudo @@ -402,7 +402,7 @@ If you're running System Manager in a non-interative script, you might run into The reason for these questions is Numtide has made pre-built binary versions of System Manager available from our cache, which speeds up performance since your system doesn't have to build System Manager from source. However, this triggers Nix to ask these four questions. You'll most likely want to answer "y" to all four. -But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the --accept-flake-config option like so: +But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the `--accept-flake-config` option like so: ```sh nix run 'github:numtide/system-manager' --accept-flake-config --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo @@ -444,17 +444,17 @@ If you like, you can add these settings into your flake file, such as in the fol } ``` -Remember, however, the flake shows what the system looks like *after* System Manager runs. That means these changes won't affect the first run of System Manager, which in this case is likely through a script. As such, the first time you run System Manager, you'll still need the `--accept-flake-config` flag. Then on subsequent runs you don't need the `--accept-flake-config flag`. +Remember, however, the flake shows what the system looks like *after* System Manager runs. That means these changes won't affect the first run of System Manager, which in this case is likely through a script. As such, the first time you run System Manager, you'll still need the `--accept-flake-config` option. Then on subsequent runs you don't need the `--accept-flake-config` option. # Recommended Workflow if You Already Have Your Nix Files -If you already have your .nix files, you don't need to run the init subcommand. Instead, we recommend the following if you're starting out on a clean system: +If you already have your `.nix` files, you don't need to run the `init` subcommand. Instead, we recommend the following if you're starting out on a clean system: -1. Remove the /etc/nix/nix.conf file. Then, when you run your System Manager the first time, System Manager will take control managing this file for you. You can then place any configuration you previously had in the /etc/etc/nix.conf file in your .nix files. +1. Remove the `/etc/nix/nix.conf` file. Then, when you run System Manager the first time, System Manager will take control managing this file for you. You can then place any configuration you previously had in the `/etc/nix/nix.conf` file in your `.nix` files. 2. Run System Manager the first time, and you'll be ready to go. -As an example, here's a starting nix.flake file: +As an example, here's a starting `flake.nix` file: **flake.nix** ```nix @@ -491,7 +491,7 @@ As an example, here's a starting nix.flake file: Notice that we've included in the modules list an object that sets experimental features, turning on flakes. -Now here's the glow.nix file referenced above; it simply installs the `glow` command, which is for displaying markdown files in a shell: +Now here's the `glow.nix` file referenced above; it simply installs the `glow` command, which is for displaying markdown files in a shell: **glow.nix** @@ -508,26 +508,26 @@ Now here's the glow.nix file referenced above; it simply installs the `glow` com } ``` -go ahead and delete /etc/nix/nix.conf: +Go ahead and delete `/etc/nix/nix.conf`: ```sh sudo rm /etc/nix/nix.conf ``` -And now run System Manager. Because you removed nix.conf, you'll need to turn on experimental features as a command-line option. +And now run System Manager. Because you removed `nix.conf`, you'll need to turn on experimental features as a command-line option. ```sh nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo ``` -After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can sipmly run: +After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can simply run: ```sh nix run 'github:numtide/system-manager' -- switch --flake . --sudo ``` -# Building system-manager .nix files +# Building System Manager `.nix` files Ready for an example! For this example, we're going to use the following: @@ -535,15 +535,15 @@ Ready for an example! For this example, we're going to use the following: * We'll have two files, one `flake.nix`, and `system.nix` -Note that we'll be using the files generated by the 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. +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 +## The Main `flake.nix` File -We recommend you start with a basic flake.nix file similar to this: +We recommend you start with a basic `flake.nix` file similar to this: ```nix { @@ -580,16 +580,16 @@ 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. +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`. +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 as nixpkgs.hostPlatform = "x86_64-linux"; +* `nixpkgs.hostPlatform`: This specifies the platform such as `nixpkgs.hostPlatform = "x86_64-linux";` * `environment`, consisting of - * systemPackages - * etc + * `systemPackages` + * `etc` * `systemd.services` * `systemd.tmpfiles` @@ -599,7 +599,7 @@ For example, you could then replace the modules = [ ./system.nix ]; ``` -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. +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: @@ -647,22 +647,22 @@ Then here are the individual "recipe" files. } ``` -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](#dealing-with-conflicting-nix-files) +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](#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 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. +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](#the-main-flakenix-file). +We're assuming you're using a `flake.nix` similar to what's found in [The Main `flake.nix` File](#the-main-flakenix-file). ```nix @@ -695,7 +695,7 @@ This line is required in the above example: wantedBy = [ "system-manager.target" ]; ``` -(There are other options for wantedBy; we discuss it in full in our Reference Guide under [Specifying WantedBy Setting](./reference-guide.md#specifying-the-wantedby-setting)) +(There are other options for `wantedBy`; we discuss it in full in our Reference Guide under [Specifying `wantedBy` Setting](./reference-guide.md#specifying-the-wantedby-setting)) Activate it using the same nix command as earlier: @@ -703,7 +703,7 @@ Activate it using the same nix command as earlier: nix run 'github:numtide/system-manager' -- switch --flake . --sudo ``` -This will create a system service called `say-hello` (the name comes from the line `config.systemd.services.say-hello`) in a unit file at `/etc/systemd/system/say-hello.service` with the following inside it: +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: ```systemd [Unit] @@ -725,7 +725,7 @@ WantedBy=system-manager.target !!! 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: +You can verify that it ran by running `journalctl`: ```sh journalctl -n 20 @@ -740,12 +740,12 @@ Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. ``` !!! Note - If you remove the `./apps.nix` line from the `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. + 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 +## 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: +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: ```nix wantedBy = [ "system-manager.target" ]; @@ -761,7 +761,7 @@ wantedBy = [ "timers.target" ] # 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. +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. @@ -805,7 +805,7 @@ 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 set. Here's the apps.nix file: +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: ```nix { lib, pkgs, ... }: @@ -824,7 +824,7 @@ Notice this flake references a file called apps.nix. In that file we'll add to t } ``` -When you run System Manager, you should have the packages called `hello` and `bat` available. +When you run System Manager, you should have the packages `hello` and `bat` available. ```console $ which hello @@ -834,9 +834,9 @@ $ which bat ``` !!! Note - The first time you install an app through System Manager, System Manager will add a file inside `/etc/profile.d`. This file adds on the `/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. + 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: +If you prefer, you can combine the above two `.nix` files into a single flake: ```nix { @@ -876,16 +876,16 @@ If you prefer, you can combine the above two .nix files into a single flake: } ``` -# Working With /etc Files Declaratively +# 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. +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. +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 +## Example: Creating a file in `/etc` Starting with a flake such as this: @@ -922,7 +922,7 @@ 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: +Notice this references a file called `files1.nix`. To create files, you add attributes to the `config.environment.etc` attribute set as follows: ```nix { lib, pkgs, ... }: @@ -944,7 +944,7 @@ Notice this references a file called `files1.nix`. To create files, you add attr } ``` -This creates a single file inside the folder `/etc/test/test2/` and the file is called `something.txt`. +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: @@ -953,7 +953,7 @@ $ cat /etc/test/test2/something.txt This is just a test!! ``` -Note that if you prefer, you can combine the above flake and separate .nix file into a single flake like so: +Note that if you prefer, you can combine the above flake and separate `.nix` file into a single flake like so: ```nix { @@ -1051,13 +1051,13 @@ with_ownership2 = { ``` !!! Tip - This use of `uid`/`gid` for IDs and `user`/`group` for names aligns with NixOS standards. + 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. +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 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: @@ -1076,9 +1076,9 @@ For this we offer two distinct syntaxes you can use, depending on your needs, as }; ``` -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 aged-based cleanup.) +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. +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. # Working with remote flakes @@ -1087,11 +1087,11 @@ Instead of saving your System Manager configuration files locally, you can optio !!! Note This is a great option if you plan to use the files on multiple machines. -In order to store them on a remote repo, it's imperative that you keep your flake.lock file up to date. +In order to store them on a remote repo, it's imperative that you keep your `flake.lock` file up to date. -## What's a flake.lock file? +## What's a `flake.lock` file? -A flake.lock file is a JSON file that stores the exact versions of all the inputs your flake file depends on, including things like nixpkgs, System Manager itself, and anything else you might import. Instead of pulling the latest version every time you build, the lock file ensures that the same inputs are used consistently across machines and over time. This makes your configuration reproducible, stable, and rollback-friendly. When you do want to update to new versions, you run a command like nix flake update, which refreshes the lock file in a controlled way. +A `flake.lock` file is a JSON file that stores the exact versions of all the inputs your flake file depends on, including things like nixpkgs, System Manager itself, and anything else you might import. Instead of pulling the latest version every time you build, the lock file ensures that the same inputs are used consistently across machines and over time. This makes your configuration reproducible, stable, and rollback-friendly. When you do want to update to new versions, you run a command like `nix flake update`, which refreshes the lock file in a controlled way. ## Setting up your project for remote hosting @@ -1104,58 +1104,58 @@ Instead, we recommend starting with a fresh machine. One option is to spin up an !!! Important You'll need to ensure you have at least 16GB of disk space on the virtual machine. If you go with 8GB, you're going to run out of space. -After starting with a fresh machine, install Nix, copy over your flake.nix and supporting files, and test it out. Once you're ready, make sure your flake.lock file is up to date. You can create or update the flake.nix file by typing: +After starting with a fresh machine, install Nix, copy over your `flake.nix` and supporting files, and test it out. Once you're ready, make sure your `flake.lock` file is up to date. You can create or update the `flake.lock` file by typing: ```sh nix flake update ``` -And make sure you've pushed it up to the repo. (If you don't do this step, nix will try to build a flake.lock, but will be unable to write it to the same location as the other files, and will error out.) +And make sure you've pushed it up to the repo. (If you don't do this step, Nix will try to build a `flake.lock`, but will be unable to write it to the same location as the other files, and will error out.) ```sh nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/numtide/system-manager-test#default --sudo ``` -### When should you update your flake.nix file? +### When should you update your `flake.lock` file? -Generally, you only need to update your flake.nix file when you want newer versions of your inputs (nixpkgs, etc). Updating isn't necessary for daily use; your configuration will continue to work with the locked versions. But you will want to update your flake.lock file in cases such as: +Generally, you only need to update your `flake.lock` file when you want newer versions of your inputs (nixpkgs, etc). Updating isn't necessary for daily use; your configuration will continue to work with the locked versions. But you will want to update your `flake.lock` file in cases such as: * You want newer package versions (e.g. newer `btop`, etc.) * You want security patches -* You've added new imputs to your flakes (in which case you'll be required to update `flake.lock`) +* You've added new inputs to your flakes (in which case you'll be required to update `flake.lock`) * You're preparing a fresh install and decide this is a good time to upgrade everything -### Can't System Manager build flake.lock for me? +### Can't System Manager build `flake.lock` for me? -Yes, but only if the flake.nix file is local to your machine. The problem is System Manager will try to write a flake.lock file in the same location as the flake.nix file, which isn't possible (at this time) with a GitHub repo. +Yes, but only if the `flake.nix` file is local to your machine. The problem is System Manager will try to write a `flake.lock` file in the same location as the `flake.nix` file, which isn't possible (at this time) with a GitHub repo. ### Ensuring success -In order to ensure System Manager retrieves the correct .nix files from your repo, we recommend including either a branch or a tag along with your repo. +In order to ensure System Manager retrieves the correct `.nix` files from your repo, we recommend including either a branch or a tag along with your repo. ## Running System Manager with a remote flake !!! Tip - Before you run this command, we recommend that you nevertheless create a folder to run it from, such as ~/.config/system-manager. + Before you run this command, we recommend that you nevertheless create a folder to run it from, such as `~/.config/system-manager`. # Using Blueprint with System Manager Blueprint is an opinionated library that maps a standard folder structure to flake outputs, allowing you to divide up your flake into individual files across these folders. This allows you to modularize and isolate these files so that they can be maintained individually and even shared across multiple projects. -Blueprint has bulit-in support for System Manager, which means: +Blueprint has built-in support for System Manager, which means: -* You do not need to call system-manager.lib.makeSystemConfig; Blueprint calls this for you -* You must follow Blueprint's folder structure by placing your files under the hosts folder, and you must name your files `system-configuration.nix`. -* You can have multiple folders under the `hosts` folder (but one level deep), and you can access these using the standard nix specifier, e.g. `.#folder-name.` +* You do not need to call `system-manager.lib.makeSystemConfig`; Blueprint calls this for you +* You must follow Blueprint's folder structure by placing your files under the `hosts` folder, and you must name your files `system-configuration.nix`. +* You can have multiple folders under the `hosts` folder (but one level deep), and you can access these using the standard nix specifier, e.g. `.#folder-name`. -In this section we should you how to use Blueprint with System Manager. +In this section we show you how to use Blueprint with System Manager. -Blueprint provides its own initialization that you can start with if you don't already have a flake.nix file using Blueprint. The command to type is: +Blueprint provides its own initialization that you can start with if you don't already have a `flake.nix` file using Blueprint. The command to type is: ```sh nix flake init -t github:numtide/blueprint @@ -1188,7 +1188,7 @@ Now add System Manager to its inputs section: }; ``` -Next, create a folder called hosts, and under that a folder called default: +Next, create a folder called `hosts`, and under that a folder called `default`: ```sh mkdir -p hosts/default @@ -1197,7 +1197,7 @@ cd hosts/default Inside `default` is where you'll put your configuration file. -**This configuration file must be named `system-configuration.nix**. +**This configuration file must be named `system-configuration.nix`.** For example, here's a configuration file that installs `bat`: @@ -1217,9 +1217,9 @@ For example, here's a configuration file that installs `bat`: ``` !!! Note - Notice that we need to include nixpkgs.hostPlatform in this file, as there's no place to include it in the parent `flake.nix` file. + Notice that we need to include `nixpkgs.hostPlatform` in this file, as there's no place to include it in the parent `flake.nix` file. -Now return to the folder two levels up (the one containing flake.nix) and you can run System Manager: +Now return to the folder two levels up (the one containing `flake.nix`) and you can run System Manager: ```sh nix run 'github:numtide/system-manager' -- switch --flake . --sudo @@ -1235,9 +1235,9 @@ $ which bat /run/system-manager/sw/bin//bat ``` -The default folder is called default; you can also refer to folders by name as mentioned earlier. +The default folder is called `default`; you can also refer to folders by name as mentioned earlier. -If, for example, under the `hosts` folder you have a folder called `tree`, and inside `tree` create a file called `system-configuration.nix` with the following contents: +If, for example, under the `hosts` folder you have a folder called `tree`, and inside `tree` you create a file called `system-configuration.nix` with the following contents: ```nix { lib, pkgs, ... }: @@ -1254,7 +1254,7 @@ If, for example, under the `hosts` folder you have a folder called `tree`, and i } ``` -Then you can choose to install tree by specifying the tree folder like so: +Then you can choose to install `tree` by specifying the `tree` folder like so: ```sh nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo @@ -1262,13 +1262,13 @@ nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo ## Using multiple configuration files with Blueprint -If you want to load multiple configuration files at once, you can create a special system-configuration.nix file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under hosts; for example, you might name it cli-tools. Starting in the folder with flake.nix: +If you want to load multiple configuration files at once, you can create a special `system-configuration.nix` file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under `hosts`; for example, you might name it `cli-tools`. Starting in the folder with `flake.nix`: ```sh mkdir -p hosts/cli-tools/modules ``` -Then inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: +Then, inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: ```nix { config, lib, pkgs, ... }: @@ -1286,7 +1286,7 @@ Then inside the `cli-tools` folder, create a `system-configuration.nix` file wit } ``` -(Notice this time we can put the nixpkgs.hostPlatform in a single place. As such we won't need it in the configuration files.) +(Notice this time we can put the `nixpkgs.hostPlatform` in a single place. As such we won't need it in the configuration files.) Now move into the `modules` folder: @@ -1327,7 +1327,7 @@ cowsay.nix: } ``` -Now you can return to the top level where you flake.nix file is and run these two configuration files: +Now you can return to the top level where your `flake.nix` file is and run these two configuration files: ```sh nix run 'github:numtide/system-manager' -- switch --flake '.#cli-tools' --sudo @@ -1340,7 +1340,7 @@ This means if you want to include various recipes, you can easily do so. ## Full Example: Installing PostgreSQL -Here's a .nix file that installs PostgreSQL. +Here's a `.nix` file that installs PostgreSQL. Note: System Manager is still in its early state, and doesn't yet have user management, which is a planned feature that will be here soon. As such, for now, before you run this, you'll need to manually create the postgres user. Additionally, go ahead and create two directories and grant the postgres user access to them: @@ -1357,7 +1357,7 @@ sudo mkdir -p /run/postgresql sudo chown postgres:postgres /run/postgresql ``` -Here, then, is the .nix file. +Here, then, is the `.nix` file. ```nix { config, lib, pkgs, ... }: @@ -1439,7 +1439,7 @@ Here, then, is the .nix file. ## Full Example: Installing Nginx -Here's a .nix file that installs and configures nginx as a system service. Note that this version only supports HTTP and not HTTPS; later we provide an example that includes HTTPS. +Here's a `.nix` file that installs and configures nginx as a system service. Note that this version only supports HTTP and not HTTPS; later we provide an example that includes HTTPS. !!! Tip This is simply an example to help you learn how to use System Manager. The usual way to install nginx under Nix is to use the [nginx package](https://search.nixos.org/packages?channel=25.11&show=nginx&query=nginx). @@ -1776,9 +1776,9 @@ And it will print out a friendly JSON message such as: We even included cowsay in this sample, which you can try at `curl localhost/cowsay`. Now even though cowsay is meant for fun, the primary reason is this is a TypeScript app that uses `bun`, and we wanted to demonstrate how easy it is to include `npm` libraries. `bun` includes a feature whereby it will install dependency packages from `package.json` automatically the first time it runs, greatly simplifying the setup. -One thing about the .nix files in this repo is that they in turn pull code (our TypeScript app) from another remote repo. Using this approach, you can separate concerns, placing the deployment .nix files in one repo, and the source app in a separate repo. +One thing about the `.nix` files in this repo is that they in turn pull code (our TypeScript app) from another remote repo. Using this approach, you can separate concerns, placing the deployment `.nix` files in one repo, and the source app in a separate repo. -Here are further details on the individual nix files. +Here are further details on the individual `.nix` files. First we have a flake much like the usual starting point: @@ -1828,7 +1828,7 @@ First we have a flake much like the usual starting point: } ``` -Next is the .nix configuration that installs and configures nginx. This is a simple ngnix configuration, as it simply routes incoming HTTP traffic directly to the app: +Next is the `.nix` configuration that installs and configures nginx. This is a simple nginx configuration, as it simply routes incoming HTTP traffic directly to the app: ```nix # nginx.nix @@ -1868,7 +1868,7 @@ Next is the .nix configuration that installs and configures nginx. This is a sim } ``` -Next, here's the .nix configuration that creates a service that runs the app. +Next, here's the `.nix` configuration that creates a service that runs the app. ```nix # bun-app.nix @@ -1988,16 +1988,16 @@ Nix allows you to run code that's stored remotely in a repo, such as in GitHub. nix profile add 'github:numtide/system-manager' ``` -Or, if you don't have the optional features set in `/opt/nix/nix.conf`, you can provide them through the command line: +Or, if you don't have the optional features set in `/etc/nix/nix.conf`, you can provide them through the command line: ```sh nix profile add 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' ``` !!! Tip - After System Manager is installed locally, you no longer need to worry about whether you have experimental features installed. You will simply pass the --flake option to System Manager. + After System Manager is installed locally, you no longer need to worry about whether you have experimental features installed. You will simply pass the `--flake` option to System Manager. -When you install System Manager, you might get some warnings about trusted user; this simply means you're not in the trusted user list of nix. But System Manager will still install and work fine. +When you install System Manager, you might get some warnings about trusted user; this simply means you're not in the trusted user list of Nix. But System Manager will still install and work fine. Then you can find System Manager: @@ -2014,7 +2014,7 @@ system-manager switch --flake . --sudo !!! Tip - System Manager is still in an early state and undergoing active development. Installing locally will not immediately pick up new changes. If you decide to install locally, you'll want to periodically check our GitHub repo for changes, and re-install it if necessary by using `nix profile upgrade`. + System Manager is still in an early state and undergoing active development. Installing locally will not immediately pick up new changes. If you decide to install locally, you'll want to periodically check our GitHub repo for changes, and upgrade it if necessary by using `nix profile upgrade`. # More stuff, possibly: diff --git a/docs/site/requirements.md b/docs/site/requirements.md index f021997..e3e2ad8 100644 --- a/docs/site/requirements.md +++ b/docs/site/requirements.md @@ -18,20 +18,20 @@ In order to use System Manager, you need: # Installation -Because Nix can load code (called "flakes") remotely, you don't need to download System Manager. Simply running it the first time will automatically install it in what's called the Nix Store, which is a special directory on your system (typically /nix/store) where Nix keeps all packages and their dependencies in isolation. +Because Nix can load code (called "flakes") remotely, you don't need to download System Manager. Simply running it the first time will automatically install it in what's called the Nix Store, which is a special directory on your system (typically `/nix/store`) where Nix keeps all packages and their dependencies in isolation. ## Regarding Experimental Features System Manager requires flakes to run. You can enable flakes using one of two methods: -* By adding the following line to /etc/nix.conf +* By adding the following line to `/etc/nix/nix.conf` ``` experimental-features = nix-command flakes ``` -* Or by adding the --extra-experimental-features option to the `nix` command line like so: +* Or by adding the `--extra-experimental-features` option to the `nix` command line like so: ``` --extra-experimental-features 'nix-command flakes' @@ -40,20 +40,20 @@ experimental-features = nix-command flakes Note, however, that if you use the `init` subcommand to initialize an environment, and you do *not* have experimental features enabled in your `nix.conf` file, you will only get a default `system.nix` file, and not an associated `flake.nix` file. !!! Recommendation - If you need to run the init subcommand, but prefer to pass the `--extra-experimental-features` option to the command line, we recommend at least temporarily adding the aforementioned line to the `nix.conf` file. + If you need to run the `init` subcommand, but prefer to pass the `--extra-experimental-features` option to the command line, we recommend at least temporarily adding the aforementioned line to the `nix.conf` file. !!! Important - This is optional, but ultimately System Manager prefers to manage the nix.conf file for you, after which you can declare experimental features inside the flake.nix file as shown later in [Letting System Manager manage /etc/nix/nix.conf](reference-guide.md#letting-system-manager-manage-etcnixnixconf). + This is optional, but ultimately System Manager prefers to manage the `nix.conf` file for you, after which you can declare experimental features inside the `flake.nix` file as shown later in [Letting System Manager manage `/etc/nix/nix.conf`](reference-guide.md#letting-system-manager-manage-etcnixnixconf). ## Running under sudo -System Manager needs `sudo` access to run. As such, we've provided a command-line option, --sudo, that allows you to grant sudo rights to System Manager. +System Manager needs `sudo` access to run. As such, we've provided a command-line option, `--sudo`, that allows you to grant sudo rights to System Manager. -**System Manager is still in early development, and for now the --sudo commmand line option is required.** +**System Manager is still in early development, and for now the `--sudo` command line option is required.** !!! Note - Adding yourself to Nix's trusted-users configuration won't help here. Trusted users have elevated privileges within the Nix daemon, but System Manager requires root filesystem permissions to modify /etc, manage services, and install system packages. You'll still need to use sudo. + Adding yourself to Nix's trusted-users configuration won't help here. Trusted users have elevated privileges within the Nix daemon, but System Manager requires root filesystem permissions to modify `/etc`, manage services, and install system packages. You'll still need to use sudo. ## How can I tell whether Nix is installed for the whole system or just me? From d465e7d6467ee7cbf506ff404a349758829c14cd Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 12:36:35 +0100 Subject: [PATCH 16/22] docs: improve requirements.md clarity and structure - Remove redundant admonitions that repeated bullet list items - Rename "Installation" to "No Installation Required" for clarity - Add first-run example command with link to getting-started.md - Rename "Regarding Experimental Features" to "Enabling Flakes" - Simplify experimental features section, link to reference-guide.md - Add language identifiers to code blocks (ini, sh, console) - Fix awkward phrasing ("installed off of" -> "installed in") - Remove extra blank lines --- docs/site/requirements.md | 52 +++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/docs/site/requirements.md b/docs/site/requirements.md index e3e2ad8..d6afafe 100644 --- a/docs/site/requirements.md +++ b/docs/site/requirements.md @@ -7,43 +7,42 @@ In order to use System Manager, you need: * **Nix installed system-wide.** (System Manager doesn't work with a per-user installation of Nix) * **Flakes** enabled -!!! Important - System Manager does not work with the single-user installation option for Nix. - -!!! Important - At this time, System Manager requires flakes to be enabled. - !!! Warning Rollback functionality is not yet fully implemented. While you can list and switch between generations manually, automatic rollback on failure is not available. Always test configuration changes in a VM or non-production environment first. -# Installation +# No Installation Required -Because Nix can load code (called "flakes") remotely, you don't need to download System Manager. Simply running it the first time will automatically install it in what's called the Nix Store, which is a special directory on your system (typically `/nix/store`) where Nix keeps all packages and their dependencies in isolation. +Because Nix can load code (called "flakes") remotely, you don't need to download or install System Manager. Simply running it the first time will automatically fetch it into the Nix Store (`/nix/store`), where Nix keeps all packages and their dependencies in isolation. +To get started, run: + +```sh +nix run 'github:numtide/system-manager' -- init +``` -## Regarding Experimental Features +This will create initial configuration files in `~/.config/system-manager/`. See [Getting Started](getting-started.md) for a complete walkthrough. + +## Enabling Flakes System Manager requires flakes to run. You can enable flakes using one of two methods: -* By adding the following line to `/etc/nix/nix.conf` +* By adding the following line to `/etc/nix/nix.conf`: -``` +```ini experimental-features = nix-command flakes ``` -* Or by adding the `--extra-experimental-features` option to the `nix` command line like so: +* Or by passing the `--extra-experimental-features` option to the `nix` command: -``` ---extra-experimental-features 'nix-command flakes' +```sh +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- init ``` -Note, however, that if you use the `init` subcommand to initialize an environment, and you do *not* have experimental features enabled in your `nix.conf` file, you will only get a default `system.nix` file, and not an associated `flake.nix` file. - -!!! Recommendation - If you need to run the `init` subcommand, but prefer to pass the `--extra-experimental-features` option to the command line, we recommend at least temporarily adding the aforementioned line to the `nix.conf` file. +!!! Note + If flakes aren't enabled in `/etc/nix/nix.conf`, the `init` subcommand will only create a `system.nix` file, not a `flake.nix` file. -!!! Important - This is optional, but ultimately System Manager prefers to manage the `nix.conf` file for you, after which you can declare experimental features inside the `flake.nix` file as shown later in [Letting System Manager manage `/etc/nix/nix.conf`](reference-guide.md#letting-system-manager-manage-etcnixnixconf). +!!! Tip + System Manager can manage your `/etc/nix/nix.conf` file for you, allowing you to declare experimental features in your `flake.nix` instead. See [Letting System Manager manage `/etc/nix/nix.conf`](reference-guide.md#letting-system-manager-manage-etcnixnixconf) for details. ## Running under sudo @@ -57,21 +56,20 @@ System Manager needs `sudo` access to run. As such, we've provided a command-lin ## How can I tell whether Nix is installed for the whole system or just me? -Simply type +Simply type: -``` +```sh which nix ``` +If you see it's installed in your home directory, e.g.: -If you see it's installed off of your home directory, e.g.: - -``` +```console /home/username/.nix-profile/bin/nix ``` -Then it's installed just for you. Alternatively, if it's installed for everybody, it will be installed like so: +Then it's installed just for you. Alternatively, if it's installed system-wide, you'll see: -``` +```console /nix/var/nix/profiles/default/bin/nix ``` \ No newline at end of file From f2880ae767c29d17a68f2f895e9bb38e34a54cb8 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 12:45:43 +0100 Subject: [PATCH 17/22] docs: rename requirements.md to install.md, focus on installation - Rename page from "System Requirements" to "Installation" - Add "Installing Nix" section with official multi-user installer - Add "Enabling Flakes" section (required after official install) - Link to nix-install.com for alternative installation options - Add "Checking Your Installation" section for system-wide verification - Update nav and all links to use install.md --- docs/mkdocs.yml | 2 +- docs/site/getting-started.md | 2 +- docs/site/{requirements.md => install.md} | 78 ++++++++++++----------- docs/site/introduction.md | 2 +- 4 files changed, 44 insertions(+), 40 deletions(-) rename docs/site/{requirements.md => install.md} (62%) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 0cc9cb4..5584af1 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -124,7 +124,7 @@ nav: - Home: index.md - Introduction: introduction.md - Getting Started: getting-started.md - - System Requirements: requirements.md + - Installation: install.md - Reference Guide: reference-guide.md - Examples: examples.md - FAQ: faq.md diff --git a/docs/site/getting-started.md b/docs/site/getting-started.md index 8d6f811..ed1a10c 100644 --- a/docs/site/getting-started.md +++ b/docs/site/getting-started.md @@ -6,7 +6,7 @@ If you've heard of NixOS, you've probably heard that it lets you define your ent # Initializing Your System -Before proceeding, ensure you have met all [System Requirements](requirements.md), including enabling flakes. +Before proceeding, ensure you have [installed Nix with flakes enabled](install.md). To get started with System Manager, run the init subcommand, which will create an initial set of files in the `~/.config/system-manager` folder: diff --git a/docs/site/requirements.md b/docs/site/install.md similarity index 62% rename from docs/site/requirements.md rename to docs/site/install.md index d6afafe..d6c8f61 100644 --- a/docs/site/requirements.md +++ b/docs/site/install.md @@ -1,75 +1,79 @@ -# System Requirements +# Installation -In order to use System Manager, you need: +## Requirements + +To use System Manager, you need: * **A Linux machine.** We've tested System Manager with Ubuntu both as standalone and under Windows Subsystem for Linux (WSL). * **At least 12GB Disk Space.** However, we recommend at least 16GB, as you will be very tight for space with under 16GB. (This is primarily due to Nix; if you're using System Manager to configure, for example, small servers on the Cloud, 8GB simply won't be enough.) -* **Nix installed system-wide.** (System Manager doesn't work with a per-user installation of Nix) -* **Flakes** enabled +* **Nix installed system-wide** with flakes enabled. (System Manager doesn't work with a per-user installation of Nix) !!! Warning Rollback functionality is not yet fully implemented. While you can list and switch between generations manually, automatic rollback on failure is not available. Always test configuration changes in a VM or non-production environment first. -# No Installation Required +# Installing Nix -Because Nix can load code (called "flakes") remotely, you don't need to download or install System Manager. Simply running it the first time will automatically fetch it into the Nix Store (`/nix/store`), where Nix keeps all packages and their dependencies in isolation. +If you don't have Nix installed yet, use the official multi-user installer: -To get started, run: +```sh +sh <(curl -L https://nixos.org/nix/install) --daemon +``` + +After installation, open a new terminal or source the profile: ```sh -nix run 'github:numtide/system-manager' -- init +. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ``` -This will create initial configuration files in `~/.config/system-manager/`. See [Getting Started](getting-started.md) for a complete walkthrough. +Verify the installation: -## Enabling Flakes +```sh +nix --version +``` -System Manager requires flakes to run. You can enable flakes using one of two methods: +## Enabling Flakes -* By adding the following line to `/etc/nix/nix.conf`: +The official installer does not enable flakes by default. Add this line to `/etc/nix/nix.conf`: ```ini experimental-features = nix-command flakes ``` -* Or by passing the `--extra-experimental-features` option to the `nix` command: - -```sh -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- init -``` - -!!! Note - If flakes aren't enabled in `/etc/nix/nix.conf`, the `init` subcommand will only create a `system.nix` file, not a `flake.nix` file. +Alternatively, you can pass the `--extra-experimental-features` option to each `nix` command, but this is less convenient. !!! Tip - System Manager can manage your `/etc/nix/nix.conf` file for you, allowing you to declare experimental features in your `flake.nix` instead. See [Letting System Manager manage `/etc/nix/nix.conf`](reference-guide.md#letting-system-manager-manage-etcnixnixconf) for details. + For other installation options (platform-specific guides, CI/CD environments), see [nix-install.com](https://nix-install.com). +## Checking Your Installation -## Running under sudo +To check if Nix is installed system-wide (required for System Manager), run: -System Manager needs `sudo` access to run. As such, we've provided a command-line option, `--sudo`, that allows you to grant sudo rights to System Manager. +```sh +which nix +``` -**System Manager is still in early development, and for now the `--sudo` command line option is required.** +If the output shows a path in your home directory (e.g., `/home/username/.nix-profile/bin/nix`), Nix is installed per-user and won't work with System Manager. A system-wide installation shows `/nix/var/nix/profiles/default/bin/nix`. -!!! Note - Adding yourself to Nix's trusted-users configuration won't help here. Trusted users have elevated privileges within the Nix daemon, but System Manager requires root filesystem permissions to modify `/etc`, manage services, and install system packages. You'll still need to use sudo. +# Running System Manager -## How can I tell whether Nix is installed for the whole system or just me? +Because Nix can load code (called "flakes") remotely, you don't need to download or install System Manager. Simply running it the first time will automatically fetch it into the Nix Store (`/nix/store`). -Simply type: +To get started, run: ```sh -which nix +nix run 'github:numtide/system-manager' -- init ``` -If you see it's installed in your home directory, e.g.: +This will create initial configuration files in `~/.config/system-manager/`. See [Getting Started](getting-started.md) for a complete walkthrough. -```console -/home/username/.nix-profile/bin/nix -``` +## Running under sudo -Then it's installed just for you. Alternatively, if it's installed system-wide, you'll see: +System Manager needs `sudo` access to run. As such, we've provided a command-line option, `--sudo`, that allows you to grant sudo rights to System Manager. -```console -/nix/var/nix/profiles/default/bin/nix -``` \ No newline at end of file +**System Manager is still in early development, and for now the `--sudo` command line option is required.** + +!!! Note + Adding yourself to Nix's trusted-users configuration won't help here. Trusted users have elevated privileges within the Nix daemon, but System Manager requires root filesystem permissions to modify `/etc`, manage services, and install system packages. You'll still need to use sudo. + +!!! Tip + System Manager can manage your `/etc/nix/nix.conf` file for you, allowing you to declare experimental features in your `flake.nix` instead. See [Letting System Manager manage `/etc/nix/nix.conf`](reference-guide.md#letting-system-manager-manage-etcnixnixconf) for details. diff --git a/docs/site/introduction.md b/docs/site/introduction.md index 5734c3a..5454eeb 100644 --- a/docs/site/introduction.md +++ b/docs/site/introduction.md @@ -23,5 +23,5 @@ System Manager is ideal for: ## Next Steps - [Getting Started](getting-started.md) - Configure your first system -- [System Requirements](requirements.md) - Check prerequisites +- [Installation](install.md) - Install Nix and get started - [Examples](examples.md) - See practical use cases From 9b7d4bf44f199da58539af4e7abef629b7f3242f Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 13:36:50 +0100 Subject: [PATCH 18/22] docs: move --sudo explanation from install.md to getting-started.md The --sudo flag is about running System Manager, not installing it. Move the explanation to where users first encounter the switch command, with a clearer description of what it does. --- docs/site/getting-started.md | 5 +++-- docs/site/install.md | 12 ------------ 2 files changed, 3 insertions(+), 14 deletions(-) diff --git a/docs/site/getting-started.md b/docs/site/getting-started.md index ed1a10c..99de773 100644 --- a/docs/site/getting-started.md +++ b/docs/site/getting-started.md @@ -181,10 +181,11 @@ Now add the file to the modules list in `flake.nix` by replacing this modules li Note: By default, `system.nix` includes starter code and some commented out examples, and nothing else. So you can leave it in the list; in its original state, it doesn't do anything. -Next, we'll run System Manager. +Next, we'll run System Manager to apply the configuration. +System Manager needs root privileges to modify `/etc`, manage systemd services, and create system profiles. Use the `--sudo` flag to run these operations via sudo: -``` +```sh nix run 'github:numtide/system-manager' -- switch --flake . --sudo ``` diff --git a/docs/site/install.md b/docs/site/install.md index d6c8f61..e1a2bd0 100644 --- a/docs/site/install.md +++ b/docs/site/install.md @@ -65,15 +65,3 @@ nix run 'github:numtide/system-manager' -- init ``` This will create initial configuration files in `~/.config/system-manager/`. See [Getting Started](getting-started.md) for a complete walkthrough. - -## Running under sudo - -System Manager needs `sudo` access to run. As such, we've provided a command-line option, `--sudo`, that allows you to grant sudo rights to System Manager. - -**System Manager is still in early development, and for now the `--sudo` command line option is required.** - -!!! Note - Adding yourself to Nix's trusted-users configuration won't help here. Trusted users have elevated privileges within the Nix daemon, but System Manager requires root filesystem permissions to modify `/etc`, manage services, and install system packages. You'll still need to use sudo. - -!!! Tip - System Manager can manage your `/etc/nix/nix.conf` file for you, allowing you to declare experimental features in your `flake.nix` instead. See [Letting System Manager manage `/etc/nix/nix.conf`](reference-guide.md#letting-system-manager-manage-etcnixnixconf) for details. From 409fcb8a1e68d3b3a6527f7c87d3069ebb5aa209 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 13:39:24 +0100 Subject: [PATCH 19/22] misc --- README.md | 2 +- docs/site/getting-started.md | 2 +- docs/site/introduction.md | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index e7f7efc..ed29390 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-# system-manager +# System Manager diff --git a/docs/site/getting-started.md b/docs/site/getting-started.md index 99de773..e167aa5 100644 --- a/docs/site/getting-started.md +++ b/docs/site/getting-started.md @@ -1,6 +1,6 @@ # Getting Started -If you've heard of NixOS, you've probably heard that it lets you define your entire system in configuration files and then reproduce that system anywhere with a single command. System Manager brings that same declarative model to any Linux distribution*, with no reinstalling, no switching operating systems, and no special prerequisites beyond having Nix installed. +If you've heard of NixOS, you've probably heard that it lets you define your entire system in configuration files and then reproduce that system anywhere with a single command. System Manager brings that same declarative model to other Linux distribution*, with no reinstalling, no switching operating systems, and no special prerequisites beyond having Nix installed. *Presently System Manager is only tested on Ubuntu, and is limited to only Linux distributions based on systemd. diff --git a/docs/site/introduction.md b/docs/site/introduction.md index 5454eeb..01e4738 100644 --- a/docs/site/introduction.md +++ b/docs/site/introduction.md @@ -1,8 +1,8 @@ # Introduction -**Declarative system configuration for non-NixOS Linux distributions.** +> Declarative system configuration for Linux distributions. -System Manager brings the power of NixOS-style declarative configuration to other Linux distributions running systemd. Define your packages, services, and `/etc` files in Nix, and System Manager ensures your system matches that configuration. +System Manager brings the power of NixOS-style declarative configuration to other Linux distributions. Define your packages, services, and `/etc` files in Nix, and System Manager ensures your system matches that configuration. ## Why System Manager? From aafe36bfa86a8ee3a195d9cf2b5d92d789ed6a44 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 13:43:32 +0100 Subject: [PATCH 20/22] docs: consolidate experimental features documentation into install.md Remove duplicate explanations of Nix flakes/experimental features from getting-started.md and reference-guide.md. These now reference the canonical "Enabling Flakes" section in install.md. --- docs/site/getting-started.md | 3 --- docs/site/reference-guide.md | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/docs/site/getting-started.md b/docs/site/getting-started.md index e167aa5..abf75d7 100644 --- a/docs/site/getting-started.md +++ b/docs/site/getting-started.md @@ -35,9 +35,6 @@ After running the command you will have the following files in your `~/.config/s !!! Tip Because this is your first time running System Manager, Nix will download and build several files, which might take some time. This only happens once, and in the future, System Manager will run very quickly. -!!! Note - If you activate flakes through the command-line, but not through your `/etc/nix/nix.conf` file, then System Manager won't create the initial `flake.nix` file for you. In that case, you can manually create it and paste in the code we provide below, or activate the experimental features (`nix-command` and `flakes`) in `/etc/nix/nix.conf`, and then re-run the System Manager `init` command. - Here are the contents of the files that were created: ## flake.nix diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md index 5713ab1..cfb2ae5 100644 --- a/docs/site/reference-guide.md +++ b/docs/site/reference-guide.md @@ -85,7 +85,7 @@ nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-mana This will create the initial files in `/home/ubuntu/system-manager`. !!! Note - Presently, System Manager requires flakes to be active. If you choose to not include the experimental features line in `/etc/nix/nix.conf` (and instead use the experimental features command line option), then `init` will only create a `system.nix` file, rather than both a `flake.nix` file and `system.nix` file. + System Manager requires flakes to be enabled in `/etc/nix/nix.conf`. See [Enabling Flakes](install.md#enabling-flakes) for setup instructions. ### switch From 9fcb7253fa90154e77bb42902db9a5db4a9a5a89 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 14:05:24 +0100 Subject: [PATCH 21/22] docs: split reference-guide.md into modular reference/ directory Reorganize the monolithic reference-guide.md (~2000 lines) into a structured reference/ subdirectory with focused files: - reference/index.md: Overview linking to all sections - reference/cli.md: CLI commands and local installation - reference/configuration.md: Project setup and workflows - reference/modules.md: Services, packages, /etc, tmpfiles - reference/remote-flakes.md: Remote flake hosting - reference/blueprint.md: Blueprint integration - reference/examples/: Individual example files (timer, docker, postgresql, nginx, nginx-https, custom-app) Also migrates examples from examples.md into the new structure and updates all cross-references. --- docs/mkdocs.yml | 17 +- docs/site/examples.md | 961 --------- docs/site/introduction.md | 2 +- docs/site/reference-guide.md | 2035 ------------------- docs/site/reference/blueprint.md | 191 ++ docs/site/reference/cli.md | 104 + docs/site/reference/configuration.md | 410 ++++ docs/site/reference/examples/custom-app.md | 277 +++ docs/site/reference/examples/docker.md | 129 ++ docs/site/reference/examples/index.md | 29 + docs/site/reference/examples/nginx-https.md | 201 ++ docs/site/reference/examples/nginx.md | 129 ++ docs/site/reference/examples/postgresql.md | 115 ++ docs/site/reference/examples/timer.md | 94 + docs/site/reference/index.md | 34 + docs/site/reference/modules.md | 556 +++++ docs/site/reference/remote-flakes.md | 61 + nix/lib.nix | 9 +- nix/modules/tmpfiles.nix | 31 +- nix/modules/upstream/nixpkgs/default.nix | 11 +- 20 files changed, 2371 insertions(+), 3025 deletions(-) delete mode 100644 docs/site/examples.md delete mode 100644 docs/site/reference-guide.md create mode 100644 docs/site/reference/blueprint.md create mode 100644 docs/site/reference/cli.md create mode 100644 docs/site/reference/configuration.md create mode 100644 docs/site/reference/examples/custom-app.md create mode 100644 docs/site/reference/examples/docker.md create mode 100644 docs/site/reference/examples/index.md create mode 100644 docs/site/reference/examples/nginx-https.md create mode 100644 docs/site/reference/examples/nginx.md create mode 100644 docs/site/reference/examples/postgresql.md create mode 100644 docs/site/reference/examples/timer.md create mode 100644 docs/site/reference/index.md create mode 100644 docs/site/reference/modules.md create mode 100644 docs/site/reference/remote-flakes.md diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 5584af1..fbea190 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -125,6 +125,19 @@ nav: - Introduction: introduction.md - Getting Started: getting-started.md - Installation: install.md - - Reference Guide: reference-guide.md - - Examples: examples.md + - Reference: + - Overview: reference/index.md + - CLI Commands: reference/cli.md + - Configuration: reference/configuration.md + - Modules: reference/modules.md + - Remote Flakes: reference/remote-flakes.md + - Blueprint: reference/blueprint.md + - Examples: + - Overview: reference/examples/index.md + - Timer: reference/examples/timer.md + - Docker: reference/examples/docker.md + - PostgreSQL: reference/examples/postgresql.md + - Nginx: reference/examples/nginx.md + - Nginx HTTPS: reference/examples/nginx-https.md + - Custom App: reference/examples/custom-app.md - FAQ: faq.md diff --git a/docs/site/examples.md b/docs/site/examples.md deleted file mode 100644 index 2025459..0000000 --- a/docs/site/examples.md +++ /dev/null @@ -1,961 +0,0 @@ -# System Manager Examples - -This document provides practical examples of using System Manager to manage system configurations on any Linux distribution. Each example demonstrates different capabilities and use cases. - -## Table of Contents - -1. [Example 1: Installing a Timer](#example-1-installing-a-timer) -2. [Example 2: Installing Docker](#example-2-installing-docker) -3. [Example 3: Software Package Management (Emacs and Others)](#example-3-software-package-management-emacs-and-others) -4. [Example 4: User Management with Userborn (PR #266)](#example-4-user-management-with-userborn-pr-266) - ---- - -## Example 1: Installing a Timer - -This example demonstrates how to install a timer that runs every one minute. First, here's a flake.nix file: - -### flake.nix - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ ./system.nix ]; - - # Optionally specify extraSpecialArgs and overlays - }; - }; -} -``` - -And then here's a system.nix file referenced from within `flake.nix`: - -```nix -{ pkgs, ... }: -{ - nixpkgs.hostPlatform = "x86_64-linux"; - - # Define the timer that fires every minute - systemd.timers.simple-timer = { - enable = true; - description = "Simple timer that runs every minute"; - wantedBy = [ "timers.target" ]; - timerConfig = { - OnCalendar = "minutely"; - Persistent = true; - }; - }; - - # Define the service that the timer triggers - systemd.services.simple-timer = { - enable = true; - description = "Simple timer service"; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${pkgs.bash}/bin/bash -c 'echo \"Timer fired at $(date)\" >> /tmp/simple-timer.log'"; - }; - }; -} -``` - -### Usage - -```bash -# Activate the configuration -nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo -``` - -Then restart the system; the timer will start automatically. - -```bash -# View the file created every one minute -cat /tmp/simple-timer.log -``` - -### Notes - -- The timer will not start automatically until you reboot the system. If you wish to start it manually, you can do so by typing: - -```bash -sudo systemctl start simple-timer.timer -``` - ---- - -## Example 2: Installing Docker - -This example shows how to install Docker and configure it as a systemd service. - -### flake.nix - -```nix -{ - description = "System Manager - Docker Example"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { self, nixpkgs, system-manager }: { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Install Docker and related tools - environment.systemPackages = with nixpkgs.legacyPackages.x86_64-linux; [ - docker - docker-compose - docker-buildx - ]; - - # Docker daemon configuration - environment.etc."docker/daemon.json".text = '' - { - "log-driver": "json-file", - "log-opts": { - "max-size": "10m", - "max-file": "3" - }, - "storage-driver": "overlay2", - "storage-opts": [ - "overlay2.override_kernel_check=true" - ] - } - ''; - - # Create Docker systemd service - systemd.services.docker = { - enable = true; - description = "Docker Application Container Engine"; - documentation = [ "https://docs.docker.com" ]; - after = [ "network-online.target" "firewalld.service" "containerd.service" ]; - wants = [ "network-online.target" ]; - requires = [ "docker.socket" ]; - wantedBy = [ "system-manager.target" ]; - - serviceConfig = { - Type = "notify"; - ExecStart = "${nixpkgs.legacyPackages.x86_64-linux.docker}/bin/dockerd --host=fd://"; - ExecReload = "/bin/kill -s HUP $MAINPID"; - TimeoutStartSec = 0; - RestartSec = 2; - Restart = "always"; - StartLimitBurst = 3; - StartLimitInterval = "60s"; - - # Security settings - LimitNOFILE = 1048576; - LimitNPROC = "infinity"; - LimitCORE = "infinity"; - TasksMax = "infinity"; - Delegate = "yes"; - KillMode = "process"; - OOMScoreAdjust = -500; - }; - }; - - # Docker socket - systemd.sockets.docker = { - enable = true; - description = "Docker Socket for the API"; - wantedBy = [ "sockets.target" ]; - - socketConfig = { - ListenStream = "/var/run/docker.sock"; - SocketMode = "0660"; - SocketUser = "root"; - SocketGroup = "docker"; - }; - }; - - # Create necessary directories and setup - systemd.tmpfiles.rules = [ - "d /var/lib/docker 0710 root root -" - "d /var/run/docker 0755 root root -" - "d /etc/docker 0755 root root -" - ]; - } - ]; - }; - }; -} -``` - -### Usage - -```bash -# Activate the configuration -nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo - -# Check Docker service status -sudo systemctl status docker - -# Test Docker -sudo docker run hello-world - -# Check Docker version -sudo docker --version - -# View Docker logs -sudo journalctl -u docker -f -``` - -### Notes - -- Ensure the `docker` group exists on your system -- Add your user to the docker group: `sudo usermod -aG docker $USER` -- You may need to log out and back in for group changes to take effect -- This example uses the Docker socket for API communication - ---- - -## Example 3: Software Package Management (Emacs and Others) - -This example demonstrates installing software packages like emacs and other development tools. It also shows what happens when you remove a package from the configuration. - -### flake.nix - -```nix -{ - description = "System Manager - Software Package Management"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { self, nixpkgs, system-manager }: { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Install various software packages - environment.systemPackages = with nixpkgs.legacyPackages.x86_64-linux; [ - # Editors - emacs - vim - neovim - - # Development tools - git - tmux - htop - - # Shell utilities - ripgrep - fd - bat - exa - fzf - - # Network tools - curl - wget - - # System tools - tree - ncdu - - # Programming languages - python3 - nodejs - go - ]; - - # Create a configuration file for easy reference - environment.etc."installed-packages.txt".text = '' - Installed packages via system-manager: - - Editors: - - emacs - - vim - - neovim - - Development Tools: - - git - - tmux - - htop - - Shell Utilities: - - ripgrep (rg) - - fd - - bat - - exa - - fzf - - Network Tools: - - curl - - wget - - System Tools: - - tree - - ncdu - - Programming Languages: - - python3 - - nodejs - - go - - These packages are managed by system-manager. - Check /nix/store for the actual installations. - ''; - - # Create a simple systemd service that uses one of the installed packages - systemd.services.software-info = { - enable = true; - description = "Log installed software information"; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - wantedBy = [ "system-manager.target" ]; - script = '' - echo "=== System Manager Software Installation Report ===" > /tmp/software-report.txt - echo "Generated on: $(date)" >> /tmp/software-report.txt - echo "" >> /tmp/software-report.txt - - echo "Emacs version:" >> /tmp/software-report.txt - ${nixpkgs.legacyPackages.x86_64-linux.emacs}/bin/emacs --version | head -n1 >> /tmp/software-report.txt - echo "" >> /tmp/software-report.txt - - echo "Vim version:" >> /tmp/software-report.txt - ${nixpkgs.legacyPackages.x86_64-linux.vim}/bin/vim --version | head -n1 >> /tmp/software-report.txt - echo "" >> /tmp/software-report.txt - - echo "Git version:" >> /tmp/software-report.txt - ${nixpkgs.legacyPackages.x86_64-linux.git}/bin/git --version >> /tmp/software-report.txt - echo "" >> /tmp/software-report.txt - - echo "Python version:" >> /tmp/software-report.txt - ${nixpkgs.legacyPackages.x86_64-linux.python3}/bin/python3 --version >> /tmp/software-report.txt - - echo "Report saved to /tmp/software-report.txt" - cat /tmp/software-report.txt - ''; - }; - } - ]; - }; - }; -} -``` - -### Initial Installation - -```bash -# Activate the configuration -nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo - -# Verify packages are available -which emacs -which vim -which git -which python3 - -# Check the software report -cat /tmp/software-report.txt - -# List installed packages -cat /etc/installed-packages.txt -``` - -### Package Removal Demonstration - -Now let's see what happens when we remove packages. Create a modified version of the flake: - -#### flake-minimal.nix - -```nix -{ - description = "System Manager - Minimal Software Package Management"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = { self, nixpkgs, system-manager }: { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Reduced package list - removed emacs, neovim, and most other tools - environment.systemPackages = with nixpkgs.legacyPackages.x86_64-linux; [ - # Keep only essential tools - vim - git - htop - ]; - - environment.etc."installed-packages.txt".text = '' - Installed packages via system-manager (MINIMAL): - - Editors: - - vim - - Development Tools: - - git - - htop - - Note: Many packages have been removed from the previous configuration. - ''; - - systemd.services.software-info = { - enable = true; - description = "Log installed software information"; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - wantedBy = [ "system-manager.target" ]; - script = '' - echo "=== Minimal Software Installation Report ===" > /tmp/software-report.txt - echo "Generated on: $(date)" >> /tmp/software-report.txt - echo "" >> /tmp/software-report.txt - - echo "Vim version:" >> /tmp/software-report.txt - ${nixpkgs.legacyPackages.x86_64-linux.vim}/bin/vim --version | head -n1 >> /tmp/software-report.txt - echo "" >> /tmp/software-report.txt - - echo "Git version:" >> /tmp/software-report.txt - ${nixpkgs.legacyPackages.x86_64-linux.git}/bin/git --version >> /tmp/software-report.txt - - cat /tmp/software-report.txt - ''; - }; - } - ]; - }; - }; -} -``` - -### Testing Package Removal - -```bash -# First, verify emacs is available with the full configuration -which emacs -# Should output: /nix/store/.../bin/emacs - -emacs --version -# Should show emacs version - -# Now switch to the minimal configuration -nix run 'github:numtide/system-manager' -- switch --flake /path/to/flake-minimal.nix --sudo - -# Try to run emacs again -which emacs -# Should output: (nothing or "emacs not found") - -# Check if the binary still exists in the nix store -ls -la /nix/store/*emacs*/bin/emacs 2>/dev/null || echo "Emacs removed from active profile" - -# The package is no longer in the system PATH -echo $PATH -# You'll notice the emacs store path is no longer included - -# View the updated installed packages list -cat /etc/installed-packages.txt -# Will show only vim, git, and htop -``` - -### What Actually Happens When You Remove a Package? - -When you remove a package from your System Manager configuration and re-run it: - -1. **The package is removed from the system PATH**: The symbolic links in `/nix/var/nix/profiles/system-manager-profiles/*/bin/` will no longer point to the removed package - -2. **The Nix store paths remain**: The actual package files stay in `/nix/store/` until garbage collection - -3. **No files are deleted from `/nix/store` automatically**: System Manager doesn't immediately delete packages to allow rollbacks - -4. **The package becomes eligible for garbage collection**: Once it's not referenced by any profile, running `nix-collect-garbage` will remove it - -5. **Configuration files are updated**: Any `/etc/` files managed by System Manager are updated to reflect the new state - -### Demonstration Script - -Here's a complete script to demonstrate the package removal behavior: - -```bash -#!/bin/bash - -echo "=== System Manager Package Removal Demonstration ===" -echo "" - -# Step 1: Apply full configuration -echo "Step 1: Installing full software suite..." -nix run 'github:numtide/system-manager' -- switch --flake /path/to/full/config --sudo -echo "" - -# Step 2: Verify emacs is available -echo "Step 2: Verifying emacs installation..." -which emacs -emacs --version | head -n 1 -echo "" - -# Step 3: Save the emacs store path -EMACS_PATH=$(which emacs) -echo "Emacs is currently at: $EMACS_PATH" -echo "" - -# Step 4: Apply minimal configuration -echo "Step 3: Switching to minimal configuration (removing emacs)..." -nix run 'github:numtide/system-manager' -- switch --flake /path/to/minimal/config --sudo -echo "" - -# Step 5: Try to find emacs -echo "Step 4: Checking if emacs is still accessible..." -which emacs 2>/dev/null || echo "✓ Emacs is no longer in PATH" -echo "" - -# Step 6: Check if files still exist in nix store -echo "Step 5: Checking if emacs files still exist in nix store..." -if [ -f "$EMACS_PATH" ]; then - echo "✓ Emacs binary still exists at: $EMACS_PATH" - echo " (It will be garbage collected when you run: nix-collect-garbage)" -else - echo "✗ Emacs binary no longer exists" -fi -echo "" - -# Step 7: Show what garbage collection would do -echo "Step 6: Preview what garbage collection would remove..." -nix-store --gc --print-dead | grep emacs | head -n 5 -echo " ... (and possibly more)" -echo "" - -echo "=== Summary ===" -echo "When you remove a package from system-manager:" -echo " 1. ✓ It's removed from your PATH" -echo " 2. ✓ New sessions won't have access to it" -echo " 3. ✓ Store files remain until garbage collection" -echo " 4. ✓ You can rollback to previous configurations" -echo " 5. ✓ Running 'nix-collect-garbage' removes unused packages" -echo "" -echo "The software is effectively UNINSTALLED from your system perspective," -echo "but the files remain for potential rollback until you garbage collect." -``` - -### Key Takeaways - -- **Removing packages from the configuration makes them unavailable** - They won't be in PATH for new shells/sessions -- **The software IS effectively uninstalled** from a user perspective -- **Store files persist for rollback capability** until garbage collection -- **You can always rollback** to previous configurations that had those packages -- **Garbage collection is manual**: Run `nix-collect-garbage` to reclaim disk space - ---- - -## Example 4: User Management with Userborn (PR #266) - -This example demonstrates how to create and manage users using the userborn feature from PR #266. This is currently a work-in-progress feature but shows the future direction of user management in System Manager. - -**Note**: This example is based on PR #266 which is still in draft status. The implementation may change before being merged. - -### flake.nix - -```nix -{ - description = "System Manager - User Management Example"; - - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - # Userborn for user management (from PR #266) - # Note: This is a WIP feature - pin to a specific commit for stability - userborn = { - url = "github:JulienMalka/userborn/stateful-users"; - # For production, pin to a specific commit: - # url = "github:JulienMalka/userborn/6e8f0d00e683049ac727b626552d5eba7f3471ff"; - }; - }; - - outputs = { self, nixpkgs, system-manager, userborn }: { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Enable userborn for user management - services.userborn.enable = true; - services.userborn.package = userborn.packages.x86_64-linux.default; - - # Set stateful users mode - systemd.services.userborn.environment.USERBORN_STATEFUL = "1"; - - # Define users - users.users = { - # Create a developer user - alice = { - isNormalUser = true; - description = "Alice Developer"; - home = "/home/alice"; - createHome = true; - homeMode = "0700"; - - # Set user shell - shell = nixpkgs.legacyPackages.x86_64-linux.bash; - - # Add to groups - extraGroups = [ "wheel" "docker" "networkmanager" ]; - - # Set initial password (will prompt to change on first login) - # Note: In production, use hashedPasswordFile instead - # Generate with: mkpasswd -m sha-512 - # Example hash for password "changeme": - initialHashedPassword = "$6$rounds=656000$YourSalt$HashedPasswordString"; - - # User-specific packages - packages = with nixpkgs.legacyPackages.x86_64-linux; [ - vim - git - tmux - htop - ]; - }; - - # Create a service account user - servicebot = { - isSystemUser = true; - description = "Service Bot Account"; - home = "/var/lib/servicebot"; - createHome = true; - group = "servicebot"; - - # System users typically use nologin - shell = "${nixpkgs.legacyPackages.x86_64-linux.shadow}/bin/nologin"; - }; - - # Create a web developer user - webdev = { - isNormalUser = true; - description = "Web Developer"; - home = "/home/webdev"; - createHome = true; - - shell = nixpkgs.legacyPackages.x86_64-linux.zsh; - extraGroups = [ "www-data" "developers" ]; - - # User-specific packages for web development - packages = with nixpkgs.legacyPackages.x86_64-linux; [ - nodejs - python3 - go - docker-compose - ]; - }; - }; - - # Define groups - users.groups = { - developers = { - gid = 3000; - members = [ "alice" "webdev" ]; - }; - - servicebot = { - gid = 3001; - }; - }; - - # Enable required shell programs - programs.bash.enable = true; - programs.zsh.enable = true; - - # Create user home directory templates - systemd.tmpfiles.rules = [ - "d /home/alice/.config 0700 alice alice -" - "d /home/webdev/.config 0700 webdev webdev -" - "d /var/lib/servicebot 0750 servicebot servicebot -" - ]; - - # Create a welcome message for new users - environment.etc."skel/.bash_profile".text = '' - # Welcome message - echo "Welcome to this system managed by system-manager!" - echo "Your user account is managed declaratively." - echo "" - - # Source bashrc if it exists - if [ -f ~/.bashrc ]; then - source ~/.bashrc - fi - ''; - - environment.etc."skel/.bashrc".text = '' - # Basic bash configuration - export PATH=$HOME/.local/bin:$PATH - - # Aliases - alias ll='ls -alh' - alias la='ls -A' - alias l='ls -CF' - - # Prompt - PS1='\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ ' - ''; - - # Activation script to set up user environments - system.activationScripts.user-setup = { - text = '' - echo "Setting up user environments..." - - # Copy skeleton files to user homes if they don't exist - for user_home in /home/alice /home/webdev; do - if [ -d "$user_home" ]; then - for skel_file in /etc/skel/.bash_profile /etc/skel/.bashrc; do - target="$user_home/$(basename $skel_file)" - if [ ! -f "$target" ]; then - cp "$skel_file" "$target" - chown $(basename $user_home):$(basename $user_home) "$target" - fi - done - fi - done - - echo "User environment setup complete." - ''; - }; - } - ]; - }; - }; -} -``` - -### Usage - -```bash -# Activate the configuration with user management -nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo - -# Verify users were created -id alice -id webdev -id servicebot - -# Check user groups -groups alice -groups webdev - -# List all users (filter for our created users) -cat /etc/passwd | grep -E 'alice|webdev|servicebot' - -# Check home directories -ls -la /home/alice -ls -la /home/webdev -ls -la /var/lib/servicebot - -# Switch to the alice user (requires password) -su - alice - -# As alice user, check available packages -which vim -which git -which tmux -``` - -### Setting Passwords - -For production use, you should use `hashedPasswordFile` instead of hardcoded passwords: - -```nix -users.users.alice = { - # ... other config ... - hashedPasswordFile = "/run/secrets/alice-password"; -}; -``` - -Generate a hashed password: - -```bash -# Generate a hashed password -mkpasswd -m sha-512 - -# Or use this one-liner to create a password file -mkpasswd -m sha-512 | sudo tee /run/secrets/alice-password -sudo chmod 600 /run/secrets/alice-password -``` - -### User Modification Example - -To modify a user, simply update the configuration and re-run System Manager: - -```nix -# Add alice to more groups -users.users.alice = { - # ... existing config ... - extraGroups = [ "wheel" "docker" "networkmanager" "video" "audio" ]; - - # Add more packages - packages = with nixpkgs.legacyPackages.x86_64-linux; [ - vim - git - tmux - htop - # New packages - ripgrep - fd - bat - ]; -}; -``` - -### Removing a User - -To remove a user, simply remove their configuration: - -```nix -# Before: users.users.alice = { ... }; -# After: (remove the alice user block entirely) - -users.users = { - # alice removed - webdev = { - # ... webdev config remains ... - }; - servicebot = { - # ... servicebot config remains ... - }; -}; -``` - -Then re-activate: - -```bash -nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo - -# The user will be removed from /etc/passwd and /etc/shadow -# Note: Home directory may remain and need manual cleanup -``` - -### Important Notes About PR #266 - -1. **Work in Progress**: This PR is still in draft status and the API may change -2. **Userborn Integration**: Requires the userborn package for systemd-sysusers integration -3. **Stateful Users**: The example uses `USERBORN_STATEFUL = "1"` for stateful user management -4. **Password Management**: Use `initialPassword` or `initialHashedPassword` for first-time setup, then users can change their passwords -5. **Activation Scripts**: The PR adds support for `system.activationScripts` which allows custom setup logic - -### Testing the User Creation - -Here's a complete test script: - -```bash -#!/bin/bash - -echo "=== User Management Test Script ===" -echo "" - -# Apply the configuration -echo "Step 1: Creating users..." -nix run 'github:numtide/system-manager' -- switch --flake /path/to/user/example --sudo -echo "" - -# Test user creation -echo "Step 2: Verifying user 'alice' was created..." -if id alice &>/dev/null; then - echo "✓ User alice exists" - echo " UID: $(id -u alice)" - echo " GID: $(id -g alice)" - echo " Groups: $(groups alice)" - echo " Home: $(eval echo ~alice)" - echo " Shell: $(getent passwd alice | cut -d: -f7)" -else - echo "✗ User alice was not created" -fi -echo "" - -# Test system user -echo "Step 3: Verifying system user 'servicebot'..." -if id servicebot &>/dev/null; then - echo "✓ System user servicebot exists" - echo " UID: $(id -u servicebot)" - echo " Shell: $(getent passwd servicebot | cut -d: -f7)" -else - echo "✗ System user servicebot was not created" -fi -echo "" - -# Test groups -echo "Step 4: Verifying groups..." -if getent group developers &>/dev/null; then - echo "✓ Group developers exists" - echo " GID: $(getent group developers | cut -d: -f3)" - echo " Members: $(getent group developers | cut -d: -f4)" -else - echo "✗ Group developers was not created" -fi -echo "" - -# Test home directories -echo "Step 5: Checking home directories..." -for user in alice webdev; do - if [ -d "/home/$user" ]; then - echo "✓ Home directory exists for $user" - ls -ld "/home/$user" - else - echo "✗ Home directory missing for $user" - fi -done -echo "" - -echo "=== Test Complete ===" -``` - -### Advantages of Declarative User Management - -1. **Reproducibility**: User accounts are defined in code -2. **Version Control**: User configurations can be tracked in git -3. **Consistency**: Same user setup across multiple machines -4. **Documentation**: User configuration serves as documentation -5. **Rollback**: Can rollback to previous user configurations - ---- - - - -## Contributing - -If you have additional examples or improvements to these examples, please contribute to the [System Manager repository](https://github.com/numtide/system-manager) or this documentation repository. diff --git a/docs/site/introduction.md b/docs/site/introduction.md index 01e4738..7b1aa88 100644 --- a/docs/site/introduction.md +++ b/docs/site/introduction.md @@ -24,4 +24,4 @@ System Manager is ideal for: - [Getting Started](getting-started.md) - Configure your first system - [Installation](install.md) - Install Nix and get started -- [Examples](examples.md) - See practical use cases +- [Examples](reference/examples/index.md) - See practical use cases diff --git a/docs/site/reference-guide.md b/docs/site/reference-guide.md deleted file mode 100644 index cfb2ae5..0000000 --- a/docs/site/reference-guide.md +++ /dev/null @@ -1,2035 +0,0 @@ -# Reference Guide - -To get the most out of System Manager, we're offering this guide to help you make the best decisions based on your particular situation. - -# Table of Contents - -- [Command Line Usage](#command-line-usage) -- [Command-line Options](#command-line-options) - - [init](#init) - - [switch](#switch) - - [register](#register) - - [build](#build) - - [deactivate](#deactivate) - - [pre-populate](#pre-populate) - - [sudo](#sudo) -- [Setting up a folder and file structure](#setting-up-a-folder-and-file-structure) - - [Deciding on a folder structure](#deciding-on-a-folder-structure) - - [Choosing a location](#choosing-a-location) - - [Choosing a file structure](#choosing-a-file-structure) - - [Dealing with conflicting .nix files](#dealing-with-conflicting-nix-files) -- [Letting System Manager manage `/etc/nix/nix.conf`](#letting-system-manager-manage-etcnixnixconf) -- [Recommended Workflow for Starting Out](#recommended-workflow-for-starting-out) -- [Using System Manager in a non-Interactive Setting](#using-system-manager-in-a-non-interactive-setting) -- [Recommended Workflow if You Already Have Your Nix Files](#recommended-workflow-if-you-already-have-your-nix-files) -- [Building system-manager .nix files](#building-system-manager-nix-files) - - [The Main flake.nix File](#the-main-flakenix-file) -- [Managing System Services](#managing-system-services) - - [Specifying the wantedBy Setting](#specifying-the-wantedby-setting) -- [Managing Software Installations](#managing-software-installations) - - [Example: Installing a couple apps](#example-installing-a-couple-apps) -- [Working With /etc Files Declaratively](#working-with-etc-files-declaratively) - - [Example: Creating a file in /etc](#example-creating-a-file-in-etc) - - [Permissions](#permissions) - - [Users and Groups](#users-and-groups) -- [Supporting System Services with tmp files and folders](#supporting-system-services-with-tmp-files-and-folders) -- [Working with remote flakes](#working-with-remote-flakes) - - [What's a flake.lock file?](#whats-a-flakelock-file) - - [Setting up your project for remote hosting](#setting-up-your-project-for-remote-hosting) - - [When should you update your flake.nix file?](#when-should-you-update-your-flakenix-file) - - [Can't System Manager build flake.lock for me?](#cant-system-manager-build-flakelock-for-me) - - [Ensuring success](#ensuring-success) - - [Running System Manager with a remote flake](#running-system-manager-with-a-remote-flake) -- [Using Blueprint with System Manager](#using-blueprint-with-system-manager) - - [Using multiple configuration files with Blueprint](#using-multiple-configuration-files-with-blueprint) -- [Full Examples](#full-examples) - - [Full Example: Installing PostgreSQL](#full-example-installing-postgresql) - - [Full Example: Installing Nginx](#full-example-installing-nginx) - - [Full Example: Installing Nginx for HTTPS with a Secure Certificate](#full-example-installing-nginx-for-https-with-a-secure-certificate) - - [Full Example: Managing a System that runs Custom Software](#full-example-managing-a-system-that-runs-custom-software) - - [Live example](#live-example) -- [Optional: Installing System Manager Locally](#optional-installing-system-manager-locally) - -# Command Line Usage - -The basic command looks like this: - -```nix -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -This is the most common scenario you'll use. - -## Command-line Options - -### init - -This subcommand creates two initial files for use with System Manager, a fully-functional `flake.nix`, and a `system.nix` file that contains skeleton code. - -#### Command line options - -**path:** The path where to create the files. If the path doesn't exist, it will be created. - -#### Example - -```sh -nix run 'github:numtide/system-manager' -- init -``` - -This will create the initial files in `~/.config/system-manager`. - -```sh -nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-manager' -``` - -This will create the initial files in `/home/ubuntu/system-manager`. - -!!! Note - System Manager requires flakes to be enabled in `/etc/nix/nix.conf`. See [Enabling Flakes](install.md#enabling-flakes) for setup instructions. - -### switch - -The `switch` subcommand builds and activates your configuration immediately, making it both the current running configuration and the default for future boots. Use it whenever you want to apply your changes. - -**Note: Rollbacks are not yet implemented.** - -The following two parameters are currently both required: - -**--flake**: Specifies a flake to use for configuration. - -**--sudo**: Specifies that System Manager can use sudo. - -### register - -The `register` subcommand builds and registers a System Manager configuration, but does not activate it. Compare this to `switch`, which does everything register does, but then activates it. - -### build - -The `build` subcommand builds everything needed for a switch, but does not register it. - -### deactivate - -The `deactivate` deactivates System Manager. - -### pre-populate - -The `pre-populate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. - -### sudo - -The `sudo` subcommand grants sudo access to System Manager, while running under the current user. All created files will be owned by the current user. - -# Setting up a folder and file structure - -Before you begin with System Manager, you'll need to decide on your folder structure. - -!!! Note - If you prefer, you can host all your System Manager configuration files on a remote Git repo (such as GitHub), and then you don't need to worry about where on your computer to store the files. For more info, see [Working with Remote Flakes](#working-with-remote-flakes). - -Technically, you are free to set up your folders and files however you like; System Manager does not enforce any rules, thus allowing you full flexibility. Below are simply some options that we recommend. - -!!! Tip - While you are free to have different System Manager `.nix` files scattered throughout your system, we recommend, if possible, keeping them in a single location simply for organizational purposes. But again, this is just a recommendation and you're not bound by any such rules. - -## Deciding on a folder structure - -You’ll need to choose where your System Manager configuration will live. Here are two main organizational patterns we recommend. - -* **Option A**: A single folder for all your configuration - -A single folder keeps everything together. This offers a clean long-term solution, along with easy version control. It's also convenient for replicating between machines. - -* **Option B**: A separate folder for each use case - -While not as common, it’s entirely possible to organize your System Manager configuration into multiple independent folders, each focused on a specific use case. In this model, you treat each configuration as its own standalone unit, often stored in its own Git repository. - -For example, you might keep: - -* a dedicated configuration folder strictly for managing nginx, - -* another for custom systemd services, - -* another for developer tools, - -* and yet another for monitoring and observability packages. - -In this manner, you can then build up a system by picking and choosing which services you need for a particular machine, and pull each one down from GitHub. - -To make this happen, however, requires careful consideration [as we discuss later](#dealing-with-conflicting-nix-files). - -## Choosing a location - -### Option 1: Your personal ~/.config folder - -If you're managing a system yourself and only you will be using it, one possibility is to put the files in `~/.config/system-manager`. - -This approach keeps everything scoped to you and avoids having to place files under `/etc` and, perhaps most importantly, avoids having to use sudo. Here's an example layout: - -``` -~/.config/system-manager/ - flake.nix - modules/ - default.nix -``` - -!!! Tip - Avoid this location if multiple people use the machine or if this configuration is meant to be shared with a team. Home-directory paths are user-specific and may not make sense across machines. - -### Option 2: A shared `/etc/system-manager` folder (Recommended for multi-user or organizational setups) - -If you are: - -* managing multiple machines, - -* part of a team, - -* deploying configurations in a corporate or server environment, - -* or simply want a clear system-level location, - -then `/etc/system-manager` is a great choice. Among the advantages are consistency across all machines; standard within an organization; and treating system manager as a system-level tool rather than a personal configuration. Here's an example layout: - -``` -/etc/system-manager/ - flake.nix - modules/ - default.nix - services.nix -``` - -## Choosing a file structure - -After choosing where your configuration lives, you must decide how to structure the files inside it. And note that while System Manager does not enforce any rules, we do recommend you maintain consistency, especially if you have multiple locations on your computer where you store System Manager `.nix` files. - -Essentially, you have two options: - -* A single `flake.nix` file - -* A reusable `flake.nix` file with one or more separate configuration files that describe what the system will look like. - -Within Option B, you can also use our open-source Blueprint product to help you manage your files, which we'll cover shortly. - -### Option A: Single `flake.nix` file - -This configuration is ideal for: - -* Small, simple setups - -* Demos and experiments - -* One-off configurations - -Drawback: This approach doesn’t scale well once you need multiple services, multiple hosts, or reusable modules. - -### Option B: Flake file with one or more configuration files - -This is the structure used by most production setups and by NixOS itself. Your arrangement might look like: - -``` -system-manager/ - flake.nix - modules/ - default.nix - services.nix - users.nix -``` - -Or, perhaps you might have separate services, one per file: - -``` -system-manager/ - flake.nix - modules/ - default.nix - service-1.nix - service-2.nix - users.nix -``` - -This also lends itself well to having multiple "recipes". For example, you might want to add nginx and postgres to your system. You might have them preconfigured somewhere, and simply "drop" them in like so: - -``` -system-manager/ - flake.nix - modules/ - default.nix - service-1.nix - service-2.nix - users.nix - nginx.nix - postgres.nix -``` - -!!! Tip - This is the approach we use in our examples in this document. That way each isolated "recipe" is repeatable and can be re-used across multiple systems. - - -### Dealing with conflicting `.nix` files - -If you have multiple flakes throughout your computer, you can run into a situation where one might install some software, and the other might install a different software -- but uninstall what was in the other configuration. - -For example; suppose you have one configuration file that includes this list of apps: - -```nix - environment = { - systemPackages = [ - pkgs.bat - pkgs.nginx - pkgs.mysql84 - ]; -``` - -And you run System Manager, which installs the three apps. - -Then you separately in another folder have another flake with a different configuration and set of apps: - -```nix - environment = { - systemPackages = [ - pkgs.hello - pkgs.postgresql_18 - pkgs.vscode - ]; -``` - -Then in this folder your run System Manager. - -System Manager does not track files, and see this as a changed configuration: - -* The configuration **no longer has** `bat`, `nginx`, and `mysql84`. -* The configuration does have `hello`, `postgresql_18`, and `vscode`. - -The end result is that System Manager will **remove** `bat`, `nginx`, and `mysql84`, and install `hello`, `postgresql_18`, and `vscode`. - -The fix to this problem is to instead have a single main `flake.nix` file, which loads all of the different `.nix` files, allowing you to run System Manager from a single location. - -This is because Nix has the ability to merge together objects in separate files into a single object; the above would then merge into: - -```nix - systemPackages = [ - pkgs.bat - pkgs.nginx - pkgs.mysql84 - pkgs.hello - pkgs.postgresql_18 - pkgs.vscode - ]; -``` - -We describe this technique in [Building System Manager `.nix` Files](#building-system-manager-nix-files). - -# Letting System Manager manage `/etc/nix/nix.conf` - -System Manager can optionally manage your `/etc/nix/nix.conf` file for you. - -If you have an existing `/etc/nix/nix.conf` file, you'll need to delete it if you want System Manager to manage the file; then run System Manager again. From that point on System Manager will manage the file for you, and you should not make changes to it. - -Instead, you'll put the changes in one of your `.nix` files you'll be building to configure System Manager. - -# Recommended Workflow for Starting Out - -As described previously, System Manager wants to manage your `/etc/nix/nix.conf` file for you, after which you can instead place your configurations directly in the `flake.nix` file, including specifying experimental features. - -To do so requires a careful set of steps. Follow these steps precisely when starting out with a fresh system. - -!!! Note - We will first run System Manager to create an initial `flake.nix` and `system.nix` file; we will then delete the `/etc/nix/nix.conf` file, and instead add the flags to the `flake.nix` file. Then we will run System Manager again to start managing your system, including the `/etc/nix/nix.conf` file. - -1. Temporarily run System Manager with `init` with experimental features enabled by including the following line in `/etc/nix/nix.conf`; this way `init` will generate a `flake.nix` file: - -```ini -experimental-features = nix-command flakes -``` - -And then running System Manager with the init subcommand: - -```sh -nix run 'github:numtide/system-manager' -- init -``` - -(For this step, do not simply add the flag for experimental features; otherwise `init` won't create the `flake.nix` file.) - -2. Under `~/.config/system-manager`, edit the `flake.nix` file, replacing this line: - -```nix - modules = [ ./system.nix ]; -``` - -with this: - -```nix -modules = [ - { - nix.settings.experimental-features = "nix-command flakes"; - } - ./system.nix -]; -``` - -3. Delete the `/etc/nix/nix.conf` file, optionally backing it up first: - -```sh -sudo cp /etc/nix/nix.conf /etc/nix/nix_old # optional -sudo rm /etc/nix/nix.conf -``` - -4. Run System Manager to initialize your system, with the experimental flags set this one time in the command-line: - -```sh -cd ~/.config/system-manager -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo -``` - -System Manager is now managing your system for you, including the `/etc/nix/nix.conf` file. And experimental features are required and turned on through the `flake.nix` file, meaning you do not need to include the `--extra-experimental-features` option when you run System Manager: - -``` -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -Next, if you want to make sure experimental features are always on, you can add it to your flake. - -# Using System Manager in a non-Interactive Setting - -If you're running System Manager in a non-interative script, you might run into a problem with the four questions presented when you first run it: - -* Do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.numtide.com' (y/N)? - -* Do you want to permanently mark this value as trusted (y/N)? - -* Do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=' (y/N)? - -* Do you want to permanently mark this value as trusted (y/N)? - -The reason for these questions is Numtide has made pre-built binary versions of System Manager available from our cache, which speeds up performance since your system doesn't have to build System Manager from source. However, this triggers Nix to ask these four questions. You'll most likely want to answer "y" to all four. - -But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the `--accept-flake-config` option like so: - -```sh -nix run 'github:numtide/system-manager' --accept-flake-config --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo -``` - -If you like, you can add these settings into your flake file, such as in the following: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nix.settings.experimental-features = "nix-command flakes"; - nix.settings.extra-substituters = https://cache.numtide.com; - nix.settings.extra-trusted-public-keys = niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=; - } - ./glow.nix - ]; - }; - }; -} -``` - -Remember, however, the flake shows what the system looks like *after* System Manager runs. That means these changes won't affect the first run of System Manager, which in this case is likely through a script. As such, the first time you run System Manager, you'll still need the `--accept-flake-config` option. Then on subsequent runs you don't need the `--accept-flake-config` option. - -# Recommended Workflow if You Already Have Your Nix Files - -If you already have your `.nix` files, you don't need to run the `init` subcommand. Instead, we recommend the following if you're starting out on a clean system: - -1. Remove the `/etc/nix/nix.conf` file. Then, when you run System Manager the first time, System Manager will take control managing this file for you. You can then place any configuration you previously had in the `/etc/nix/nix.conf` file in your `.nix` files. - -2. Run System Manager the first time, and you'll be ready to go. - -As an example, here's a starting `flake.nix` file: - -**flake.nix** -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - nix.settings.experimental-features = "nix-command flakes"; - } - ./glow.nix - ]; - }; - }; -} -``` - -Notice that we've included in the modules list an object that sets experimental features, turning on flakes. - -Now here's the `glow.nix` file referenced above; it simply installs the `glow` command, which is for displaying markdown files in a shell: - -**glow.nix** - -```nix -{ pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - environment.systemPackages = with pkgs; [ - glow - ]; - }; -} -``` - -Go ahead and delete `/etc/nix/nix.conf`: - -```sh -sudo rm /etc/nix/nix.conf -``` - -And now run System Manager. Because you removed `nix.conf`, you'll need to turn on experimental features as a command-line option. - -```sh -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo -``` - -After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can simply run: - -```sh -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - - -# 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`, and `system.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: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ ./system.nix ]; - - # Optionally specify extraSpecialArgs and overlays - }; - }; -} -``` - -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 as `nixpkgs.hostPlatform = "x86_64-linux";` -* `environment`, consisting of - * `systemPackages` - * `etc` -* `systemd.services` -* `systemd.tmpfiles` - -For example, you could then replace the - -```nix -modules = [ ./system.nix ]; -``` - -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: - -```nix -modules = [ - { - nixpkgs.hostPlatform = "x86_64-linux"; - } - ./modules/tree.nix - ./modules/bat.nix -]; -``` - -Then here are the individual "recipe" files. - -**modules/bat.nix** - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.bat - ]; - }; - }; -} -``` - -**modules/tree.nix** - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.tree - ]; - }; - }; -} -``` - -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](#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](#the-main-flakenix-file). - - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - systemd.services.say-hello = { - description = "say-hello"; - enable = true; - wantedBy = [ "system-manager.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - }; - script = '' - ${lib.getBin pkgs.hello}/bin/hello - ''; - }; - }; -} -``` - -Note: - -This line is required in the above example: - -```nix -wantedBy = [ "system-manager.target" ]; -``` - -(There are other options for `wantedBy`; we discuss it in full in our Reference Guide under [Specifying `wantedBy` Setting](./reference-guide.md#specifying-the-wantedby-setting)) - -Activate it using the same nix command as earlier: - -```sh -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -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: - -```systemd -[Unit] -Description=say-hello - -[Service] -Environment="PATH=/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/bin:/nix/store/qqvfnxa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/bin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/bin: -/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/bin:/nix/store/g48529av5z0vcsyl4d2wbh9kl58c7p73-systemd-minimal-258/bin:/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/sbin:/nix/store/qqvfn -xa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/sbin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/sbin:/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/sbin:/nix/store/g48529av5z0vcsyl4d2wbh9 -kl58c7p73-systemd-minimal-258/sbin" -ExecStart=/nix/store/d8rjglbhinylg8v6s780byaa60k6jpz1-unit-script-say-hello-start/bin/say-hello-start -RemainAfterExit=true -Type=oneshot - -[Install] -WantedBy=system-manager.target -``` - -!!! 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`: - -```sh -journalctl -n 20 -``` - -and you can find the following output in it: - -```log -Nov 18 12:12:51 my-ubuntu systemd[1]: Starting say-hello.service - say-hello... -Nov 18 12:12:51 my-ubuntu say-hello-start[3488278]: Hello, world! -Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. -``` - -!!! 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: - -```nix -wantedBy = [ "system-manager.target" ]; -``` - -(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: - -```nix -wantedBy = [ "timers.target" ] -``` - -# 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: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ - ./apps.nix - ]; - }; - }; -} -``` - -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: - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.hello - pkgs.bat - ]; - }; - }; -} -``` - -When you run System Manager, you should have the packages `hello` and `bat` available. - -```console -$ which hello -/run/system-manager/sw/bin//hello -$ which bat -/run/system-manager/sw/bin//bat -``` - -!!! 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: - -```nix -{ - description = "Standalone System Manager configuration"; - inputs = { - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - ({ lib, pkgs, ... }: { - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - environment.systemPackages = [ - pkgs.hello - pkgs.bat - ]; - }; - }) - ]; - }; - }; -} -``` - -# 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: - -```nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - ./files1.nix - ]; - }; - }; -} -``` - -Notice this references a file called `files1.nix`. To create files, you add attributes to the `config.environment.etc` attribute set as follows: - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - etc = { - "test/test2/something.txt" = { - text = '' - This is just a test!! - ''; - mode = "0755"; - user = "ubuntu"; - group = "ubuntu"; - }; - }; - }; - }; -} -``` - -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: - -```console -$ cat /etc/test/test2/something.txt -This is just a test!! -``` - -Note that if you prefer, you can combine the above flake and separate `.nix` file into a single flake like so: - -```nix -{ - description = "Standalone System Manager configuration"; - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - modules = [ - { - config.nixpkgs.hostPlatform = "x86_64-linux"; - config.environment.etc."test/test2/something.txt" = { - text = '' - This is just a test!!! - ''; - mode = "0755"; - user = "ubuntu"; - group = "ubuntu"; - }; - } - ]; - }; - }; -} -``` - -## 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`): - -```nix -with_ownership = { - text = '' - This is just a test! - ''; - mode = "0755"; - uid = 5; - gid = 6; -}; -``` - -And here's an example that uses named user and group (notice we set `user` and `group`): - -```nix -with_ownership2 = { - text = '' - This is just a test! - ''; - mode = "0755"; - user = "nobody"; - group = "users"; -}; -``` - -!!! 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: - -```nix - # Configure systemd tmpfile settings - systemd.tmpfiles = { - rules = [ - "D /var/tmp/system-manager 0755 root root -" - ]; - - settings.sample = { - "/var/tmp/sample".d = { - mode = "0755"; - }; - }; - }; -``` - -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. - -# Working with remote flakes - -Instead of saving your System Manager configuration files locally, you can optionally keep them in a remote Git repository, such as on GitHub. - -!!! Note - This is a great option if you plan to use the files on multiple machines. - -In order to store them on a remote repo, it's imperative that you keep your `flake.lock` file up to date. - -## What's a `flake.lock` file? - -A `flake.lock` file is a JSON file that stores the exact versions of all the inputs your flake file depends on, including things like nixpkgs, System Manager itself, and anything else you might import. Instead of pulling the latest version every time you build, the lock file ensures that the same inputs are used consistently across machines and over time. This makes your configuration reproducible, stable, and rollback-friendly. When you do want to update to new versions, you run a command like `nix flake update`, which refreshes the lock file in a controlled way. - -## Setting up your project for remote hosting - -As you create your flake.nix and set up any supporting files, you'll want to test it out thoroughly before pushing it up to a remote repo. - -For this you have a couple options; one is to test it out on the machine you're currently using. However, we recommend against this, as there might be artifacts on your computer that can interfere with the configuration. - -Instead, we recommend starting with a fresh machine. One option is to spin up an EC2 instance on AWS; another is to open up a Virtual Box session on your computer. - -!!! Important - You'll need to ensure you have at least 16GB of disk space on the virtual machine. If you go with 8GB, you're going to run out of space. - -After starting with a fresh machine, install Nix, copy over your `flake.nix` and supporting files, and test it out. Once you're ready, make sure your `flake.lock` file is up to date. You can create or update the `flake.lock` file by typing: - -```sh -nix flake update -``` - -And make sure you've pushed it up to the repo. (If you don't do this step, Nix will try to build a `flake.lock`, but will be unable to write it to the same location as the other files, and will error out.) - -```sh -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/numtide/system-manager-test#default --sudo -``` - -### When should you update your `flake.lock` file? - -Generally, you only need to update your `flake.lock` file when you want newer versions of your inputs (nixpkgs, etc). Updating isn't necessary for daily use; your configuration will continue to work with the locked versions. But you will want to update your `flake.lock` file in cases such as: - -* You want newer package versions (e.g. newer `btop`, etc.) -* You want security patches -* You've added new inputs to your flakes (in which case you'll be required to update `flake.lock`) -* You're preparing a fresh install and decide this is a good time to upgrade everything - -### Can't System Manager build `flake.lock` for me? - -Yes, but only if the `flake.nix` file is local to your machine. The problem is System Manager will try to write a `flake.lock` file in the same location as the `flake.nix` file, which isn't possible (at this time) with a GitHub repo. - - - -### Ensuring success - -In order to ensure System Manager retrieves the correct `.nix` files from your repo, we recommend including either a branch or a tag along with your repo. - - - -## Running System Manager with a remote flake - -!!! Tip - Before you run this command, we recommend that you nevertheless create a folder to run it from, such as `~/.config/system-manager`. - - -# Using Blueprint with System Manager - -Blueprint is an opinionated library that maps a standard folder structure to flake outputs, allowing you to divide up your flake into individual files across these folders. This allows you to modularize and isolate these files so that they can be maintained individually and even shared across multiple projects. - -Blueprint has built-in support for System Manager, which means: - -* You do not need to call `system-manager.lib.makeSystemConfig`; Blueprint calls this for you -* You must follow Blueprint's folder structure by placing your files under the `hosts` folder, and you must name your files `system-configuration.nix`. -* You can have multiple folders under the `hosts` folder (but one level deep), and you can access these using the standard nix specifier, e.g. `.#folder-name`. - -In this section we show you how to use Blueprint with System Manager. - -Blueprint provides its own initialization that you can start with if you don't already have a `flake.nix` file using Blueprint. The command to type is: - -```sh -nix flake init -t github:numtide/blueprint -``` - -This results in the following flake: - -```nix -{ - description = "Simple flake with a devshell"; - - # Add all your dependencies here - inputs = { - nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; - blueprint.url = "github:numtide/blueprint"; - blueprint.inputs.nixpkgs.follows = "nixpkgs"; - }; - - # Load the blueprint - outputs = inputs: inputs.blueprint { inherit inputs; }; -} -``` - -Now add System Manager to its inputs section: - -```nix - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; -``` - -Next, create a folder called `hosts`, and under that a folder called `default`: - -```sh -mkdir -p hosts/default -cd hosts/default -``` - -Inside `default` is where you'll put your configuration file. - -**This configuration file must be named `system-configuration.nix`.** - -For example, here's a configuration file that installs `bat`: - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.bat - ]; - }; - }; -} -``` - -!!! Note - Notice that we need to include `nixpkgs.hostPlatform` in this file, as there's no place to include it in the parent `flake.nix` file. - -Now return to the folder two levels up (the one containing `flake.nix`) and you can run System Manager: - -```sh -nix run 'github:numtide/system-manager' -- switch --flake . --sudo -``` - -!!! Remember - As mentioned elsewhere, if this is the first time running System Manager on this computer, you'll need to log out and log back in to pick up the new path. - -Then you should find `bat` on your path: - -```console -$ which bat -/run/system-manager/sw/bin//bat -``` - -The default folder is called `default`; you can also refer to folders by name as mentioned earlier. - -If, for example, under the `hosts` folder you have a folder called `tree`, and inside `tree` you create a file called `system-configuration.nix` with the following contents: - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.tree - ]; - }; - }; -} -``` - -Then you can choose to install `tree` by specifying the `tree` folder like so: - -```sh -nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo -``` - -## Using multiple configuration files with Blueprint - -If you want to load multiple configuration files at once, you can create a special `system-configuration.nix` file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under `hosts`; for example, you might name it `cli-tools`. Starting in the folder with `flake.nix`: - -```sh -mkdir -p hosts/cli-tools/modules -``` - -Then, inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: - -```nix -{ config, lib, pkgs, ... }: -{ - # Import all your modular configs - they auto-merge! ✨ - imports = [ - ./modules/tldr.nix - ./modules/cowsay.nix - ]; - - # Base configuration that applies to everything - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - }; -} -``` - -(Notice this time we can put the `nixpkgs.hostPlatform` in a single place. As such we won't need it in the configuration files.) - -Now move into the `modules` folder: - -```sh -cd modules -``` - -And create two files here: - -tldr.nix: - -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.tldr - ]; - }; - }; -} -``` - -cowsay.nix: -```nix -{ lib, pkgs, ... }: -{ - config = { - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.cowsay - ]; - }; - }; -} -``` - -Now you can return to the top level where your `flake.nix` file is and run these two configuration files: - -```sh -nix run 'github:numtide/system-manager' -- switch --flake '.#cli-tools' --sudo -``` - -This means if you want to include various recipes, you can easily do so. - - -# Full Examples - -## Full Example: Installing PostgreSQL - -Here's a `.nix` file that installs PostgreSQL. - -Note: System Manager is still in its early state, and doesn't yet have user management, which is a planned feature that will be here soon. As such, for now, before you run this, you'll need to manually create the postgres user. Additionally, go ahead and create two directories and grant the postgres user access to them: - -```sh -# Create postgres user and group -sudo groupadd -r postgres -sudo useradd -r -g postgres -d /var/lib/postgresql -s /bin/bash postgres - -# Create directories with proper permissions -sudo mkdir -p /var/lib/postgresql -sudo chown postgres:postgres /var/lib/postgresql - -sudo mkdir -p /run/postgresql -sudo chown postgres:postgres /run/postgresql -``` - -Here, then, is the `.nix` file. - -```nix -{ config, lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - environment.systemPackages = with pkgs; [ - postgresql_16 - ]; - - # PostgreSQL service - systemd.services.postgresql = { - description = "PostgreSQL database server"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - - serviceConfig = { - Type = "notify"; - User = "postgres"; - Group = "postgres"; - ExecStart = "${pkgs.postgresql_16}/bin/postgres -D /var/lib/postgresql/16"; - ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; - KillMode = "mixed"; - KillSignal = "SIGINT"; - TimeoutSec = 120; - - # Create directories and initialize database - ExecStartPre = [ - "${pkgs.coreutils}/bin/mkdir -p /var/lib/postgresql/16" - "${pkgs.bash}/bin/bash -c 'if [ ! -d /var/lib/postgresql/16/base ]; then ${pkgs.postgresql_16}/bin/initdb -D /var/lib/postgresql/16; fi'" - ]; - }; - - environment = { - PGDATA = "/var/lib/postgresql/16"; - }; - }; - - # Initialize database and user - systemd.services.postgresql-init = { - description = "Initialize PostgreSQL database for myapp"; - after = [ "postgresql.service" ]; - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - Type = "oneshot"; - RemainAfterExit = true; - User = "postgres"; - }; - script = '' - # Wait for PostgreSQL to be ready - until ${pkgs.postgresql_16}/bin/pg_isready; do - echo "Waiting for PostgreSQL..." - sleep 2 - done - - # Optional: Create database if it doesn't exist - ${pkgs.postgresql_16}/bin/psql -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw myapp || \ - ${pkgs.postgresql_16}/bin/createdb myapp - - # Optional: Create user if it doesn't exist - ${pkgs.postgresql_16}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='myapp'" | ${pkgs.gnugrep}/bin/grep -q 1 || \ - ${pkgs.postgresql_16}/bin/createuser myapp - - # Grant database privileges - ${pkgs.postgresql_16}/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp" - - # Grant schema privileges (allows creating tables!) - ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON SCHEMA public TO myapp" - ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL TABLES IN SCHEMA public TO myapp" - ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO myapp" - - echo "PostgreSQL is ready and configured!" - ''; - }; - }; -} -``` - -## Full Example: Installing Nginx - -Here's a `.nix` file that installs and configures nginx as a system service. Note that this version only supports HTTP and not HTTPS; later we provide an example that includes HTTPS. - -!!! Tip - This is simply an example to help you learn how to use System Manager. The usual way to install nginx under Nix is to use the [nginx package](https://search.nixos.org/packages?channel=25.11&show=nginx&query=nginx). - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Enable and configure services - services = { - nginx.enable = true; - }; - - environment = { - # Packages that should be installed on a system - systemPackages = [ - pkgs.hello - pkgs.mariadb - pkgs.nginx - ]; - - # Add directories and files to `/etc` and set their permissions - etc = { - "nginx/nginx.conf"= { - - user = "root"; - group = "root"; - mode = "0644"; - - text = '' -# The user/group is often set to 'nginx' or 'www-data', -# but for a simple root-only demo, we'll keep the default. -# user nginx; -worker_processes auto; - -# NGINX looks for modules relative to the install prefix, -# but we explicitly point to the Nix store path to be safe. -error_log /var/log/nginx/error.log; -pid /run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include ${pkgs.nginx}/conf/mime.types; - default_type application/octet-stream; - - sendfile on; - keepalive_timeout 65; - - # Basic default server block - server { - listen 80; - server_name localhost; - - # Point the root directory to a standard location or a Nix store path - root ${pkgs.nginx}/html; - - location / { - index index.html; - } - - # Example log files - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - } -} - ''; - - - }; - }; - }; - - # Enable and configure systemd services - systemd.services = { - nginx = { - enable = true; - description = "A high performance web server and reverse proxy server"; - wantedBy = [ "system-manager.target" ]; - preStart = '' - mkdir -p /var/log/nginx - chown -R root:root /var/log/nginx # Ensure permissions are right for root user - ''; - serviceConfig = { - Type = "forking"; - PIDFile = "/run/nginx.pid"; - - # The main binary execution command, pointing to the Nix store path - ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; - - # The command to stop the service gracefully - ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; - - # NGINX needs to run as root to bind to port 80/443 - User = "root"; - Group = "root"; - - # Restart policy for robustness - Restart = "on-failure"; - }; - }; - }; - - - }; -} - -``` - -## Full Example: Installing Nginx for HTTPS with a Secure Certificate - -Here's an example that installs nginx. This example shows places where you would copy in your own secure certificate information. - -```nix -{ lib, pkgs, ... }: -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Enable and configure services - # Commenting this out -- apparently this loads a bunch of nginx service files we don't need or want - #services = { - # nginx.enable = true; - #}; - - environment = { - systemPackages = [ - pkgs.hello - pkgs.mariadb - pkgs.nginx - ]; - - # Add SSL certificate files to /etc - etc = { - # SSL Certificate - "ssl/certs/your-domain.crt" = { - user = "root"; - group = "root"; - mode = "0644"; - # Option 1: Embed the certificate directly - text = '' ------BEGIN CERTIFICATE----- -MIIDwzCCAqugAwIBAgIUXbQ2ie2/2pxLH/okEB4KEbVDqjEwDQYJKoZIhvcNAQEL... ------END CERTIFICATE----- - ''; - # Option 2: Or reference a file from your repo - # source = ./certs/your-domain.crt; - }; - - # SSL Private Key - "ssl/private/your-domain.key" = { - user = "root"; - group = "root"; - mode = "0600"; # Restrict access to private key! - # Option 1: Embed the key directly - text = '' ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5gQjZxG7rYPub.... ------END PRIVATE KEY----- - ''; - # Option 2: Or reference a file from your repo - # source = ./certs/your-domain.key; - }; - - # Optional: Certificate chain/intermediate certificates - # For this demo we're using a self-signed cert; for a real - # one, uncomment below and add your - "ssl/certs/chain.pem" = { - user = "root"; - group = "root"; - mode = "0644"; - text = '' - -----BEGIN CERTIFICATE----- -YOUR_CHAIN_CERTIFICATE_HERE... - -----END CERTIFICATE----- - ''; - #}; - - # Nginx configuration with HTTPS - "nginx/nginx.conf" = { - user = "root"; - group = "root"; - mode = "0644"; - text = '' -worker_processes auto; - -error_log /var/log/nginx/error.log; -pid /run/nginx.pid; - -events { - worker_connections 1024; -} - -http { - include ${pkgs.nginx}/conf/mime.types; - default_type application/octet-stream; - - sendfile on; - keepalive_timeout 65; - - # SSL Settings - ssl_protocols TLSv1.2 TLSv1.3; - ssl_prefer_server_ciphers on; - ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; - - # HTTP Server - Redirect to HTTPS - server { - listen 80; - server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; - - # Redirect all HTTP to HTTPS - return 301 https://$server_name$request_uri; - } - - # HTTPS Server - server { - listen 443 ssl; - server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; - - # SSL Certificate files - ssl_certificate /etc/ssl/certs/your-domain.crt; - ssl_certificate_key /etc/ssl/private/your-domain.key; - - # Optional: Certificate chain - # ssl_trusted_certificate /etc/ssl/certs/chain.pem; - - # Optional: Enable OCSP stapling - ssl_stapling on; - ssl_stapling_verify on; - - # Optional: Enable HSTS (HTTP Strict Transport Security) - add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; - - root ${pkgs.nginx}/html; - - location / { - index index.html; - } - - access_log /var/log/nginx/access.log; - error_log /var/log/nginx/error.log; - } -} - ''; - }; - }; - }; - - systemd.services = { - nginx = { - enable = true; - #description = "A high performance web server and reverse proxy server"; - wantedBy = [ "system-manager.target" ]; - preStart = '' - mkdir -p /var/log/nginx - chown -R root:root /var/log/nginx - - # Verify SSL certificate files exist - if [ ! -f /etc/ssl/certs/your-domain.crt ]; then - echo "ERROR: SSL certificate not found!" - exit 1 - fi - if [ ! -f /etc/ssl/private/your-domain.key ]; then - echo "ERROR: SSL private key not found!" - exit 1 - fi - ''; - serviceConfig = { - Type = "forking"; - PIDFile = "/run/nginx.pid"; - ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; - ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; - User = "root"; - Group = "root"; - Restart = "on-failure"; - }; - }; - }; - }; -} - -``` - -## Full Example: Managing a System that runs Custom Software - -Here's an example where you might have custom web software living in a repository and you want to run the software on a system behind nginx. - -## Live example - -We have a complete example live that you can try out. All you need is a fresh server (such as on Amazon EC2) with at least 16GB memory. (We recommend the latest Ubuntu, with a t3Large instance, with 16GB RAM. Then allow SSH, HTTP traffic, and HTTPS traffic if you plan to build on these examples.) We have two repos: - -1. The sample application - -2. The configuration files - -The configuration files install both nginx and the sample app. - -After you spin up an instance, install nix for all users: - -```sh -sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon -``` - -Next, log out and log back in so that nix is available in the system path. - -And then you can run System Manager and deploy the app with one command: - -```sh -nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake github:frecklefacelabs/system-manager-custom-app-deploy/v1.0.0#default --sudo -``` - -(Remember, the first time System Manager runs, it takes up to five minutes or so to compile everything.) - -!!! Tip - We're specifying a tag in our URL. This is good practice to make sure you get the right version of your flakes. Also, modern Nix supports the use of a protocol called "github", and when you use that protocol, you can specify the tag behind a slash symbol, as we did here for tag v1.0.0. - -!!! Tip - If you make changes to your flakes, be sure to create a new tag. Without it, Nix sometimes refuses to load the "latest version" of the repo, and will insist on using whatever version of your repo it used first. - -Then, the app should be installed, with nginx sitting in front of it, and you should be able to run: - -```sh -curl localhost -``` -And it will print out a friendly JSON message such as: - -```json -{"message":"Welcome to the Bun API!","status":"running","endpoints":["/","/health","/random","/cowsay"]} -``` - -We even included cowsay in this sample, which you can try at `curl localhost/cowsay`. Now even though cowsay is meant for fun, the primary reason is this is a TypeScript app that uses `bun`, and we wanted to demonstrate how easy it is to include `npm` libraries. `bun` includes a feature whereby it will install dependency packages from `package.json` automatically the first time it runs, greatly simplifying the setup. - -One thing about the `.nix` files in this repo is that they in turn pull code (our TypeScript app) from another remote repo. Using this approach, you can separate concerns, placing the deployment `.nix` files in one repo, and the source app in a separate repo. - -Here are further details on the individual `.nix` files. - -First we have a flake much like the usual starting point: - -```nix -# flake.nix -{ - description = "Standalone System Manager configuration"; - - inputs = { - # Specify the source of System Manager and Nixpkgs. - nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; - system-manager = { - url = "github:numtide/system-manager"; - inputs.nixpkgs.follows = "nixpkgs"; - }; - }; - - outputs = - { - self, - nixpkgs, - system-manager, - ... - }: - let - system = "x86_64-linux"; - in - { - systemConfigs.default = system-manager.lib.makeSystemConfig { - - # Specify your system configuration modules here, for example, - # the path to your system.nix. - modules = [ - - { - nix.settings.experimental-features = "nix-command flakes"; - services.myapp.enable = true; - } - ./system.nix - ./nginx.nix - ./bun-app.nix - ]; - - # Optionally specify extraSpecialArgs and overlays - }; - }; -} -``` - -Next is the `.nix` configuration that installs and configures nginx. This is a simple nginx configuration, as it simply routes incoming HTTP traffic directly to the app: - -```nix -# nginx.nix -{ config, lib, pkgs, ... }: -{ - config = { - services.nginx = { - enable = true; - - recommendedGzipSettings = true; - recommendedOptimisation = true; - recommendedProxySettings = true; - recommendedTlsSettings = true; - - virtualHosts."_" = { - default = true; - - locations."/" = { - proxyPass = "http://127.0.0.1:3000"; - extraConfig = '' - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto $scheme; - ''; - }; - - locations."/health" = { - proxyPass = "http://127.0.0.1:3000/health"; - extraConfig = '' - access_log off; - ''; - }; - }; - }; - }; -} -``` - -Next, here's the `.nix` configuration that creates a service that runs the app. - -```nix -# bun-app.nix -{ config, lib, pkgs, ... }: -let - # Fetch the app from GitHub - appSource = pkgs.fetchFromGitHub { - owner = "frecklefacelabs"; - repo = "typescript_app_for_system_manager"; - rev = "v1.0.0"; # Use a tag - sha256 = "sha256-TWt/Y2B7cGxjB9pxMOApt83P29uiCBv5nVT3KyycYEA="; - }; -in -{ - config = { - nixpkgs.hostPlatform = "x86_64-linux"; - - # Install Bun - environment.systemPackages = with pkgs; [ - bun - ]; - - # Simple systemd service - runs Bun directly from Nix store! - systemd.services.bunapp = { - description = "Bun TypeScript Application"; - after = [ "network.target" ]; - wantedBy = [ "multi-user.target" ]; - - serviceConfig = { - Type = "simple"; - User = "ubuntu"; - Group = "ubuntu"; - WorkingDirectory = "${appSource}"; - # Bun will auto-install dependencies from package.json on first run - ExecStart = "${pkgs.bun}/bin/bun run index.ts"; - Restart = "always"; - RestartSec = "10s"; - }; - - environment = { - NODE_ENV = "production"; - }; - }; - }; -} - -``` - -And finally, here's the `index.ts` file; it's just a simple REST app that also makes use of one third-party `npm` library. - -```typescript -import cowsay from "cowsay"; - -const messages = [ - "Hello from System Manager!", - "Bun is blazingly fast! ?", - "Nix + Bun = Easy deployments", - "Making it happen!", - "Nix rocks!" -]; - -const server = Bun.serve({ - port: 3000, - fetch(req) { - const url = new URL(req.url); - - if (url.pathname === "/") { - return new Response(JSON.stringify({ - message: "Welcome to the Bun API!", - status: "running", - endpoints: ["/", "/health", "/random", "/cowsay"] - }), { - headers: { "Content-Type": "application/json" } - }); - } - - if (url.pathname === "/health") { - return new Response(JSON.stringify({ - status: "healthy" - }), { - headers: { "Content-Type": "application/json" } - }); - } - - if (url.pathname === "/random") { - const randomMessage = messages[Math.floor(Math.random() * messages.length)]; - return new Response(JSON.stringify({ - message: randomMessage, - timestamp: new Date().toISOString() - }), { - headers: { "Content-Type": "application/json" } - }); - } - - if (url.pathname === "/cowsay") { - const cow = cowsay.say({ - text: "Deployed with System Manager and Nix!" - }); - return new Response(cow, { - headers: { "Content-Type": "text/plain" } - }); - } - - return new Response("Not Found", { status: 404 }); - }, -}); - -console.log(`? Server running on http://localhost:${server.port}`); -``` - - -# Optional: Installing System Manager Locally - -Nix allows you to run code that's stored remotely in a repo, such as in GitHub. As such, you don't have to install System Manager locally to use it. However, if you want to install locally, you can do so with the following `nix profile` command. - -```sh -nix profile add 'github:numtide/system-manager' -``` - -Or, if you don't have the optional features set in `/etc/nix/nix.conf`, you can provide them through the command line: - -```sh -nix profile add 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -``` - -!!! Tip - After System Manager is installed locally, you no longer need to worry about whether you have experimental features installed. You will simply pass the `--flake` option to System Manager. - -When you install System Manager, you might get some warnings about trusted user; this simply means you're not in the trusted user list of Nix. But System Manager will still install and work fine. - -Then you can find System Manager: - -```console -$ which system-manager -/home/ubuntu/.nix-profile/bin/system-manager -``` - -And you can run System Manager: - -```sh -system-manager switch --flake . --sudo -``` - - -!!! Tip - System Manager is still in an early state and undergoing active development. Installing locally will not immediately pick up new changes. If you decide to install locally, you'll want to periodically check our GitHub repo for changes, and upgrade it if necessary by using `nix profile upgrade`. - - -# More stuff, possibly: - -Inspecting /var/lib/system-manager/state/system-manager-state.json - -Troubleshooting Guide - -Recipes (individual software packages, etc.) - -Package overlays - -Managing a Remote System with System Manager - -Working with Timers - -Managing Users - diff --git a/docs/site/reference/blueprint.md b/docs/site/reference/blueprint.md new file mode 100644 index 0000000..1575fc0 --- /dev/null +++ b/docs/site/reference/blueprint.md @@ -0,0 +1,191 @@ +# Blueprint + +Blueprint is an opinionated library that maps a standard folder structure to flake outputs, allowing you to divide up your flake into individual files across these folders. This allows you to modularize and isolate these files so that they can be maintained individually and even shared across multiple projects. + +Blueprint has built-in support for System Manager, which means: + +* You do not need to call `system-manager.lib.makeSystemConfig`; Blueprint calls this for you +* You must follow Blueprint's folder structure by placing your files under the `hosts` folder, and you must name your files `system-configuration.nix`. +* You can have multiple folders under the `hosts` folder (but one level deep), and you can access these using the standard nix specifier, e.g. `.#folder-name`. + +In this section we show you how to use Blueprint with System Manager. + +Blueprint provides its own initialization that you can start with if you don't already have a `flake.nix` file using Blueprint. The command to type is: + +```sh +nix flake init -t github:numtide/blueprint +``` + +This results in the following flake: + +```nix +{ + description = "Simple flake with a devshell"; + + # Add all your dependencies here + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs?ref=nixos-unstable"; + blueprint.url = "github:numtide/blueprint"; + blueprint.inputs.nixpkgs.follows = "nixpkgs"; + }; + + # Load the blueprint + outputs = inputs: inputs.blueprint { inherit inputs; }; +} +``` + +Now add System Manager to its inputs section: + +```nix + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; +``` + +Next, create a folder called `hosts`, and under that a folder called `default`: + +```sh +mkdir -p hosts/default +cd hosts/default +``` + +Inside `default` is where you'll put your configuration file. + +**This configuration file must be named `system-configuration.nix`.** + +For example, here's a configuration file that installs `bat`: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.bat + ]; + }; + }; +} +``` + +!!! Note + Notice that we need to include `nixpkgs.hostPlatform` in this file, as there's no place to include it in the parent `flake.nix` file. + +Now return to the folder two levels up (the one containing `flake.nix`) and you can run System Manager: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +!!! Remember + As mentioned elsewhere, if this is the first time running System Manager on this computer, you'll need to log out and log back in to pick up the new path. + +Then you should find `bat` on your path: + +```console +$ which bat +/run/system-manager/sw/bin//bat +``` + +The default folder is called `default`; you can also refer to folders by name as mentioned earlier. + +If, for example, under the `hosts` folder you have a folder called `tree`, and inside `tree` you create a file called `system-configuration.nix` with the following contents: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tree + ]; + }; + }; +} +``` + +Then you can choose to install `tree` by specifying the `tree` folder like so: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake '.#tree' --sudo +``` + +## Using multiple configuration files with Blueprint + +If you want to load multiple configuration files at once, you can create a special `system-configuration.nix` file that loads multiple files from a `modules` folder (or any name you choose). To accomplish this, create a folder under `hosts`; for example, you might name it `cli-tools`. Starting in the folder with `flake.nix`: + +```sh +mkdir -p hosts/cli-tools/modules +``` + +Then, inside the `cli-tools` folder, create a `system-configuration.nix` file with the following: + +```nix +{ config, lib, pkgs, ... }: +{ + # Import all your modular configs - they auto-merge! + imports = [ + ./modules/tldr.nix + ./modules/cowsay.nix + ]; + + # Base configuration that applies to everything + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + }; +} +``` + +(Notice this time we can put the `nixpkgs.hostPlatform` in a single place. As such we won't need it in the configuration files.) + +Now move into the `modules` folder: + +```sh +cd modules +``` + +And create two files here: + +tldr.nix: + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tldr + ]; + }; + }; +} +``` + +cowsay.nix: +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.cowsay + ]; + }; + }; +} +``` + +Now you can return to the top level where your `flake.nix` file is and run these two configuration files: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake '.#cli-tools' --sudo +``` + +This means if you want to include various recipes, you can easily do so. diff --git a/docs/site/reference/cli.md b/docs/site/reference/cli.md new file mode 100644 index 0000000..bf2e378 --- /dev/null +++ b/docs/site/reference/cli.md @@ -0,0 +1,104 @@ +# CLI Commands + +The basic command looks like this: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +This is the most common scenario you'll use. + +## Command-line Options + +### init + +This subcommand creates two initial files for use with System Manager, a fully-functional `flake.nix`, and a `system.nix` file that contains skeleton code. + +#### Command line options + +**path:** The path where to create the files. If the path doesn't exist, it will be created. + +#### Example + +```sh +nix run 'github:numtide/system-manager' -- init +``` + +This will create the initial files in `~/.config/system-manager`. + +```sh +nix run 'github:numtide/system-manager' -- init --path='/home/ubuntu/system-manager' +``` + +This will create the initial files in `/home/ubuntu/system-manager`. + +!!! Note + System Manager requires flakes to be enabled in `/etc/nix/nix.conf`. See [Enabling Flakes](../install.md#enabling-flakes) for setup instructions. + +### switch + +The `switch` subcommand builds and activates your configuration immediately, making it both the current running configuration and the default for future boots. Use it whenever you want to apply your changes. + +**Note: Rollbacks are not yet implemented.** + +The following two parameters are currently both required: + +**--flake**: Specifies a flake to use for configuration. + +**--sudo**: Specifies that System Manager can use sudo. + +### register + +The `register` subcommand builds and registers a System Manager configuration, but does not activate it. Compare this to `switch`, which does everything register does, but then activates it. + +### build + +The `build` subcommand builds everything needed for a switch, but does not register it. + +### deactivate + +The `deactivate` deactivates System Manager. + +### pre-populate + +The `pre-populate` subcommand puts all files defined by the given generation in place, but does not start the services. This is useful in scripts. + +### sudo + +The `sudo` subcommand grants sudo access to System Manager, while running under the current user. All created files will be owned by the current user. + +# Optional: Installing System Manager Locally + +Nix allows you to run code that's stored remotely in a repo, such as in GitHub. As such, you don't have to install System Manager locally to use it. However, if you want to install locally, you can do so with the following `nix profile` command. + +```sh +nix profile add 'github:numtide/system-manager' +``` + +Or, if you don't have the optional features set in `/etc/nix/nix.conf`, you can provide them through the command line: + +```sh +nix profile add 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' +``` + +!!! Tip + After System Manager is installed locally, you no longer need to worry about whether you have experimental features installed. You will simply pass the `--flake` option to System Manager. + +When you install System Manager, you might get some warnings about trusted user; this simply means you're not in the trusted user list of Nix. But System Manager will still install and work fine. + +Then you can find System Manager: + +```console +$ which system-manager +/home/ubuntu/.nix-profile/bin/system-manager +``` + +And you can run System Manager: + +```sh +system-manager switch --flake . --sudo +``` + + +!!! Tip + System Manager is still in an early state and undergoing active development. Installing locally will not immediately pick up new changes. If you decide to install locally, you'll want to periodically check our GitHub repo for changes, and upgrade it if necessary by using `nix profile upgrade`. diff --git a/docs/site/reference/configuration.md b/docs/site/reference/configuration.md new file mode 100644 index 0000000..f5568ca --- /dev/null +++ b/docs/site/reference/configuration.md @@ -0,0 +1,410 @@ +# Configuration + +This guide covers how to organize your System Manager project and recommended workflows for different scenarios. + +# Setting up a folder and file structure + +Before you begin with System Manager, you'll need to decide on your folder structure. + +!!! Note + If you prefer, you can host all your System Manager configuration files on a remote Git repo (such as GitHub), and then you don't need to worry about where on your computer to store the files. For more info, see [Working with Remote Flakes](remote-flakes.md). + +Technically, you are free to set up your folders and files however you like; System Manager does not enforce any rules, thus allowing you full flexibility. Below are simply some options that we recommend. + +!!! Tip + While you are free to have different System Manager `.nix` files scattered throughout your system, we recommend, if possible, keeping them in a single location simply for organizational purposes. But again, this is just a recommendation and you're not bound by any such rules. + +## Deciding on a folder structure + +You'll need to choose where your System Manager configuration will live. Here are two main organizational patterns we recommend. + +* **Option A**: A single folder for all your configuration + +A single folder keeps everything together. This offers a clean long-term solution, along with easy version control. It's also convenient for replicating between machines. + +* **Option B**: A separate folder for each use case + +While not as common, it's entirely possible to organize your System Manager configuration into multiple independent folders, each focused on a specific use case. In this model, you treat each configuration as its own standalone unit, often stored in its own Git repository. + +For example, you might keep: + +* a dedicated configuration folder strictly for managing nginx, + +* another for custom systemd services, + +* another for developer tools, + +* and yet another for monitoring and observability packages. + +In this manner, you can then build up a system by picking and choosing which services you need for a particular machine, and pull each one down from GitHub. + +To make this happen, however, requires careful consideration [as we discuss later](#dealing-with-conflicting-nix-files). + +## Choosing a location + +### Option 1: Your personal ~/.config folder + +If you're managing a system yourself and only you will be using it, one possibility is to put the files in `~/.config/system-manager`. + +This approach keeps everything scoped to you and avoids having to place files under `/etc` and, perhaps most importantly, avoids having to use sudo. Here's an example layout: + +``` +~/.config/system-manager/ + flake.nix + modules/ + default.nix +``` + +!!! Tip + Avoid this location if multiple people use the machine or if this configuration is meant to be shared with a team. Home-directory paths are user-specific and may not make sense across machines. + +### Option 2: A shared `/etc/system-manager` folder (Recommended for multi-user or organizational setups) + +If you are: + +* managing multiple machines, + +* part of a team, + +* deploying configurations in a corporate or server environment, + +* or simply want a clear system-level location, + +then `/etc/system-manager` is a great choice. Among the advantages are consistency across all machines; standard within an organization; and treating system manager as a system-level tool rather than a personal configuration. Here's an example layout: + +``` +/etc/system-manager/ + flake.nix + modules/ + default.nix + services.nix +``` + +## Choosing a file structure + +After choosing where your configuration lives, you must decide how to structure the files inside it. And note that while System Manager does not enforce any rules, we do recommend you maintain consistency, especially if you have multiple locations on your computer where you store System Manager `.nix` files. + +Essentially, you have two options: + +* A single `flake.nix` file + +* A reusable `flake.nix` file with one or more separate configuration files that describe what the system will look like. + +Within Option B, you can also use our open-source Blueprint product to help you manage your files, which we'll cover shortly. + +### Option A: Single `flake.nix` file + +This configuration is ideal for: + +* Small, simple setups + +* Demos and experiments + +* One-off configurations + +Drawback: This approach doesn't scale well once you need multiple services, multiple hosts, or reusable modules. + +### Option B: Flake file with one or more configuration files + +This is the structure used by most production setups and by NixOS itself. Your arrangement might look like: + +``` +system-manager/ + flake.nix + modules/ + default.nix + services.nix + users.nix +``` + +Or, perhaps you might have separate services, one per file: + +``` +system-manager/ + flake.nix + modules/ + default.nix + service-1.nix + service-2.nix + users.nix +``` + +This also lends itself well to having multiple "recipes". For example, you might want to add nginx and postgres to your system. You might have them preconfigured somewhere, and simply "drop" them in like so: + +``` +system-manager/ + flake.nix + modules/ + default.nix + service-1.nix + service-2.nix + users.nix + nginx.nix + postgres.nix +``` + +!!! Tip + This is the approach we use in our examples in this document. That way each isolated "recipe" is repeatable and can be re-used across multiple systems. + + +### Dealing with conflicting `.nix` files + +If you have multiple flakes throughout your computer, you can run into a situation where one might install some software, and the other might install a different software -- but uninstall what was in the other configuration. + +For example; suppose you have one configuration file that includes this list of apps: + +```nix + environment = { + systemPackages = [ + pkgs.bat + pkgs.nginx + pkgs.mysql84 + ]; +``` + +And you run System Manager, which installs the three apps. + +Then you separately in another folder have another flake with a different configuration and set of apps: + +```nix + environment = { + systemPackages = [ + pkgs.hello + pkgs.postgresql_18 + pkgs.vscode + ]; +``` + +Then in this folder your run System Manager. + +System Manager does not track files, and see this as a changed configuration: + +* The configuration **no longer has** `bat`, `nginx`, and `mysql84`. +* The configuration does have `hello`, `postgresql_18`, and `vscode`. + +The end result is that System Manager will **remove** `bat`, `nginx`, and `mysql84`, and install `hello`, `postgresql_18`, and `vscode`. + +The fix to this problem is to instead have a single main `flake.nix` file, which loads all of the different `.nix` files, allowing you to run System Manager from a single location. + +This is because Nix has the ability to merge together objects in separate files into a single object; the above would then merge into: + +```nix + systemPackages = [ + pkgs.bat + pkgs.nginx + pkgs.mysql84 + pkgs.hello + pkgs.postgresql_18 + pkgs.vscode + ]; +``` + +We describe this technique in [Building System Manager `.nix` Files](modules.md#building-system-manager-nix-files). + +# Letting System Manager manage `/etc/nix/nix.conf` + +System Manager can optionally manage your `/etc/nix/nix.conf` file for you. + +If you have an existing `/etc/nix/nix.conf` file, you'll need to delete it if you want System Manager to manage the file; then run System Manager again. From that point on System Manager will manage the file for you, and you should not make changes to it. + +Instead, you'll put the changes in one of your `.nix` files you'll be building to configure System Manager. + +# Recommended Workflow for Starting Out + +As described previously, System Manager wants to manage your `/etc/nix/nix.conf` file for you, after which you can instead place your configurations directly in the `flake.nix` file, including specifying experimental features. + +To do so requires a careful set of steps. Follow these steps precisely when starting out with a fresh system. + +!!! Note + We will first run System Manager to create an initial `flake.nix` and `system.nix` file; we will then delete the `/etc/nix/nix.conf` file, and instead add the flags to the `flake.nix` file. Then we will run System Manager again to start managing your system, including the `/etc/nix/nix.conf` file. + +1. Temporarily run System Manager with `init` with experimental features enabled by including the following line in `/etc/nix/nix.conf`; this way `init` will generate a `flake.nix` file: + +```ini +experimental-features = nix-command flakes +``` + +And then running System Manager with the init subcommand: + +```sh +nix run 'github:numtide/system-manager' -- init +``` + +(For this step, do not simply add the flag for experimental features; otherwise `init` won't create the `flake.nix` file.) + +2. Under `~/.config/system-manager`, edit the `flake.nix` file, replacing this line: + +```nix + modules = [ ./system.nix ]; +``` + +with this: + +```nix +modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + } + ./system.nix +]; +``` + +3. Delete the `/etc/nix/nix.conf` file, optionally backing it up first: + +```sh +sudo cp /etc/nix/nix.conf /etc/nix/nix_old # optional +sudo rm /etc/nix/nix.conf +``` + +4. Run System Manager to initialize your system, with the experimental flags set this one time in the command-line: + +```sh +cd ~/.config/system-manager +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +System Manager is now managing your system for you, including the `/etc/nix/nix.conf` file. And experimental features are required and turned on through the `flake.nix` file, meaning you do not need to include the `--extra-experimental-features` option when you run System Manager: + +``` +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +Next, if you want to make sure experimental features are always on, you can add it to your flake. + +# Using System Manager in a non-Interactive Setting + +If you're running System Manager in a non-interative script, you might run into a problem with the four questions presented when you first run it: + +* Do you want to allow configuration setting 'extra-substituters' to be set to 'https://cache.numtide.com' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + +* Do you want to allow configuration setting 'extra-trusted-public-keys' to be set to 'niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=' (y/N)? + +* Do you want to permanently mark this value as trusted (y/N)? + +The reason for these questions is Numtide has made pre-built binary versions of System Manager available from our cache, which speeds up performance since your system doesn't have to build System Manager from source. However, this triggers Nix to ask these four questions. You'll most likely want to answer "y" to all four. + +But doing so can cause problems with a non-interactive script. To run System Manager in a script, you can simply add the `--accept-flake-config` option like so: + +```sh +nix run 'github:numtide/system-manager' --accept-flake-config --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +If you like, you can add these settings into your flake file, such as in the following: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + nix.settings.extra-substituters = https://cache.numtide.com; + nix.settings.extra-trusted-public-keys = niks3.numtide.com-1:DTx8wZduET09hRmMtKdQDxNNthLQETkc/yaX7M4qK0g=; + } + ./glow.nix + ]; + }; + }; +} +``` + +Remember, however, the flake shows what the system looks like *after* System Manager runs. That means these changes won't affect the first run of System Manager, which in this case is likely through a script. As such, the first time you run System Manager, you'll still need the `--accept-flake-config` option. Then on subsequent runs you don't need the `--accept-flake-config` option. + +# Recommended Workflow if You Already Have Your Nix Files + +If you already have your `.nix` files, you don't need to run the `init` subcommand. Instead, we recommend the following if you're starting out on a clean system: + +1. Remove the `/etc/nix/nix.conf` file. Then, when you run System Manager the first time, System Manager will take control managing this file for you. You can then place any configuration you previously had in the `/etc/nix/nix.conf` file in your `.nix` files. + +2. Run System Manager the first time, and you'll be ready to go. + +As an example, here's a starting `flake.nix` file: + +**flake.nix** +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nix.settings.experimental-features = "nix-command flakes"; + } + ./glow.nix + ]; + }; + }; +} +``` + +Notice that we've included in the modules list an object that sets experimental features, turning on flakes. + +Now here's the `glow.nix` file referenced above; it simply installs the `glow` command, which is for displaying markdown files in a shell: + +**glow.nix** + +```nix +{ pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + glow + ]; + }; +} +``` + +Go ahead and delete `/etc/nix/nix.conf`: + +```sh +sudo rm /etc/nix/nix.conf +``` + +And now run System Manager. Because you removed `nix.conf`, you'll need to turn on experimental features as a command-line option. + +```sh +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake . --sudo +``` + +After System Manager runs, you'll have the changes in place (in this case the `glow` command added), and you'll be able to manage features, including experimental features, through your flake. And because you turned on the flakes experimental features, future runs of System Manager no longer need the flags. You can simply run: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` diff --git a/docs/site/reference/examples/custom-app.md b/docs/site/reference/examples/custom-app.md new file mode 100644 index 0000000..3843bfa --- /dev/null +++ b/docs/site/reference/examples/custom-app.md @@ -0,0 +1,277 @@ +# Custom App + +This example shows how to deploy custom web software from a repository and run it behind Nginx. + +## Live example + +We have a complete example live that you can try out. All you need is a fresh server (such as on Amazon EC2) with at least 16GB memory. (We recommend the latest Ubuntu, with a t3Large instance, with 16GB RAM. Then allow SSH, HTTP traffic, and HTTPS traffic if you plan to build on these examples.) We have two repos: + +1. The sample application + +2. The configuration files + +The configuration files install both nginx and the sample app. + +After you spin up an instance, install nix for all users: + +```sh +sh <(curl --proto '=https' --tlsv1.2 -L https://nixos.org/nix/install) --daemon +``` + +Next, log out and log back in so that nix is available in the system path. + +And then you can run System Manager and deploy the app with one command: + +```sh +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake github:frecklefacelabs/system-manager-custom-app-deploy/v1.0.0#default --sudo +``` + +(Remember, the first time System Manager runs, it takes up to five minutes or so to compile everything.) + +!!! Tip + We're specifying a tag in our URL. This is good practice to make sure you get the right version of your flakes. Also, modern Nix supports the use of a protocol called "github", and when you use that protocol, you can specify the tag behind a slash symbol, as we did here for tag v1.0.0. + +!!! Tip + If you make changes to your flakes, be sure to create a new tag. Without it, Nix sometimes refuses to load the "latest version" of the repo, and will insist on using whatever version of your repo it used first. + +Then, the app should be installed, with nginx sitting in front of it, and you should be able to run: + +```sh +curl localhost +``` +And it will print out a friendly JSON message such as: + +```json +{"message":"Welcome to the Bun API!","status":"running","endpoints":["/","/health","/random","/cowsay"]} +``` + +We even included cowsay in this sample, which you can try at `curl localhost/cowsay`. Now even though cowsay is meant for fun, the primary reason is this is a TypeScript app that uses `bun`, and we wanted to demonstrate how easy it is to include `npm` libraries. `bun` includes a feature whereby it will install dependency packages from `package.json` automatically the first time it runs, greatly simplifying the setup. + +One thing about the `.nix` files in this repo is that they in turn pull code (our TypeScript app) from another remote repo. Using this approach, you can separate concerns, placing the deployment `.nix` files in one repo, and the source app in a separate repo. + +## Configuration files + +Here are further details on the individual `.nix` files. + +### flake.nix + +First we have a flake much like the usual starting point: + +```nix +# flake.nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ + + { + nix.settings.experimental-features = "nix-command flakes"; + services.myapp.enable = true; + } + ./system.nix + ./nginx.nix + ./bun-app.nix + ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +### nginx.nix + +Next is the `.nix` configuration that installs and configures nginx. This is a simple nginx configuration, as it simply routes incoming HTTP traffic directly to the app: + +```nix +# nginx.nix +{ config, lib, pkgs, ... }: +{ + config = { + services.nginx = { + enable = true; + + recommendedGzipSettings = true; + recommendedOptimisation = true; + recommendedProxySettings = true; + recommendedTlsSettings = true; + + virtualHosts."_" = { + default = true; + + locations."/" = { + proxyPass = "http://127.0.0.1:3000"; + extraConfig = '' + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + ''; + }; + + locations."/health" = { + proxyPass = "http://127.0.0.1:3000/health"; + extraConfig = '' + access_log off; + ''; + }; + }; + }; + }; +} +``` + +### bun-app.nix + +Next, here's the `.nix` configuration that creates a service that runs the app. + +```nix +# bun-app.nix +{ config, lib, pkgs, ... }: +let + # Fetch the app from GitHub + appSource = pkgs.fetchFromGitHub { + owner = "frecklefacelabs"; + repo = "typescript_app_for_system_manager"; + rev = "v1.0.0"; # Use a tag + sha256 = "sha256-TWt/Y2B7cGxjB9pxMOApt83P29uiCBv5nVT3KyycYEA="; + }; +in +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Install Bun + environment.systemPackages = with pkgs; [ + bun + ]; + + # Simple systemd service - runs Bun directly from Nix store! + systemd.services.bunapp = { + description = "Bun TypeScript Application"; + after = [ "network.target" ]; + wantedBy = [ "multi-user.target" ]; + + serviceConfig = { + Type = "simple"; + User = "ubuntu"; + Group = "ubuntu"; + WorkingDirectory = "${appSource}"; + # Bun will auto-install dependencies from package.json on first run + ExecStart = "${pkgs.bun}/bin/bun run index.ts"; + Restart = "always"; + RestartSec = "10s"; + }; + + environment = { + NODE_ENV = "production"; + }; + }; + }; +} + +``` + +### index.ts (The application) + +And finally, here's the `index.ts` file; it's just a simple REST app that also makes use of one third-party `npm` library. + +```typescript +import cowsay from "cowsay"; + +const messages = [ + "Hello from System Manager!", + "Bun is blazingly fast!", + "Nix + Bun = Easy deployments", + "Making it happen!", + "Nix rocks!" +]; + +const server = Bun.serve({ + port: 3000, + fetch(req) { + const url = new URL(req.url); + + if (url.pathname === "/") { + return new Response(JSON.stringify({ + message: "Welcome to the Bun API!", + status: "running", + endpoints: ["/", "/health", "/random", "/cowsay"] + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/health") { + return new Response(JSON.stringify({ + status: "healthy" + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/random") { + const randomMessage = messages[Math.floor(Math.random() * messages.length)]; + return new Response(JSON.stringify({ + message: randomMessage, + timestamp: new Date().toISOString() + }), { + headers: { "Content-Type": "application/json" } + }); + } + + if (url.pathname === "/cowsay") { + const cow = cowsay.say({ + text: "Deployed with System Manager and Nix!" + }); + return new Response(cow, { + headers: { "Content-Type": "text/plain" } + }); + } + + return new Response("Not Found", { status: 404 }); + }, +}); + +console.log(`Server running on http://localhost:${server.port}`); +``` + +## What this configuration does + +1. **Fetches the application** from GitHub using `pkgs.fetchFromGitHub` +2. **Installs Bun** as a system package +3. **Creates a systemd service** that: + - Runs the TypeScript app using Bun + - Automatically restarts on failure + - Sets the working directory to the fetched source +4. **Configures Nginx** as a reverse proxy to the app on port 3000 + +## Key concepts + +- **Separation of concerns**: The deployment configuration (`.nix` files) lives in one repo, while the application source lives in another +- **Automatic dependency installation**: Bun installs npm dependencies from `package.json` on first run +- **Reproducible deployments**: The `sha256` hash ensures you get the exact version you expect diff --git a/docs/site/reference/examples/docker.md b/docs/site/reference/examples/docker.md new file mode 100644 index 0000000..b90bc92 --- /dev/null +++ b/docs/site/reference/examples/docker.md @@ -0,0 +1,129 @@ +# Docker + +This example shows how to install Docker and configure it as a systemd service. + +## Configuration + +```nix +{ + description = "System Manager - Docker Example"; + + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = { self, nixpkgs, system-manager }: { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Install Docker and related tools + environment.systemPackages = with nixpkgs.legacyPackages.x86_64-linux; [ + docker + docker-compose + docker-buildx + ]; + + # Docker daemon configuration + environment.etc."docker/daemon.json".text = '' + { + "log-driver": "json-file", + "log-opts": { + "max-size": "10m", + "max-file": "3" + }, + "storage-driver": "overlay2", + "storage-opts": [ + "overlay2.override_kernel_check=true" + ] + } + ''; + + # Create Docker systemd service + systemd.services.docker = { + enable = true; + description = "Docker Application Container Engine"; + documentation = [ "https://docs.docker.com" ]; + after = [ "network-online.target" "firewalld.service" "containerd.service" ]; + wants = [ "network-online.target" ]; + requires = [ "docker.socket" ]; + wantedBy = [ "system-manager.target" ]; + + serviceConfig = { + Type = "notify"; + ExecStart = "${nixpkgs.legacyPackages.x86_64-linux.docker}/bin/dockerd --host=fd://"; + ExecReload = "/bin/kill -s HUP $MAINPID"; + TimeoutStartSec = 0; + RestartSec = 2; + Restart = "always"; + StartLimitBurst = 3; + StartLimitInterval = "60s"; + + # Security settings + LimitNOFILE = 1048576; + LimitNPROC = "infinity"; + LimitCORE = "infinity"; + TasksMax = "infinity"; + Delegate = "yes"; + KillMode = "process"; + OOMScoreAdjust = -500; + }; + }; + + # Docker socket + systemd.sockets.docker = { + enable = true; + description = "Docker Socket for the API"; + wantedBy = [ "sockets.target" ]; + + socketConfig = { + ListenStream = "/var/run/docker.sock"; + SocketMode = "0660"; + SocketUser = "root"; + SocketGroup = "docker"; + }; + }; + + # Create necessary directories and setup + systemd.tmpfiles.rules = [ + "d /var/lib/docker 0710 root root -" + "d /var/run/docker 0755 root root -" + "d /etc/docker 0755 root root -" + ]; + } + ]; + }; + }; +} +``` + +## Usage + +```bash +# Activate the configuration +nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo + +# Check Docker service status +sudo systemctl status docker + +# Test Docker +sudo docker run hello-world + +# Check Docker version +sudo docker --version + +# View Docker logs +sudo journalctl -u docker -f +``` + +## Notes + +- Ensure the `docker` group exists on your system +- Add your user to the docker group: `sudo usermod -aG docker $USER` +- You may need to log out and back in for group changes to take effect +- This example uses the Docker socket for API communication diff --git a/docs/site/reference/examples/index.md b/docs/site/reference/examples/index.md new file mode 100644 index 0000000..fe713d7 --- /dev/null +++ b/docs/site/reference/examples/index.md @@ -0,0 +1,29 @@ +# Examples + +Complete, working examples demonstrating System Manager configurations for common use cases. + +## Available Examples + +### [Timer](timer.md) + +Create a simple systemd timer that runs every minute, demonstrating how to set up scheduled tasks. + +### [Docker](docker.md) + +Install Docker and configure it as a systemd service with proper socket activation and daemon configuration. + +### [PostgreSQL](postgresql.md) + +Set up a PostgreSQL database server with automatic initialization, user creation, and proper systemd integration. + +### [Nginx](nginx.md) + +Configure Nginx as a web server with HTTP support, including systemd service management and `/etc` configuration. + +### [Nginx HTTPS](nginx-https.md) + +Extend the Nginx configuration with SSL/TLS certificates for HTTPS support, including certificate management and security best practices. + +### [Custom App](custom-app.md) + +Deploy a custom TypeScript/Bun application behind Nginx, demonstrating how to fetch code from GitHub and run it as a systemd service. Includes a live example you can try. diff --git a/docs/site/reference/examples/nginx-https.md b/docs/site/reference/examples/nginx-https.md new file mode 100644 index 0000000..e6e91f7 --- /dev/null +++ b/docs/site/reference/examples/nginx-https.md @@ -0,0 +1,201 @@ +# Nginx HTTPS + +This example shows how to install Nginx with HTTPS support using SSL certificates. + +## Configuration + +Here's an example that installs nginx with HTTPS. This example shows places where you would copy in your own secure certificate information. + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable and configure services + # Commenting this out -- apparently this loads a bunch of nginx service files we don't need or want + #services = { + # nginx.enable = true; + #}; + + environment = { + systemPackages = [ + pkgs.hello + pkgs.mariadb + pkgs.nginx + ]; + + # Add SSL certificate files to /etc + etc = { + # SSL Certificate + "ssl/certs/your-domain.crt" = { + user = "root"; + group = "root"; + mode = "0644"; + # Option 1: Embed the certificate directly + text = '' +-----BEGIN CERTIFICATE----- +MIIDwzCCAqugAwIBAgIUXbQ2ie2/2pxLH/okEB4KEbVDqjEwDQYJKoZIhvcNAQEL... +-----END CERTIFICATE----- + ''; + # Option 2: Or reference a file from your repo + # source = ./certs/your-domain.crt; + }; + + # SSL Private Key + "ssl/private/your-domain.key" = { + user = "root"; + group = "root"; + mode = "0600"; # Restrict access to private key! + # Option 1: Embed the key directly + text = '' +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC5gQjZxG7rYPub.... +-----END PRIVATE KEY----- + ''; + # Option 2: Or reference a file from your repo + # source = ./certs/your-domain.key; + }; + + # Optional: Certificate chain/intermediate certificates + # For this demo we're using a self-signed cert; for a real + # one, uncomment below and add your + "ssl/certs/chain.pem" = { + user = "root"; + group = "root"; + mode = "0644"; + text = '' + -----BEGIN CERTIFICATE----- +YOUR_CHAIN_CERTIFICATE_HERE... + -----END CERTIFICATE----- + ''; + #}; + + # Nginx configuration with HTTPS + "nginx/nginx.conf" = { + user = "root"; + group = "root"; + mode = "0644"; + text = '' +worker_processes auto; + +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include ${pkgs.nginx}/conf/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + # SSL Settings + ssl_protocols TLSv1.2 TLSv1.3; + ssl_prefer_server_ciphers on; + ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384'; + + # HTTP Server - Redirect to HTTPS + server { + listen 80; + server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; + + # Redirect all HTTP to HTTPS + return 301 https://$server_name$request_uri; + } + + # HTTPS Server + server { + listen 443 ssl; + server_name demo.frecklefacelabs.com www.demo.frecklefacelabs.com; + + # SSL Certificate files + ssl_certificate /etc/ssl/certs/your-domain.crt; + ssl_certificate_key /etc/ssl/private/your-domain.key; + + # Optional: Certificate chain + # ssl_trusted_certificate /etc/ssl/certs/chain.pem; + + # Optional: Enable OCSP stapling + ssl_stapling on; + ssl_stapling_verify on; + + # Optional: Enable HSTS (HTTP Strict Transport Security) + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + + root ${pkgs.nginx}/html; + + location / { + index index.html; + } + + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + } +} + ''; + }; + }; + }; + + systemd.services = { + nginx = { + enable = true; + #description = "A high performance web server and reverse proxy server"; + wantedBy = [ "system-manager.target" ]; + preStart = '' + mkdir -p /var/log/nginx + chown -R root:root /var/log/nginx + + # Verify SSL certificate files exist + if [ ! -f /etc/ssl/certs/your-domain.crt ]; then + echo "ERROR: SSL certificate not found!" + exit 1 + fi + if [ ! -f /etc/ssl/private/your-domain.key ]; then + echo "ERROR: SSL private key not found!" + exit 1 + fi + ''; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/nginx.pid"; + ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; + ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; + User = "root"; + Group = "root"; + Restart = "on-failure"; + }; + }; + }; + }; +} + +``` + +## What this configuration does + +1. **Creates SSL certificate files** in `/etc/ssl/`: + - `/etc/ssl/certs/your-domain.crt` - The public certificate + - `/etc/ssl/private/your-domain.key` - The private key (with restricted permissions) + - `/etc/ssl/certs/chain.pem` - Optional intermediate certificates + +2. **Configures Nginx for HTTPS**: + - Redirects HTTP (port 80) to HTTPS + - Enables TLS 1.2 and 1.3 only + - Uses strong cipher suites + - Enables HSTS for security + +3. **Creates a systemd service** that: + - Verifies SSL certificates exist before starting + - Runs Nginx with the HTTPS configuration + +## Security notes + +- The private key file uses mode `0600` to restrict access +- TLS 1.0 and 1.1 are disabled for security +- HSTS is enabled to enforce HTTPS +- Replace the placeholder certificate content with your actual certificates diff --git a/docs/site/reference/examples/nginx.md b/docs/site/reference/examples/nginx.md new file mode 100644 index 0000000..0ee9c0f --- /dev/null +++ b/docs/site/reference/examples/nginx.md @@ -0,0 +1,129 @@ +# Nginx + +This example shows how to install and configure Nginx as a web server with HTTP support. + +!!! Tip + This is simply an example to help you learn how to use System Manager. The usual way to install nginx under Nix is to use the [nginx package](https://search.nixos.org/packages?channel=25.11&show=nginx&query=nginx). + +## Configuration + +Here's a `.nix` file that installs and configures nginx as a system service. Note that this version only supports HTTP and not HTTPS; see [Nginx HTTPS](nginx-https.md) for an example that includes HTTPS. + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + # Enable and configure services + services = { + nginx.enable = true; + }; + + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.hello + pkgs.mariadb + pkgs.nginx + ]; + + # Add directories and files to `/etc` and set their permissions + etc = { + "nginx/nginx.conf"= { + + user = "root"; + group = "root"; + mode = "0644"; + + text = '' +# The user/group is often set to 'nginx' or 'www-data', +# but for a simple root-only demo, we'll keep the default. +# user nginx; +worker_processes auto; + +# NGINX looks for modules relative to the install prefix, +# but we explicitly point to the Nix store path to be safe. +error_log /var/log/nginx/error.log; +pid /run/nginx.pid; + +events { + worker_connections 1024; +} + +http { + include ${pkgs.nginx}/conf/mime.types; + default_type application/octet-stream; + + sendfile on; + keepalive_timeout 65; + + # Basic default server block + server { + listen 80; + server_name localhost; + + # Point the root directory to a standard location or a Nix store path + root ${pkgs.nginx}/html; + + location / { + index index.html; + } + + # Example log files + access_log /var/log/nginx/access.log; + error_log /var/log/nginx/error.log; + } +} + ''; + + + }; + }; + }; + + # Enable and configure systemd services + systemd.services = { + nginx = { + enable = true; + description = "A high performance web server and reverse proxy server"; + wantedBy = [ "system-manager.target" ]; + preStart = '' + mkdir -p /var/log/nginx + chown -R root:root /var/log/nginx # Ensure permissions are right for root user + ''; + serviceConfig = { + Type = "forking"; + PIDFile = "/run/nginx.pid"; + + # The main binary execution command, pointing to the Nix store path + ExecStart = "${pkgs.nginx}/bin/nginx -c /etc/nginx/nginx.conf"; + + # The command to stop the service gracefully + ExecStop = "${pkgs.nginx}/bin/nginx -s stop"; + + # NGINX needs to run as root to bind to port 80/443 + User = "root"; + Group = "root"; + + # Restart policy for robustness + Restart = "on-failure"; + }; + }; + }; + + + }; +} + +``` + +## What this configuration does + +1. **Installs Nginx** as a system package +2. **Creates `/etc/nginx/nginx.conf`** with a basic HTTP configuration +3. **Creates a systemd service** that: + - Creates the log directory on startup + - Runs Nginx with the custom configuration + - Restarts on failure +4. **Serves the default Nginx welcome page** on port 80 diff --git a/docs/site/reference/examples/postgresql.md b/docs/site/reference/examples/postgresql.md new file mode 100644 index 0000000..2a71b6b --- /dev/null +++ b/docs/site/reference/examples/postgresql.md @@ -0,0 +1,115 @@ +# PostgreSQL + +This example shows how to install and configure PostgreSQL as a systemd service. + +## Prerequisites + +System Manager is still in its early state, and doesn't yet have user management, which is a planned feature that will be here soon. As such, for now, before you run this, you'll need to manually create the postgres user. Additionally, go ahead and create two directories and grant the postgres user access to them: + +```sh +# Create postgres user and group +sudo groupadd -r postgres +sudo useradd -r -g postgres -d /var/lib/postgresql -s /bin/bash postgres + +# Create directories with proper permissions +sudo mkdir -p /var/lib/postgresql +sudo chown postgres:postgres /var/lib/postgresql + +sudo mkdir -p /run/postgresql +sudo chown postgres:postgres /run/postgresql +``` + +## Configuration + +Here's the `.nix` file that installs PostgreSQL. + +```nix +{ config, lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment.systemPackages = with pkgs; [ + postgresql_16 + ]; + + # PostgreSQL service + systemd.services.postgresql = { + description = "PostgreSQL database server"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + + serviceConfig = { + Type = "notify"; + User = "postgres"; + Group = "postgres"; + ExecStart = "${pkgs.postgresql_16}/bin/postgres -D /var/lib/postgresql/16"; + ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID"; + KillMode = "mixed"; + KillSignal = "SIGINT"; + TimeoutSec = 120; + + # Create directories and initialize database + ExecStartPre = [ + "${pkgs.coreutils}/bin/mkdir -p /var/lib/postgresql/16" + "${pkgs.bash}/bin/bash -c 'if [ ! -d /var/lib/postgresql/16/base ]; then ${pkgs.postgresql_16}/bin/initdb -D /var/lib/postgresql/16; fi'" + ]; + }; + + environment = { + PGDATA = "/var/lib/postgresql/16"; + }; + }; + + # Initialize database and user + systemd.services.postgresql-init = { + description = "Initialize PostgreSQL database for myapp"; + after = [ "postgresql.service" ]; + wantedBy = [ "multi-user.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + User = "postgres"; + }; + script = '' + # Wait for PostgreSQL to be ready + until ${pkgs.postgresql_16}/bin/pg_isready; do + echo "Waiting for PostgreSQL..." + sleep 2 + done + + # Optional: Create database if it doesn't exist + ${pkgs.postgresql_16}/bin/psql -lqt | ${pkgs.coreutils}/bin/cut -d \| -f 1 | ${pkgs.gnugrep}/bin/grep -qw myapp || \ + ${pkgs.postgresql_16}/bin/createdb myapp + + # Optional: Create user if it doesn't exist + ${pkgs.postgresql_16}/bin/psql -tAc "SELECT 1 FROM pg_roles WHERE rolname='myapp'" | ${pkgs.gnugrep}/bin/grep -q 1 || \ + ${pkgs.postgresql_16}/bin/createuser myapp + + # Grant database privileges + ${pkgs.postgresql_16}/bin/psql -c "GRANT ALL PRIVILEGES ON DATABASE myapp TO myapp" + + # Grant schema privileges (allows creating tables!) + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON SCHEMA public TO myapp" + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL TABLES IN SCHEMA public TO myapp" + ${pkgs.postgresql_16}/bin/psql -d myapp -c "GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO myapp" + + echo "PostgreSQL is ready and configured!" + ''; + }; + }; +} +``` + +## What this configuration does + +1. **Installs PostgreSQL 16** as a system package +2. **Creates a systemd service** that: + - Runs as the `postgres` user + - Initializes the database directory on first run + - Starts PostgreSQL with the data directory at `/var/lib/postgresql/16` +3. **Creates an initialization service** that: + - Waits for PostgreSQL to be ready + - Creates a database called `myapp` + - Creates a user called `myapp` + - Grants appropriate privileges diff --git a/docs/site/reference/examples/timer.md b/docs/site/reference/examples/timer.md new file mode 100644 index 0000000..59bc40f --- /dev/null +++ b/docs/site/reference/examples/timer.md @@ -0,0 +1,94 @@ +# Timer + +This example demonstrates how to install a systemd timer that runs every minute. + +## Configuration + +### flake.nix + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ ./system.nix ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +### system.nix + +```nix +{ pkgs, ... }: +{ + nixpkgs.hostPlatform = "x86_64-linux"; + + # Define the timer that fires every minute + systemd.timers.simple-timer = { + enable = true; + description = "Simple timer that runs every minute"; + wantedBy = [ "timers.target" ]; + timerConfig = { + OnCalendar = "minutely"; + Persistent = true; + }; + }; + + # Define the service that the timer triggers + systemd.services.simple-timer = { + enable = true; + description = "Simple timer service"; + serviceConfig = { + Type = "oneshot"; + ExecStart = "${pkgs.bash}/bin/bash -c 'echo \"Timer fired at $(date)\" >> /tmp/simple-timer.log'"; + }; + }; +} +``` + +## Usage + +```bash +# Activate the configuration +nix run 'github:numtide/system-manager' -- switch --flake /path/to/this/example --sudo +``` + +Then restart the system; the timer will start automatically. + +```bash +# View the file created every one minute +cat /tmp/simple-timer.log +``` + +## Notes + +- The timer will not start automatically until you reboot the system. If you wish to start it manually, you can do so by typing: + +```bash +sudo systemctl start simple-timer.timer +``` diff --git a/docs/site/reference/index.md b/docs/site/reference/index.md new file mode 100644 index 0000000..48d1c54 --- /dev/null +++ b/docs/site/reference/index.md @@ -0,0 +1,34 @@ +# Reference + +This reference documentation provides detailed information about System Manager's features and configuration options. + +## Sections + +### [CLI Commands](cli.md) + +Command-line usage and all available subcommands: `init`, `switch`, `register`, `build`, `deactivate`, `pre-populate`, and `sudo`. Also covers optional local installation. + +### [Configuration](configuration.md) + +How to organize your System Manager project: folder structure, file organization, workflows for getting started, managing `/etc/nix/nix.conf`, and running in non-interactive settings. + +### [Modules](modules.md) + +Writing `.nix` configuration modules: the `flake.nix` structure, managing systemd services, installing packages, creating `/etc` files, and configuring tmpfiles. + +### [Remote Flakes](remote-flakes.md) + +Hosting your configuration in a Git repository: understanding `flake.lock`, setting up remote hosting, and running System Manager from GitHub. + +### [Blueprint](blueprint.md) + +Using the Blueprint library with System Manager for a standardized project structure. + +### [Examples](examples/index.md) + +Complete, working examples: + +- [PostgreSQL](examples/postgresql.md) - Database server setup +- [Nginx](examples/nginx.md) - HTTP web server +- [Nginx HTTPS](examples/nginx-https.md) - HTTPS with SSL certificates +- [Custom App](examples/custom-app.md) - Deploying a Bun/TypeScript application diff --git a/docs/site/reference/modules.md b/docs/site/reference/modules.md new file mode 100644 index 0000000..2334643 --- /dev/null +++ b/docs/site/reference/modules.md @@ -0,0 +1,556 @@ +# 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`, and `system.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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ ./system.nix ]; + + # Optionally specify extraSpecialArgs and overlays + }; + }; +} +``` + +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 as `nixpkgs.hostPlatform = "x86_64-linux";` +* `environment`, consisting of + * `systemPackages` + * `etc` +* `systemd.services` +* `systemd.tmpfiles` + +For example, you could then replace the + +```nix +modules = [ ./system.nix ]; +``` + +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: + +```nix +modules = [ + { + nixpkgs.hostPlatform = "x86_64-linux"; + } + ./modules/tree.nix + ./modules/bat.nix +]; +``` + +Then here are the individual "recipe" files. + +**modules/bat.nix** + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.bat + ]; + }; + }; +} +``` + +**modules/tree.nix** + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.tree + ]; + }; + }; +} +``` + +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](configuration.md#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](#the-main-flakenix-file). + + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + systemd.services.say-hello = { + description = "say-hello"; + enable = true; + wantedBy = [ "system-manager.target" ]; + serviceConfig = { + Type = "oneshot"; + RemainAfterExit = true; + }; + script = '' + ${lib.getBin pkgs.hello}/bin/hello + ''; + }; + }; +} +``` + +Note: + +This line is required in the above example: + +```nix +wantedBy = [ "system-manager.target" ]; +``` + +(There are other options for `wantedBy`; we discuss it in full under [Specifying `wantedBy` Setting](#specifying-the-wantedby-setting)) + +Activate it using the same nix command as earlier: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake . --sudo +``` + +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: + +```systemd +[Unit] +Description=say-hello + +[Service] +Environment="PATH=/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/bin:/nix/store/qqvfnxa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/bin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/bin: +/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/bin:/nix/store/g48529av5z0vcsyl4d2wbh9kl58c7p73-systemd-minimal-258/bin:/nix/store/xs8scz9w9jp4hpqycx3n3bah5y07ymgj-coreutils-9.8/sbin:/nix/store/qqvfn +xa9jg71wp4hfg1l63r4m78iwvl9-findutils-4.10.0/sbin:/nix/store/22r4s6lqhl43jkazn51f3c18qwk894g4-gnugrep-3.12/sbin:/nix/store/zppkx0lkizglyqa9h26wf495qkllrjgy-gnused-4.9/sbin:/nix/store/g48529av5z0vcsyl4d2wbh9 +kl58c7p73-systemd-minimal-258/sbin" +ExecStart=/nix/store/d8rjglbhinylg8v6s780byaa60k6jpz1-unit-script-say-hello-start/bin/say-hello-start +RemainAfterExit=true +Type=oneshot + +[Install] +WantedBy=system-manager.target +``` + +!!! 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`: + +```sh +journalctl -n 20 +``` + +and you can find the following output in it: + +```log +Nov 18 12:12:51 my-ubuntu systemd[1]: Starting say-hello.service - say-hello... +Nov 18 12:12:51 my-ubuntu say-hello-start[3488278]: Hello, world! +Nov 18 12:12:51 my-ubuntu systemd[1]: Finished say-hello.service - say-hello. +``` + +!!! 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: + +```nix +wantedBy = [ "system-manager.target" ]; +``` + +(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: + +```nix +wantedBy = [ "timers.target" ] +``` + +# 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + # Specify your system configuration modules here, for example, + # the path to your system.nix. + modules = [ + ./apps.nix + ]; + }; + }; +} +``` + +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: + +```nix +{ lib, pkgs, ... }: +{ + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + + environment = { + # Packages that should be installed on a system + systemPackages = [ + pkgs.hello + pkgs.bat + ]; + }; + }; +} +``` + +When you run System Manager, you should have the packages `hello` and `bat` available. + +```console +$ which hello +/run/system-manager/sw/bin//hello +$ which bat +/run/system-manager/sw/bin//bat +``` + +!!! 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: + +```nix +{ + description = "Standalone System Manager configuration"; + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + ({ lib, pkgs, ... }: { + config = { + nixpkgs.hostPlatform = "x86_64-linux"; + environment.systemPackages = [ + pkgs.hello + pkgs.bat + ]; + }; + }) + ]; + }; + }; +} +``` + +# 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: + +```nix +{ + description = "Standalone System Manager configuration"; + + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + ./files1.nix + ]; + }; + }; +} +``` + +Notice this references a file called `files1.nix`. To create files, you add attributes to the `config.environment.etc` attribute set as follows: + +```nix +{ lib, pkgs, ... }: +{ + config = { + environment = { + etc = { + "test/test2/something.txt" = { + text = '' + This is just a test!! + ''; + mode = "0755"; + user = "ubuntu"; + group = "ubuntu"; + }; + }; + }; + }; +} +``` + +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: + +```console +$ cat /etc/test/test2/something.txt +This is just a test!! +``` + +Note that if you prefer, you can combine the above flake and separate `.nix` file into a single flake like so: + +```nix +{ + description = "Standalone System Manager configuration"; + inputs = { + # Specify the source of System Manager and Nixpkgs. + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + system-manager = { + url = "github:numtide/system-manager"; + inputs.nixpkgs.follows = "nixpkgs"; + }; + }; + outputs = + { + self, + nixpkgs, + system-manager, + ... + }: + let + system = "x86_64-linux"; + in + { + systemConfigs.default = system-manager.lib.makeSystemConfig { + modules = [ + { + config.nixpkgs.hostPlatform = "x86_64-linux"; + config.environment.etc."test/test2/something.txt" = { + text = '' + This is just a test!!! + ''; + mode = "0755"; + user = "ubuntu"; + group = "ubuntu"; + }; + } + ]; + }; + }; +} +``` + +## 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`): + +```nix +with_ownership = { + text = '' + This is just a test! + ''; + mode = "0755"; + uid = 5; + gid = 6; +}; +``` + +And here's an example that uses named user and group (notice we set `user` and `group`): + +```nix +with_ownership2 = { + text = '' + This is just a test! + ''; + mode = "0755"; + user = "nobody"; + group = "users"; +}; +``` + +!!! 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: + +```nix + # Configure systemd tmpfile settings + systemd.tmpfiles = { + rules = [ + "D /var/tmp/system-manager 0755 root root -" + ]; + + settings.sample = { + "/var/tmp/sample".d = { + mode = "0755"; + }; + }; + }; +``` + +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. diff --git a/docs/site/reference/remote-flakes.md b/docs/site/reference/remote-flakes.md new file mode 100644 index 0000000..92ddda8 --- /dev/null +++ b/docs/site/reference/remote-flakes.md @@ -0,0 +1,61 @@ +# Remote Flakes + +Instead of saving your System Manager configuration files locally, you can optionally keep them in a remote Git repository, such as on GitHub. + +!!! Note + This is a great option if you plan to use the files on multiple machines. + +In order to store them on a remote repo, it's imperative that you keep your `flake.lock` file up to date. + +## What's a `flake.lock` file? + +A `flake.lock` file is a JSON file that stores the exact versions of all the inputs your flake file depends on, including things like nixpkgs, System Manager itself, and anything else you might import. Instead of pulling the latest version every time you build, the lock file ensures that the same inputs are used consistently across machines and over time. This makes your configuration reproducible, stable, and rollback-friendly. When you do want to update to new versions, you run a command like `nix flake update`, which refreshes the lock file in a controlled way. + +## Setting up your project for remote hosting + +As you create your flake.nix and set up any supporting files, you'll want to test it out thoroughly before pushing it up to a remote repo. + +For this you have a couple options; one is to test it out on the machine you're currently using. However, we recommend against this, as there might be artifacts on your computer that can interfere with the configuration. + +Instead, we recommend starting with a fresh machine. One option is to spin up an EC2 instance on AWS; another is to open up a Virtual Box session on your computer. + +!!! Important + You'll need to ensure you have at least 16GB of disk space on the virtual machine. If you go with 8GB, you're going to run out of space. + +After starting with a fresh machine, install Nix, copy over your `flake.nix` and supporting files, and test it out. Once you're ready, make sure your `flake.lock` file is up to date. You can create or update the `flake.lock` file by typing: + +```sh +nix flake update +``` + +And make sure you've pushed it up to the repo. (If you don't do this step, Nix will try to build a `flake.lock`, but will be unable to write it to the same location as the other files, and will error out.) + +```sh +nix run 'github:numtide/system-manager' --extra-experimental-features 'nix-command flakes' -- switch --flake git+https://github.com/numtide/system-manager-test#default --sudo +``` + +### When should you update your `flake.lock` file? + +Generally, you only need to update your `flake.lock` file when you want newer versions of your inputs (nixpkgs, etc). Updating isn't necessary for daily use; your configuration will continue to work with the locked versions. But you will want to update your `flake.lock` file in cases such as: + +* You want newer package versions (e.g. newer `btop`, etc.) +* You want security patches +* You've added new inputs to your flakes (in which case you'll be required to update `flake.lock`) +* You're preparing a fresh install and decide this is a good time to upgrade everything + +### Can't System Manager build `flake.lock` for me? + +Yes, but only if the `flake.nix` file is local to your machine. The problem is System Manager will try to write a `flake.lock` file in the same location as the `flake.nix` file, which isn't possible (at this time) with a GitHub repo. + + + +### Ensuring success + +In order to ensure System Manager retrieves the correct `.nix` files from your repo, we recommend including either a branch or a tag along with your repo. + + + +## Running System Manager with a remote flake + +!!! Tip + Before you run this command, we recommend that you nevertheless create a folder to run it from, such as `~/.config/system-manager`. diff --git a/nix/lib.nix b/nix/lib.nix index b0d43cb..4b7e830 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -69,8 +69,7 @@ let (lib.evalModules { specialArgs = { nixosModulesPath = "${nixos}/modules"; - } - // extraSpecialArgs; + } // extraSpecialArgs; modules = [ extraArgsModule ./modules @@ -78,8 +77,7 @@ let _file = "${self.printAttrPos (builtins.unsafeGetAttrPos "a" { a = null; })}: inline module"; build = { inherit toplevel; }; } - ] - ++ modules; + ] ++ modules; }).config; inherit (config.nixpkgs) pkgs; @@ -131,8 +129,7 @@ let (linkFarmEntryFromDrv servicesPath) (linkFarmEntryFromDrv etcPath) engineEntry - ] - ++ scripts; + ] ++ scripts; addPassthru = drv: diff --git a/nix/modules/tmpfiles.nix b/nix/modules/tmpfiles.nix index 38be46c..e869380 100644 --- a/nix/modules/tmpfiles.nix +++ b/nix/modules/tmpfiles.nix @@ -187,20 +187,21 @@ in ''; }; }; - systemd.tmpfiles.packages = [ - (pkgs.writeTextFile { - name = "system-manager-tmpfiles.d"; - destination = "/lib/tmpfiles.d/00-system-manager.conf"; - text = '' - # This file is created automatically and should not be modified. - # Please change the option ‘systemd.tmpfiles.rules’ instead. - - ${concatStringsSep "\n" config.systemd.tmpfiles.rules} - ''; - }) - ] - ++ (mapAttrsToList ( - name: paths: pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (mkRuleFileContent paths) - ) config.systemd.tmpfiles.settings); + systemd.tmpfiles.packages = + [ + (pkgs.writeTextFile { + name = "system-manager-tmpfiles.d"; + destination = "/lib/tmpfiles.d/00-system-manager.conf"; + text = '' + # This file is created automatically and should not be modified. + # Please change the option ‘systemd.tmpfiles.rules’ instead. + + ${concatStringsSep "\n" config.systemd.tmpfiles.rules} + ''; + }) + ] + ++ (mapAttrsToList ( + name: paths: pkgs.writeTextDir "lib/tmpfiles.d/${name}.conf" (mkRuleFileContent paths) + ) config.systemd.tmpfiles.settings); }; } diff --git a/nix/modules/upstream/nixpkgs/default.nix b/nix/modules/upstream/nixpkgs/default.nix index c5779e2..3985e2f 100644 --- a/nix/modules/upstream/nixpkgs/default.nix +++ b/nix/modules/upstream/nixpkgs/default.nix @@ -4,11 +4,12 @@ ... }: { - imports = [ - ./nginx.nix - ./nix.nix - ] - ++ + imports = + [ + ./nginx.nix + ./nix.nix + ] + ++ # List of imported NixOS modules # TODO: how will we manage this in the long term? map (path: nixosModulesPath + path) [ From dc51da583eeaae208da794f0012c27dc211030a5 Mon Sep 17 00:00:00 2001 From: zimbatm Date: Tue, 6 Jan 2026 14:24:45 +0100 Subject: [PATCH 22/22] docs: complete 'Running System Manager with a remote flake' section --- docs/site/reference/remote-flakes.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/site/reference/remote-flakes.md b/docs/site/reference/remote-flakes.md index 92ddda8..391afb0 100644 --- a/docs/site/reference/remote-flakes.md +++ b/docs/site/reference/remote-flakes.md @@ -59,3 +59,25 @@ In order to ensure System Manager retrieves the correct `.nix` files from your r !!! Tip Before you run this command, we recommend that you nevertheless create a folder to run it from, such as `~/.config/system-manager`. + +Once your configuration is pushed to a remote repository with a valid `flake.lock`, you can apply it directly: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake github:your-username/your-repo --sudo +``` + +You can also specify a branch or tag: + +```sh +# Using a specific branch +nix run 'github:numtide/system-manager' -- switch --flake github:your-username/your-repo/main --sudo + +# Using a tag +nix run 'github:numtide/system-manager' -- switch --flake github:your-username/your-repo?ref=v1.0.0 --sudo +``` + +For private repositories, use the `git+ssh` protocol: + +```sh +nix run 'github:numtide/system-manager' -- switch --flake git+ssh://git@github.com/your-username/your-repo --sudo +```