How do I deploy a virtual server with custom packages?

First up, I’m in love with the philosophy and promise of Nix, and I’m just falling apart on the implementation.

I’m trying to build a (now virtualbox, later AWS) web server with two virtual hosts
The first (and simpler) is just a static webpage whose contents I control through git
If I make a custom package, I can get a results symlink that appears to have the right contents

with import <nixpkgs> {};

stdenv.mkDerivation {
  name = "mapServer";
  src = builtins.fetchGit {
    url = "git@github.com:fakeuser/fakerepo.git";
    ref = "master";
  };
  installPhase =
    ''
      mkdir -p $out
      cp -r * $out
    '';
}

The next step, of trying to get a webserver to create and point at it then is my sticking point
I’m getting the error that I’m trying to call something that’s not a function, but not a set. Which, if I understand syntax is certainly true. What is the right way to incorporate these custom packages (and their paths as root for the virtual server)

My next task is to do the same thing again with a python flask application with a datastore that also needs to be git-cloned, so if there’s magic google incantations I should be invoking to learn these things, I would also appreciate pointers there.

{
webserver  =
  { config, pkgs, ... }:
  {
    networking.firewall.allowedTCPPorts = [80 443];

    nixpkgs.overlays = [
      (
        self: super:
        {
          maps = super.callPackage ./mapserver.nix {};
        }
      )
    ];

    services.nginx = {
      enable = true;
    };

    services.nginx.virtualHosts."map.fakename.org" = {
          forceSSL = true;
          enableACME = false; /* This will be true later */
          root = "${pkgs.maps}";
      };
  };
}

I don’t see anything obviously wrong here (although I haven’t really used nginx + nix before).

For nix-deployments, you have a few options. The most “native” solution would be to use NixOps. Or you could use some of the docker images utilities listed here: Docker - NixOS Wiki . And then leverage docker deployment options.

What is the exact error?

Also you might have a problem with forceSSL = true; if you don’t specify a certificate.

Ye, I’ve been looking at NixOps for the deployment. That file looks exactly like the “trivial-vbox.nix” for now from the tutorial.

I’ve commented out the forceSSL for now, as you are right, that is a problem. I will use ACME, but I’m mentally struggling there with the need to test like I want it built, but having and active server out there already.

The exact error is:
error: attempt to call something which is not a function but a set, at …

It appears to be coming from my attempt to set the root path
root = "${pkgs.maps}";

Which, I believe is calling the function I defined in overlays above, which then tries to call mapserver.nix as a function when it looks like a set.

It is an interesting experience to have the code make sense and the error make sense and have no idea how to proceed.

It may be the way mapserver.nix is written. callPackage expects a function but, at the moment, gets the set returned by stdenv.mkDerivation.

Does the problem still appear when you replace the first line with { stdenv }: (instead of with import <nixpkgs> {};)?

Interesting, that gets me much further. I have to change the overlay line to be

maps = super.callPackage ./mapserver.nix pkgs.stdenv { };

And that gets me much further. At a guess, it’ll be the document root that kills me next.

Yes, maybe?
The error is that mapserver.nix was “called with unexpected argument ‘outPath’”, which looks like a deep rabbit hole asking for drvPath.

I’ve done what I should have done from the beginning. I’ve created a stupid fakerepo so that these files should just be runnable by others trying to help. You should be able to fool the virtualhost with a local HOSTALIASES file

mapserver.nix

  { stdenv }:

  stdenv.mkDerivation {
    name = "mapServer";
    src = builtins.fetchGit {
      url = "git@github.com:mcbeth/fakerepo.git";
      ref = "master";
    };
    installPhase =
      ''
        mkdir -p $out
        cp -r * $out
      '';
  }

mapserver-service.nix

  {
  webserver  =
    { config, pkgs, ... }:
    {
      networking.firewall.allowedTCPPorts = [80 443];

      nixpkgs.overlays = [
        (
          self: super:
          {
            maps = super.callPackage ./mapserver.nix pkgs.stdenv { };
          }
        )
      ];

      services.nginx = {
        enable = true;
      };

      services.nginx.virtualHosts."map.fakename.org" = {
            forceSSL = false;
            enableACME = false; /* This will be true later */
            root = "${pkgs.maps}";
        };
    };
  }

trivial-vbox.nix

  {
    webserver =
      { config, pkgs, ... }:
      { deployment.targetEnv = "virtualbox";
        deployment.virtualbox.memorySize = 1024; # megabytes
        deployment.virtualbox.vcpu = 2; # number of cpus
        deployment.virtualbox.headless = true;
      };
  }

HOSTALIAS

map.fakename.org 192.168.56.102

BTW, it’d be really cool if I could figure out how to call get that IP out of NIXOPS and populate the file automatically, but that’s a stupid quality of life issue I can deal with after I get a machine to even start :slight_smile:

1 Like

That line should only read maps = super.callPackage ./mapserver.nix { };.

If your derivation maps only contains the document root for you web server you could even shorten the whole system config to:

let
  maps = builtins.fetchGit {
      url = "git@github.com:mcbeth/fakerepo.git";
      ref = "master";
    };

in {
  webserver  =
    { config, pkgs, ... }:
    {
      networking.firewall.allowedTCPPorts = [80 443];

      services.nginx = {
        enable = true;
      };

      services.nginx.virtualHosts."map.fakename.org" = {
            forceSSL = false;
            enableACME = false; /* This will be true later */
            root = "${maps}";
        };
    };
  }
1 Like

I had tried not having the stdenv in that line and it failed, and yet now that part works. Definite PEBKAC problem. Thanks for that pointer!

On to figuring out how to custom package a python flask app next (that’s why I was starting with a static site, I need both on the same server, and I thought the same mechanism for both would be cleaner and help me learn it better.

I’ll start a new topic when if get to that point and find all my new stupidities. Thanks!