Community Contribution: The Nix flake and NixOS module are community-provided. The SparkyFitness maintainers do not use Nix/NixOS and are unable to test or officially support this installation method. It is provided as-is as part of a community contribution.

NixOS

Run SparkyFitness on NixOS without Docker using the Nix flake and the services.sparkyfitness NixOS module. The module runs the backend as a systemd service, optionally provisions a local PostgreSQL database, and serves the static frontend through nginx with the same reverse-proxy routes (/api, /health-data, /uploads, /mcp) the Docker deployment uses.

Prerequisites

  • A NixOS host with flakes enabled (nix.settings.experimental-features = [ "nix-command" "flakes" ];).
  • PostgreSQL 15 or later. The module defaults to postgresql_16. Earlier versions reject the migrations (which use UNIQUE NULLS NOT DISTINCT) with a syntax error near "NULLS".

1. Add the flake input

Add SparkyFitness as a flake input and import its NixOS module in your system configuration:

{
  inputs.sparkyfitness.url = "github:CodeWithCJ/SparkyFitness";

  outputs = { nixpkgs, sparkyfitness, ... }: {
    nixosConfigurations.myhost = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        sparkyfitness.nixosModules.sparkyfitness
        {
          services.sparkyfitness = {
            enable = true;
            frontendUrl = "https://fitness.example.com";
            # Secrets (DB passwords, encryption key, auth secret) live here:
            environmentFile = "/run/secrets/sparkyfitness.env";
            nginx.virtualHost = "fitness.example.com";
          };
        }
      ];
    };
  };
}

Use nixosModules.sparkyfitness to wire in this flake's package builds automatically, or nixosModules.default and set backendPackage / frontendPackage yourself.

2. Provide the secrets

environmentFile is a systemd EnvironmentFile and should contain at least:

SPARKY_FITNESS_DB_PASSWORD=...
SPARKY_FITNESS_APP_DB_PASSWORD=...
SPARKY_FITNESS_API_ENCRYPTION_KEY=...   # openssl rand -hex 32
BETTER_AUTH_SECRET=...

Keep this file out of the Nix store (e.g. via sops-nix or agenix). When database.createLocally = true (the default), SPARKY_FITNESS_DB_PASSWORD is also used to provision the local PostgreSQL owner role.

3. Build and switch

sudo nixos-rebuild switch --flake .#myhost

On first activation the module:

  • provisions the local PostgreSQL owner role and database (when database.createLocally is enabled);
  • starts the backend systemd service, which runs migrations and creates the limited application role automatically;
  • serves the frontend through nginx on the configured virtual host.

Key options

OptionDefaultDescription
enablefalseEnable the SparkyFitness service.
frontendUrl(required)Public URL of the site (CORS / Better Auth trusted origins).
environmentFile(required)Path to the systemd EnvironmentFile holding the secrets above.
port3010Backend API listen port.
stateDir/var/lib/sparkyfitnessPersistent state (uploads, backups, temp uploads).
database.createLocallytrueProvision a local PostgreSQL instance, owner role and database.
database.packagepkgs.postgresql_16PostgreSQL package for the local instance (must be >= 15).
nginx.enabletrueServe the frontend and reverse-proxy the API through nginx.
nginx.virtualHostlocalhostnginx virtual host name.
extraEnvironment{}Additional environment variables for the backend.

To point at an external database instead, set database.createLocally = false and configure database.host, database.port, database.name, database.user and the corresponding passwords in environmentFile.

Local build and development

nix build .#sparkyfitness-server
nix build .#sparkyfitness-frontend

nix develop   # dev shell with node 24, pnpm and postgresql

The pnpmDeps.hash values pinned in the flake must be refreshed whenever pnpm-lock.yaml changes (a stale hash surfaces as a hash mismatch build failure). Run the helper script to recompute them:

nix/update-hashes.sh
# or, if nix is not on PATH:
NIX=/path/to/nix nix/update-hashes.sh

See nix/README.md in the repository for the full layout and additional detail.