Simple_nix_service
Building a Custom NixOS Service with Flakes and Overlays

TL;DR NixOS’s declarative configuration and flakes make it easy to create custom services. This post shows how to build a minimal service using flakes and overlays for a “meow” command
- This will be a complete minimal configuration for testing purposes.
Create Project Directory
Start by creating a directory to hold your project, I called mine meow:
mkdir meow && cd meow
Create flake
Create a flake.nix with the following:
# flake.nix
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
outputs = { self, nixpkgs, ... }: {
overlays.default = final: prev: {
meow = final.writeShellScriptBin "meow" ''
echo meow
'';
};
nixosModules.default = { pkgs, config, lib, ... }: {
imports = [ ./nixos-module.nix ];
# inject dependencies from flake.nix, and don't do anything else
config = lib.mkIf config.services.meow.enable {
nixpkgs.overlays = [ self.overlays.default ];
services.meow.package = lib.mkDefault pkgs.meow;
};
};
};
}
Create Service Module
Next we’ll create the nixos-module.nix in the same directory with the
following content:
# nixos-module.nix
{ pkgs, config, lib, ... }:
let cfg = config.services.meow; in {
options = {
services.meow = {
enable = lib.mkEnableOption "meow";
package = lib.mkOption {
description = "meow package to use";
type = lib.types.package;
};
};
};
config = lib.mkIf cfg.enable {
systemd.services.meow = {
description = "meow at the user on the console";
serviceConfig = {
Type = "oneshot";
ExecStart = "${cfg.package}/bin/meow";
StandardOutput = "journal+console";
};
wantedBy = [ "multi-user.target" ];
};
};
}
Add nixosConfigurations Output
Lastly, we will add a nixosConfigurations output to the flake.nix
# flake.nix
nixosConfigurations.test = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.default
({ pkgs, lib, ... }: {
fileSystems."/" = {
device = "/dev/sda1";
};
boot.loader.grub.enable = false;
boot.initrd.enable = false;
boot.kernel.enable = false;
documentation.enable = false;
services.meow.enable = true;
system.stateVersion = "25.05";
})
];
};
nixosConfigurations.testis simply the name we chose for this particular NixOS system configuration.
The final product will look like this:
# flake.nix
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
outputs = {
self,
nixpkgs,
...
}: {
overlays.default = final: prev: {
meow = final.writeShellScriptBin "meow" ''
echo meow
'';
};
nixosModules.default = {
pkgs,
config,
lib,
...
}: {
imports = [./nixos-module.nix];
# inject dependencies from flake.nix, and don't do anything else
config = lib.mkIf config.services.meow.enable {
nixpkgs.overlays = [self.overlays.default];
services.meow.package = lib.mkDefault pkgs.meow;
};
};
nixosConfigurations.test = nixpkgs.lib.nixosSystem {
system = "x86_64-linux";
modules = [
self.nixosModules.default
({
pkgs,
lib,
...
}: {
fileSystems."/" = {
device = "/dev/sda1";
};
boot.loader.grub.enable = false;
boot.initrd.enable = false;
boot.kernel.enable = false;
documentation.enable = false;
services.meow.enable = true;
system.stateVersion = "25.05";
})
];
};
};
}
Build the System Configuration
Then build the system configuration:
nix build .#nixosConfigurations.test.config.system.build.toplevel
If this builds successfully you’ll see a
resultdirectory within yourmeowdirectory.I wouldn’t recommend actually switching to this configuration but you could build it to gain familiarity with it. If you were to switch to it you would run
./result/bin/switch-to-configurationTest in a NixOS Virtual Machine (Recommended):The safest way to see the “meow” output is to build the configuration and then run it in a NixOS virtual machine. You can do this using tools like
nixos-generate-configand a virtualization tool (like VirtualBox, QEMU, or GNOME Boxes).