Skip to content

Services

NullString1 edited this page Jan 13, 2026 · 1 revision

Services

System and user services configuration in NullOS.

Overview

NullOS configures services at two levels:

  • System services - NixOS configuration (require sudo)
  • User services - Home Manager (run as user)

System Services

Configured in modules/services/ directory.

Audio

Pipewire (modules/services/audio.nix):

services.pipewire = {
  enable = true;
  alsa.enable = true;
  alsa.support32Bit = true;
  pulse.enable = true;
  jack.enable = true;
  
  wireplumber.enable = true;
};

hardware.pulseaudio.enable = false;

Features:

  • Low-latency audio
  • ALSA compatibility
  • PulseAudio replacement
  • JACK support
  • Bluetooth audio

Control:

# Volume control
wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle

# List devices
wpctl status

Display Manager

SDDM (modules/services/sddm.nix):

services.displayManager.sddm = {
  enable = true;
  wayland.enable = true;
  
  theme = "sugar-candy";
  package = pkgs.kdePackages.sddm;
};

Auto-login:

services.displayManager.autoLogin = {
  enable = vars.autoLogin;
  user = vars.username;
};

Desktop Portal

XDG Desktop Portal (modules/services/xdg-portal.nix):

xdg.portal = {
  enable = true;
  
  extraPortals = with pkgs; [
    xdg-desktop-portal-gtk
    xdg-desktop-portal-hyprland
  ];
  
  config.common.default = "*";
};

Provides:

  • Screen sharing
  • File picker
  • Notifications
  • Screenshot portal

Network

NetworkManager:

networking.networkmanager.enable = true;

users.users.${vars.username}.extraGroups = [ "networkmanager" ];

Control:

# CLI
nmcli device wifi list
nmcli device wifi connect SSID password PASSWORD

# GUI
nm-applet  # System tray

Bluetooth

BlueZ:

hardware.bluetooth = {
  enable = true;
  powerOnBoot = true;
  
  settings = {
    General = {
      Enable = "Source,Sink,Media,Socket";
      Experimental = true;
    };
  };
};

services.blueman.enable = true;

Control:

# CLI
bluetoothctl
> scan on
> pair XX:XX:XX:XX:XX:XX
> connect XX:XX:XX:XX:XX:XX

# GUI
blueman-manager

Printing

CUPS:

services.printing = {
  enable = true;
  drivers = with pkgs; [
    gutenprint
    hplip
    splix
  ];
};

services.avahi = {
  enable = true;
  nssmdns4 = true;
  openFirewall = true;
};

Manage:

# Web interface
http://localhost:631/

# CLI
lpstat -p -d
lpr -P printer-name file.pdf

Virtualization

Docker (modules/services/virtualisation.nix):

virtualisation.docker = {
  enable = true;
  enableOnBoot = true;
  
  daemon.settings = {
    data-root = "/var/lib/docker";
  };
};

users.users.${vars.username}.extraGroups = [ "docker" ];

Libvirt (optional):

virtualisation.libvirtd = {
  enable = true;
  qemu = {
    package = pkgs.qemu_kvm;
    ovmf.enable = true;
    swtpm.enable = true;
  };
};

users.users.${vars.username}.extraGroups = [ "libvirtd" ];

SSH

services.openssh = {
  enable = true;
  
  settings = {
    PermitRootLogin = "no";
    PasswordAuthentication = false;
  };
  
  ports = [ 22 ];
};

Firewall:

networking.firewall.allowedTCPPorts = [ 22 ];

Locate

plocate:

services.locate = {
  enable = true;
  package = pkgs.plocate;
  localuser = null;
};

Usage:

# Update database
sudo updatedb

# Search
locate filename

Flatpak

services.flatpak.enable = true;

xdg.portal.enable = true;

Usage:

# Add Flathub
flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo

# Install
flatpak install flathub org.gimp.GIMP

# Run
flatpak run org.gimp.GIMP

Automatic Upgrades

system.autoUpgrade = {
  enable = false;  # Manual recommended
  flake = "/home/${vars.username}/NullOS";
  dates = "weekly";
  allowReboot = false;
};

Garbage Collection

nix.gc = {
  automatic = true;
  dates = "weekly";
  options = "--delete-older-than 30d";
};

nix.optimise = {
  automatic = true;
  dates = [ "weekly" ];
};

User Services

Configured via Home Manager in home/ directory.

Hyprpaper

Wallpaper Service:

systemd.user.services.hyprpaper = {
  Unit = {
    Description = "Hyprland wallpaper daemon";
    PartOf = [ "graphical-session.target" ];
    After = [ "graphical-session.target" ];
  };
  
  Service = {
    ExecStart = "${pkgs.hyprpaper}/bin/hyprpaper";
    Restart = "on-failure";
  };
  
  Install = {
    WantedBy = [ "hyprland-session.target" ];
  };
};

SwayNC

Notification Daemon (home/swaync.nix):

services.swaync = {
  enable = true;
  
  settings = {
    positionX = "right";
    positionY = "top";
    control-center-width = 380;
    
    notification-visibility = {
      telegram = {
        state = "muted";
      };
    };
  };
};

SwayOSD

On-Screen Display (home/swayosd.nix):

services.swayosd = {
  enable = true;
  topMargin = 0.9;
};

Shows OSD for:

  • Volume changes
  • Brightness changes
  • Caps Lock

Syncthing

services.syncthing = {
  enable = true;
  user = vars.username;
  dataDir = "/home/${vars.username}/.syncthing";
  configDir = "/home/${vars.username}/.config/syncthing";
  
  overrideDevices = true;
  overrideFolders = true;
  
  settings = {
    devices = {
      "device1" = { id = "DEVICE-ID-HERE"; };
    };
    
    folders = {
      "Documents" = {
        path = "/home/${vars.username}/Documents";
        devices = [ "device1" ];
      };
    };
  };
};

