Nightscout on NixOS

These instructions will help you build, configure, and run Nightscout (plus MongoDB and Nginx) on any infrastructure (bare metal, VM, VPS, etc.) that can run NixOS.

Table of contents

  1. Install and configure MongoDB

  2. Build Nightscout

  3. Run Nightscout with Systemd

  4. Configure Nginx as an entry point

  5. Complete NixOS configuration

Install and configure MongoDB

MongoDB is installed and configured via the options under services.mongodb.

Authentication and the root user can be set up automatically via the enableAuth and initialRootPasswordFile options, or manually via the mongosh shell command:

$ nix-shell -p mongosh
$ mongosh
test> use admin
admin> db.createUser(
  {
    user: "root",
    pwd: "SUPER_SECRET_PASSWORD",
    roles: [
      { role: "userAdminAnyDatabase", db: "admin" },
      { role: "dbAdminAnyDatabase", db: "admin" },
      { role: "readWriteAnyDatabase", db: "admin" }
    ]
  }
)

A database and user for Nightscout to use can be created automatically via the initialScript option, or manually via the mongosh shell command:

$ nix-shell -p mongosh
$ mongosh --username root --authenticationDatabase admin
test> use nightscout
nightscout> db.createUser(
  {
    user: 'nightscout',
    pwd: 'CHANGEME',
    roles:  [
      { role: 'readWrite', db: 'nightscout' }
    ]
  }
)

Build Nightscout

Nightscout can be built using the buildNpmPackage. Here, we pass as an argument the Node.js package to use.

nightscout.nix:

{ pkgs, nodejs }:
pkgs.buildNpmPackage (finalAttrs: {
  pname = "cgm-remote-monitor";
  version = "15.0.3";

  src = pkgs.fetchFromGitHub {
    owner = "nightscout";
    repo = finalAttrs.pname;
    rev = "${finalAttrs.version}";
    hash = "sha256-bI7RvEz9+7k0ZsZWuW9SrLs2qlUHhmDjOwPlLp83Jzs=";
  };

  npmDepsHash = "sha256-p3Dqj78vzRmTPMgaodGXQgvHFE0jGsmBL0p9n403Y2M=";

  nodejs = nodejs;

  npmFlags = [ "--ignore-scripts" ];

  npmBuildScript = "bundle";

  meta = {
    description = "nightscout web monitor";
    homepage = "https://github.com/nightscout/cgm-remote-monitor";
    license = lib.licenses.agpl3Only;
    maintainers = with lib.maintainers; [ earldouglas ];
  };
})

Run Nightscout with Systemd

See systemd.services.nightscout block in the complete NixOS configuration below.

Read more about Systemd services on the NixOS wiki.

Configure Nginx as an entry point

See services.nginx block in the complete NixOS configuration below.

Read more about Nginx on the NixOS wiki.

Complete NixOS configuration

The configuration below expects the following environment variables to be set:

  • NIGHTSCOUT_API_SECRET

  • NIGHTSCOUT_MONGO_PASSWORD

  • NIGHTSCOUT_PORT

  • NIGHTSCOUT_DNS_NAME

  • DEXCOM_PASSWORD

  • DEXCOM_USERNAME

This configuration assumes that we want to source our data from Dexcom Share. For alternatives, see the connect section of the docs.

This configuration also assumes that we want to run an Nginx reverse proxy in front of our Nightscout instance. This can be useful for observability, rate limiting, HTTPS support, etc.

configuration.nix:

{ pkgs, lib, ... }:

let

  dexcomPassword          = builtins.getEnv "DEXCOM_PASSWORD";
  dexcomUsername          = builtins.getEnv "DEXCOM_USERNAME";

  nightscoutApiSecret     = builtins.getEnv "NIGHTSCOUT_API_SECRET";
  nightscoutMongoPassword = builtins.getEnv "NIGHTSCOUT_MONGO_PASSWORD";
  nightscoutPort          = builtins.parseInt(builtins.getEnv "NIGHTSCOUT_PORT");

  dnsName                 = builtins.getEnv "NIGHTSCOUT_DNS_NAME";

  # Use a supported version of Node.js
  nodejs = pkgs.nodejs_20;

  mongodb =
    (import <nixpkgs> {
      config.permittedInsecurePackages = [];
      config.allowUnfreePredicate =
        pkg: builtins.elem (pkgs.lib.getName pkg) [ "mongodb-ce" ];
    }).mongodb-ce;

  nightscout =
    import ./nightscout.nix {
      inherit pkgs;
      inherit nodejs;
    };

in {

  ## MongoDB ###########################################################

  services.mongodb = {
    enable = true;
    package = mongodb;
    extraConfig = ''
      security.authorization: enabled
    '';
  };

  ## Nightscout service account ########################################

  users.groups.nightscout = {};
  users.users.nightscout = {
    group = "nightscout";
    isSystemUser = true;
  };

  ## Nightscout service ################################################

  systemd.services.nightscout = {
    description = "nightscout";
    after = [ "network.target" ];
    wantedBy = [ "multi-user.target" ];
    serviceConfig = {
      WorkingDirectory = "${nightscout}/lib/node_modules/nightscout";
      ExecStart = "${nodejs}/bin/node lib/server/server.js";
      User = "nightscout";
      Restart = "always";
    };
    environment = {

      ## Required ######################################################
      # See https://github.com/nightscout/cgm-remote-monitor?tab=readme-ov-file#required

      MONGODB_URI = "mongodb://nightscout:${nightscoutMongoPassword}@localhost:27017/nightscout";
      API_SECRET = nightscoutApiSecret;
      MONGODB_COLLECTION = "entries";
      DISPLAY_UNITS = "mg/dl";

      ## Features ######################################################
      # See https://github.com/nightscout/cgm-remote-monitor?tab=readme-ov-file#features

      ENABLE =
        lib.strings.concatStrings (
          lib.intersperse " " [
            "connect"
            "ar2"
            "basal"
            "boluscalc"
            "careportal"
            "delta"
            "devicestatus"
            "direction"
            "errorcodes"
            "food"
            "profile"
            "simplealarms"
            "speech"
            "timeago"
          ]
        );
      AUTH_DEFAULT_ROLES = "denied";

      BASE_URL = "https://nightscout.earldouglas.com";

      ## Core ##########################################################
      # See https://github.com/nightscout/cgm-remote-monitor?tab=readme-ov-file#core

      PORT = toString nightscoutPort;

      # See https://github.com/nightscout/cgm-remote-monitor?tab=readme-ov-file#predefined-values-for-your-server-settings-optional

      TIME_FORMAT = "24";
      INSECURE_USE_HTTP = "true";
      THEME = "colors";

      ## Dexcom Share ##################################################
      # See https://github.com/nightscout/cgm-remote-monitor?tab=readme-ov-file#connect-nightscout-connect

      CONNECT_SOURCE = "dexcomshare";
      CONNECT_SHARE_ACCOUNT_NAME = dexcomUsername;
      CONNECT_SHARE_PASSWORD = dexcomPassword;

      ## Plugins #######################################################
      # See https://github.com/nightscout/cgm-remote-monitor?tab=readme-ov-file#plugins

      AR2_CONE_FACTOR = "0";
    };
  };

  ## Nginx entry point and reverse proxy ###############################

  services.nginx = {
    enable = true;
    virtualHosts."${dnsName}" = {
      locations."/".extraConfig = ''
        proxy_pass http://localhost:${toString nightscoutPort};
      '';
    };
  };
}