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.test
is 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
result
directory within yourmeow
directory.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-configuration
Test 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-config
and a virtualization tool (like VirtualBox, QEMU, or GNOME Boxes).