Custom service

When using services-flake you are not just limited to the builtin services. You can also define your own service.

By default, services-flake supports multiple instances for each service, allowing you to run several instances of the same service simultaneously. However, you also have the option to create custom single-instance services. In the following sections, we’ll explore how to define custom services of both types.

Single instance service

We will create a hello service that will return a greeting message:

{ config, lib, pkgs, ... }:
{
  options = {
    services.hello = {
      enable = lib.mkEnableOption "Enable hello service";
      package = lib.mkPackageOption pkgs "hello" { };
      message = lib.mkOption {
        type = lib.types.str;
        default = "Hello, world!";
        description = "The message to be displayed";
      };
    };
  };
  config =
    let
      cfg = config.services.hello;
    in
    lib.mkIf cfg.enable {
      settings.processes.hello = {
        command = "${lib.getExe cfg.package} --greeting='${cfg.message}'";
      };
    };
}

Let’s call this file hello.nix.

Now, we can import this service in our flake. In this example, we will configure an existing service, Ollama, and our custom service from above:

{
  description = "A demo of importing a single instance custom service";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    systems.url = "github:nix-systems/default";
    process-compose-flake.url = "github:Platonic-Systems/process-compose-flake";
    services-flake.url = "github:juspay/services-flake";
  };
  outputs = inputs:
    inputs.flake-parts.lib.mkFlake { inherit inputs; } {
      systems = import inputs.systems;
      imports = [
        inputs.process-compose-flake.flakeModule
      ];
      perSystem = { self', pkgs, lib, ... }: {
        process-compose."default" = { config, ... }: {
          imports = [
            inputs.services-flake.processComposeModules.default
            ./hello.nix
          ];

          services.ollama."ollama1".enable = true;
          services.hello.enable = true;
        };
      };
    };
}

Finally, nix run: [[single-instance-hello.png]]

Multi-instance service

For this purpose, services-flake exports a multiService library function. It aims to provide an interface wherein the user just writes the configuration like they would for a single instance service, and the library takes care of creating multiple instances of the service.

Let’s write the same hello service as above, in hello.nix, but this time as a multi-instance service:

{ config, lib, name, pkgs, ... }:
{
  options = {
    enable = lib.mkEnableOption "Enable ${name} service";
    package = lib.mkPackageOption pkgs "hello" { };
    message = lib.mkOption {
      type = lib.types.str;
      default = "Hello, world!";
      description = "The message to be displayed";
    };
  };
  config = {
    outputs.settings = {
      processes.${name} = {
        command = "${lib.getExe config.package} --greeting='${config.message}'";
      };
    };
  };
}

The primary differences from the single instance service are:

  • The module now takes an additional argument name, which is the name of the instance of the service.
  • We no longer have to write the config block, as it is now handled by the library by importing the outputs.settings option.
  • And we don’t have to write options.services."${name}", as that is abstracted away by the library.

Now that we have defined the multi-instance service, we can import it in our flake:

{
  description = "A demo of importing a multi-instance custom service";
  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
    flake-parts.url = "github:hercules-ci/flake-parts";
    systems.url = "github:nix-systems/default";
    process-compose-flake.url = "github:Platonic-Systems/process-compose-flake";
    services-flake.url = "github:juspay/services-flake";
  };
  outputs = inputs:
    inputs.flake-parts.lib.mkFlake { inherit inputs; } {
      systems = import inputs.systems;
      imports = [
        inputs.process-compose-flake.flakeModule
      ];
      perSystem = { self', pkgs, lib, ... }: {
        process-compose."default" = 
        let
          inherit (inputs.services-flake.lib) multiService;
        in
        {
          imports = [
            inputs.services-flake.processComposeModules.default
            (multiService ./hello.nix)
          ];

          services.ollama."ollama1".enable = true;
          services.hello = {
            hello1 = {
              enable = true;
              message = "Hello, world!";
            };
            hello2 = {
              enable = true;
              message = "Hello, Nix!";
            };
          };
        };
      };
    };
}

And finally, nix run: [[multi-instance-hello.png]]

See also

Links to this page