From dev environment to production using flakes with Nix only

Hi,

After developing a CLI app using Node.js, I’m trying to understand how to make it run on a production server using only Nix

I have been using this flake.nix for my dev environment which I run using nix develop

{
  description = "Development environment";
  inputs = {
    nixpkgs.url = "https://flakehub.com/f/NixOS/nixpkgs/0.2305.492090.tar.gz";
    flake-utils.url = "github:numtide/flake-utils";
  };
  outputs = { nixpkgs, flake-utils, ... }: flake-utils.lib.eachDefaultSystem (system:
    let
      pkgs = import nixpkgs {
        inherit system;
      };
    in rec {
      devShell = pkgs.mkShell {
        buildInputs = with pkgs; [
          nodejs
	  sqlite
	  rclone
        ];
        shellHook = "
          source ./.envrc  # this sets some environment variables
       ";
      };
    }
  );
}

Now, I want to deploy the finished app to several production servers that have Nix installed.

What’s the appropriate way to translate this environment to production and ensure that the tools will run when the server boots up?.

Can this be achieved using only Nix? (not NixOS)?

Thanks!

It depends on the package manager you’re using. You might want to use something like node2nix, which looks at your lockfile and generates Nix expressions for all your dependencies, but there’s other tools out there you can try depending on your setup.

Yes, but if it’s a server you’ll have to manage the systemd configuration on your own! It should come down to writing a .service file that contains something along the lines of ExecStart=/nix/store/<store-hash>-nodejs/bin/node /nix/store/<store-hash>-yourservice/index.js. If you need additional setup you can also create an additional “package” with a shell script that starts the server and does whatever you need it to do and then run that in the ExecStart section, kind of like this:

{
  writeShellScript "start-yourservice.sh" ''
    ${nodejs}/bin/node ${yourservice}/index.js
  ''
}

@steinuil Thanks for pointing this out.

Do you mean manually create the systemd service?. Is there a way to do it with nix? (I know that you can do it using NixOS).

Should the .service run the flake too?. How can ensure that nodejs, sqlite or any tool needed is installed first on the server before the ExecStart? is it with nix run or nix-build??

Is there any place were i could find an example of a simple server provisioning for javascript/node?

You could that with writeText. You can add a package to your flake such as this one:

{
  packages.yourservice-systemd-unit = writeText "yourservice.service" ''
    [Unit]
    ...

    [Service]
    Type=simple
    ExecStart=${nodejs}/bin/node ${yourservice}/index.js
  '';
}

Then build it with nix build .#yourservice-systemd-unit, get the store path for it and symlink it to /etc/systemd/system/yourservice.service. Then when you want to upgrade it you’d have to build it again, update the symlink and restart the service.

I don’t know if calling nix run inside a systemd service could be considered best practice, but you could do that as well. In that case you’ll have to write the .service file yourself, but I suppose it’d be easier to upgrade.

For linked libraries, Nix will take care of that when you build the service if you add them to your buildInputs. If your program calls to other binaries you could use makeWrapper or similar functions during the postInstall phase.

Since you’re using a flake, you should define your service’s derivation in the packages section of the flake, and then run nix build .#yourservice. Check the outputs section in this page.

1 Like

Ok, I think I get the idea and now I have a direction to explore. I’ll give it a try!
Thanks for the suggestions!