Backup Service

Backup services are typically configured at the system level using Restic.

See Backup for complete configuration.

MPD

Music Player Daemon:

services.mpd = {
  enable = true;
  musicDirectory = "/home/${vars.username}/Music";
  
  extraConfig = ''
    audio_output {
      type "pipewire"
      name "PipeWire"
    }
  '';
};

services.mpdris2.enable = true;  # MPRIS support

Espanso

Text Expander:

services.espanso = {
  enable = true;
  
  configs = {
    default = {
      toggle_key = "CTRL";
    };
  };
  
  matches = {
    base = {
      matches = [
        {
          trigger = ":shrug";
          replace = \\_(ツ)_/¯";
        }
      ];
    };
  };
};

Managing Services

System Services

Check status:

sudo systemctl status service-name

Start/Stop:

sudo systemctl start service-name
sudo systemctl stop service-name
sudo systemctl restart service-name

Enable/Disable:

sudo systemctl enable service-name
sudo systemctl disable service-name

View logs:

sudo journalctl -u service-name
sudo journalctl -u service-name -f  # Follow

User Services

Check status:

systemctl --user status service-name

Start/Stop:

systemctl --user start service-name
systemctl --user stop service-name
systemctl --user restart service-name

Enable/Disable:

systemctl --user enable service-name
systemctl --user disable service-name

View logs:

journalctl --user -u service-name
journalctl --user -u service-name -f

List Services

All system services:

systemctl list-units --type=service

All user services:

systemctl --user list-units --type=service

Failed services:

systemctl list-units --failed
systemctl --user list-units --failed

Creating Custom Services

System Service

Create modules/services/my-service.nix:

{ config, pkgs, vars, ... }:

{
  systemd.services.my-service = {
    description = "My Custom Service";
    wantedBy = [ "multi-user.target" ];
    after = [ "network.target" ];
    
    serviceConfig = {
      Type = "simple";
      ExecStart = "${pkgs.my-package}/bin/my-command";
      Restart = "on-failure";
      RestartSec = "5s";
      
      User = vars.username;
      Group = "users";
      
      # Security
      PrivateTmp = true;
      NoNewPrivileges = true;
      ProtectSystem = "strict";
      ProtectHome = true;
    };
  };
}

Import in configuration.nix or modules/services/default.nix.

User Service

In home/my-service.nix:

{ config, pkgs, ... }:

{
  systemd.user.services.my-service = {
    Unit = {
      Description = "My User Service";
      After = [ "graphical-session.target" ];
    };
    
    Service = {
      ExecStart = "${pkgs.my-package}/bin/my-command";
      Restart = "on-failure";
      RestartSec = "5s";
    };
    
    Install = {
      WantedBy = [ "default.target" ];
    };
  };
}

Timer Service

System timer:

systemd.timers.my-timer = {
  wantedBy = [ "timers.target" ];
  
  timerConfig = {
    OnCalendar = "daily";
    OnBootSec = "5m";
    Persistent = true;
  };
};

systemd.services.my-timer = {
  serviceConfig = {
    Type = "oneshot";
    ExecStart = "${pkgs.bash}/bin/bash -c 'echo hello'";
  };
};

User timer:

systemd.user.timers.my-timer = {
  Unit = {
    Description = "My Timer";
  };
  
  Timer = {
    OnCalendar = "hourly";
    Persistent = true;
  };
  
  Install = {
    WantedBy = [ "timers.target" ];
  };
};

Service Dependencies

Ordering

systemd.services.my-service = {
  after = [ "network.target" "other-service.service" ];
  before = [ "another-service.service" ];
  requires = [ "required-service.service" ];
  wants = [ "optional-service.service" ];
};

Difference:

  • requires - Hard dependency (fails if missing)
  • wants - Soft dependency (continues if missing)
  • after - Start after other service
  • before - Start before other service

Service Security

Hardening Options

serviceConfig = {
  # Process
  User = "specific-user";
  Group = "specific-group";
  NoNewPrivileges = true;
  
  # Filesystem
  PrivateTmp = true;
  ProtectSystem = "strict";
  ProtectHome = true;
  ReadWritePaths = [ "/var/lib/myservice" ];
  
  # Network
  PrivateNetwork = false;
  RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
  
  # Capabilities
  CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
  AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
  
  # System calls
  SystemCallFilter = "@system-service";
  SystemCallErrorNumber = "EPERM";
};

Analyzing Security

systemd-analyze security service-name

Environment Variables

System Service

systemd.services.my-service = {
  environment = {
    MY_VAR = "value";
    PATH = "${pkgs.package}/bin";
  };
  
  # Or from file
  environmentFile = "/run/secrets/service-env";
};

User Service

systemd.user.services.my-service = {
  Service = {
    Environment = "MY_VAR=value";
    EnvironmentFile = "/home/user/.env";
  };
};

Troubleshooting Services

Service Won't Start

# Check status
systemctl status service-name

# View full logs
journalctl -u service-name -n 100

# Check for errors
journalctl -p err -u service-name

Service Crashes

# Check restart count
systemctl show service-name -p NRestarts

# Increase verbosity
journalctl -u service-name -f --output=verbose

Permission Issues

# Check which user runs service
systemctl show service-name -p User

# Check file permissions
ls -l /path/to/file

# Test command manually
sudo -u username /path/to/command

Best Practices

  1. Use Security Options - Restrict what services can access
  2. Set Restart Policies - Handle crashes gracefully
  3. Log Appropriately - Use structured logging
  4. Resource Limits - Prevent resource exhaustion
  5. User Services When Possible - Don't run as root unnecessarily

Next Steps

Clone this wiki locally