Nginx: Multiple different ports in one virtual host

I would like to expose different services under different ports on a comon host name. 80/443 for normal HTTP/HTTPS traffic, 9100 for node metrics, 9200 for service metrics and so on. To do this, I would have to create multiple server sections in the nginx configuration file with the same host name but different ports to listen on. It seems to me that this is currently not possible in NixOS because the virtual host names in the attribute set must be unique. Is this assumption correct? If so, what would be the best way to work around this limitation? Is this something that could be fixed upstream? Or is there a better, different way to solve my original problem?

I’ve never configured nginx but I would guess you can just put all your config in a string and set it to services.nginx.config.

see NixOS Search

That looks like a good escape hatch, but if it’s possible I would like to keep my config within Nix. I really like enableACME = true; and forceSSL = true;.

Instead of listening on different ports I added subdomains for all metrics endpoints, so this isn’t a problem for me anymore. It’s also probably better this way.

For all future readers: The real solution is to create one virtual hosts for each service and configure the listen stuff to the right ports. The keys in the attribute set don’t have to match the server_name, you can set server_name explicitely as an attribute in the virtual host.

I managed to do it and it after 1+ hours of frustration, to save future readers’ time, here is an example of what i did (followed @MazeChaZer 's advice, thanks!)

lets say I want to host 2 sites, anki.example.com for an anki sync server, and example.com for my blog/website.

starting with the website:

    security.acme.certs."example.com".email = "name@example.com";

   # notice that I don't place the domain name here, name it something
  # meaningful like "my_main_website"
    services.nginx.virtualHosts."my_main_website" = {
     # here I put the domain name
      serverName = "example.com";
     # and then (optionally if you want) use certbot to make a certificate
     # and forceSSL (see next comment block, has ssl=true)
      forceSSL = true;
      enableACME = true;

     # accept requests from 0.0.0.0:443 (any IP to port 443)
     # don't forget to add "ssl=true" or "ssl=false"!
    # you need to also do add another for ipv6, I didn't want to 
      listen = [{port = 443;  addr="0.0.0.0"; ssl=true;}];
      # All serverAliases will be added as extra domain names on the certificate.
      # serverAliases = [ "bar.example.com" ];
      locations."/" = { root = "/var/www/example.com"; };
    };

now I host my Anki server:

  # these lines are for anki sync server, you can ignore them
 # (I choose to include them so that there is context)
    services.ankisyncd.enable = true;
    services.ankisyncd.port = 27702;

   # again, don't place the name of the domain here
   # place a meaningful name
    services.nginx.virtualHosts."my_anki_sync" = {
     # domain name goes here
      serverName = "anki.example.com";
      forceSSL = true;
      enableACME = true;

     # important: "ssl=true/false"
      listen = [{port = 27701;  addr="0.0.0.0"; ssl = true;}];

      locations."/" = {
        proxyPass = "http://127.0.0.1:27702";
       # ignore this
        extraConfig = "proxy_http_version 1.0;"
                    + "client_max_body_size 222M;";
      };

    };

don’t forgot to open the new ports if any is needed

1 Like