# Nix-built Grafana Pyroscope continuous profiling server # Builds v1.19.1 from forge mirror # Uses stdenv + make (not buildGoModule) due to multi-module go.work workspace # with local replace directives (./api, ./lidia) # Built with dockerTools.buildLayeredImage for efficient layer caching { pkgs ? import { } }: let version = "1.19.1"; src = pkgs.fetchgit { url = "https://forge.ops.eblu.me/mirrors/pyroscope.git"; rev = "v${version}"; hash = "sha256-UPxGimkzXLFACqmAM1hNQIoNjN6OquVibwVmNvP00+s="; }; # Build frontend assets via yarn + webpack (upstream uses Docker for this) # mkYarnPackage symlinks node_modules from the Nix store, but webpack's # CopyPlugin can't follow symlinks for glob patterns. We dereference the # @grafana/ui icons directory before running the build. ui = pkgs.mkYarnPackage { inherit version src; pname = "pyroscope-ui"; buildPhase = '' runHook preBuild export HOME=$TMPDIR cd deps/grafana-pyroscope # mkYarnPackage symlinks node_modules into the Nix store (read-only). # Webpack CopyPlugin can't glob through these symlinks to find # @grafana/ui icons. Pre-copy them to the output location and patch # webpack to skip the CopyPlugin entry for icons. mkdir -p public/build/grafana/build/img cp -rL ../../node_modules/@grafana/ui/dist/public/img/icons \ public/build/grafana/build/img/ # Rewrite the CopyPlugin icons path to point at our pre-copied location # instead of the symlinked node_modules path that webpack can't glob sed -i "s|from: 'node_modules/@grafana/ui/dist/public/img/icons'|from: 'public/build/grafana/build/img/icons'|" \ scripts/webpack/webpack.common.js yarn --offline build runHook postBuild ''; installPhase = '' runHook preInstall mkdir -p $out cp -r public/build/* $out/ runHook postInstall ''; distPhase = "true"; }; # Pre-fetch Go modules for all go.mod files in the workspace (fixed-output derivation) goModules = pkgs.stdenv.mkDerivation { pname = "pyroscope-go-modules"; inherit src version; nativeBuildInputs = with pkgs; [ go git cacert ]; buildPhase = '' export GOPATH=$TMPDIR/go export GOFLAGS=-modcacherw # Download modules for all workspace members go mod download cd api && go mod download && cd .. cd lidia && go mod download && cd .. ''; installPhase = '' cp -r $TMPDIR/go/pkg/mod $out ''; # Disable fixup: patchelf and patchShebangs modify downloaded Go toolchain # binaries, which makes the fixed-output derivation reference store paths dontFixup = true; outputHashMode = "recursive"; outputHash = "sha256-RCWuqz1XaDrS7+GqL/9v7LNA14M4/ohWEtPeTMDkJFc="; outputHashAlgo = "sha256"; }; pyroscope = pkgs.stdenv.mkDerivation { inherit src version; pname = "pyroscope"; nativeBuildInputs = with pkgs; [ go git gnumake cacert ]; buildPhase = '' runHook preBuild export HOME=$TMPDIR export GOPATH=$TMPDIR/go export GOFLAGS=-modcacherw # Populate module cache from pre-fetched modules mkdir -p $GOPATH/pkg cp -r ${goModules} $GOPATH/pkg/mod chmod -R u+w $GOPATH/pkg/mod # Copy pre-built frontend assets mkdir -p public/build cp -r ${ui}/* public/build/ # Build Go binary with embedded frontend assets # Skip the Makefile's frontend/build target (uses Docker) and # invoke go/bin directly with EMBEDASSETS set # CGO_ENABLED=0 for static binary (matches upstream) CGO_ENABLED=0 \ EMBEDASSETS=embedassets \ IMAGE_TAG=v${version} \ make go/bin runHook postBuild ''; installPhase = '' runHook preInstall mkdir -p $out/bin cp pyroscope $out/bin/pyroscope runHook postInstall ''; meta = with pkgs.lib; { description = "Grafana Pyroscope continuous profiling platform"; homepage = "https://grafana.com/docs/pyroscope/"; license = licenses.agpl3Only; mainProgram = "pyroscope"; }; }; in pkgs.dockerTools.buildLayeredImage { name = "blumeops/pyroscope"; contents = [ pyroscope pkgs.cacert pkgs.tzdata ]; config = { Entrypoint = [ "${pyroscope}/bin/pyroscope" ]; Cmd = [ "-config.path=/etc/pyroscope/config.yaml" ]; Env = [ "SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt" "TZDIR=${pkgs.tzdata}/share/zoneinfo" ]; ExposedPorts = { "4040/tcp" = { }; }; User = "65534"; }; }