Materialize Monitoring Alloy#

mzmon-alloy is a minimal, distroless repackaging of Grafana Alloy hardened for use in Materialize Monitoring. It is published to ghcr.io/materializeinc/mzmon-alloy and is a drop-in replacement for the upstream grafana/alloy image.

Rather than build Alloy from source, the image repackages the signed upstream release binary (verified against the release SHA256SUMS) onto a distroless base. This keeps us in lockstep with upstream releases while giving us a smaller, non-root, FIPS-capable image.

Tags and versioning#

Images are tagged <alloy-version>-<suffix>, for example v1.17.0-mz1.

  • <alloy-version> is the upstream Alloy release the binary comes from.
  • <suffix> is the Materialize revision of that repackaging. Release images use mzN (mz1, mz2, …). The counter increments whenever the image changes at a fixed Alloy version, a base-image digest bump included. An Alloy upgrade restarts it at mz1. Within one <alloy-version>, the counter never reuses or moves back over a suffix already published. An upgrade that shipped without restarting therefore keeps counting from what the registry holds for that version. v1.19.2 is the standing case of that. It first shipped as v1.19.2-mz2, carrying the suffix over from v1.18.1, so no v1.19.2-mz1 was ever published.
  • Pull requests that touch the image publish a throwaway build tagged <alloy-version>-dev.0--pr.g<short-sha> for pre-release testing.

Compliance and security posture#

The table below summarizes how the image maps to the frameworks we track. Status legend: ✅ enforced in the image · ⚙️ enforced at deploy/CI (image is compatible) · ⚠️ operator/config-dependent.

FrameworkControlStatusHow it is met
FIPS 140-3Validated cryptographic modulealloy-boringcrypto routes Go crypto through the CMVP-validated BoringCrypto module; build-asserted via grep boringcrypto.
FIPS 140-3Approved-mode operation⚠️Requires FIPS-approved ciphers / TLS 1.2+ in the overlaid pipeline config.
CIS Docker 4.1Run as non-root userUSER 473:473 (numeric).
CIS Docker 4.2Trusted, pinned base imageDistroless base, pinned by digest and tracked by Renovate.
CIS Docker 4.3No unnecessary packagesDistroless final stage — no shell or package manager.
CIS Docker 4.5Image signing⚙️cosign signing in CI (planned).
CIS Docker 4.6Health check⚙️No HEALTHCHECK (distroless); use a Kubernetes httpGet probe on /-/ready:12345.
CIS Docker 4.7No standalone apt-get updateCombined update+install layer in the builder.
CIS Docker 4.8No setuid/setgid binariesBinary installed 0755; distroless base has none.
CIS Docker 4.9Prefer COPY over ADDADD used only to fetch the release artifact, then checksum-verified.
CIS Docker 4.10No secrets in the imageNone present.
CIS Docker 4.11Verify downloaded packagesAlloy binary verified against upstream SHA256SUMS.
CIS Kubernetes §5Restricted pod securityContext⚙️Image supports the securityContext below (non-root, read-only rootfs, drop all caps).
NIST SP 800-190 §4.1.1Image vulnerabilities⚙️Distroless minimizes surface; Trivy/Grype scan gate in CI (planned).
NIST SP 800-190 §4.1.2Image configuration defectsNon-root, minimal, no secrets, no embedded services.
NIST SP 800-190 §4.1.3Embedded malware / integrityChecksum-verified binary + build provenance + SBOM (signing planned).
NIST SP 800-190 §4.1.5Use of untrusted imagesDigest-pinned distroless base; official verified Alloy release.

FIPS 140-3#

The image ships the alloy-boringcrypto release variant, so Alloy’s Go cryptography is routed through the CMVP-validated BoringCrypto module. FIPS mode is engaged automatically at process start (power-on self-tests run at init); no runtime environment variable is required. The build asserts the FIPS backend is present (alloy --version | grep boringcrypto), so a wrong or downgraded release asset fails the build.

The FIPS boundary is Alloy’s in-process Go cryptography only. The distroless base ships Debian’s (non-validated) OpenSSL libraries, but Alloy is a pure-Go binary and never calls them, so they are outside the boundary — a FIPS-validated base OS is not required for this workload. Operating in an approved mode still requires configuration: TLS on remote_write and receivers must be constrained to FIPS-approved cipher suites and TLS 1.2+ in the overlaid pipeline config.

Image hardening#

  • Non-root by default. Runs as uid/gid 473:473 (a numeric user, which satisfies Kubernetes runAsNonRoot).
  • Distroless base. No shell, package manager, or unnecessary packages, minimizing CVE and attack surface (NIST SP 800-190 §4.1).
  • Verified provenance. The Alloy binary is checksum-verified against the upstream SHA256SUMS at build time; base images are pinned by digest and tracked by Renovate.
  • Supply-chain attestations. Published images carry build provenance (provenance: mode=max) and an SBOM.

Kubernetes securityContext#

The image is compatible with a locked-down securityContext; set this in the deployment (CIS Kubernetes Benchmark §5):

securityContext:
  runAsNonRoot: true
  runAsUser: 473
  allowPrivilegeEscalation: false
  readOnlyRootFilesystem: true
  capabilities:
    drop: ["ALL"]
  seccompProfile:
    type: RuntimeDefault

With readOnlyRootFilesystem: true, provide writable emptyDir volumes for the two paths Alloy writes to:

  • /var/lib/alloy — the storage path (WAL / remote-config state). Declared as a VOLUME in the image.
  • /tmp — scratch space.

A host-monitoring DaemonSet that scrapes the node (host /proc, /sys, journal) is a deliberate exception that needs additional mounts and relaxed settings. The Materialize scraping use case does not, and should run fully unprivileged as above.

Health checks#

The image has no HEALTHCHECK — distroless has no shell and Alloy has no self-probe subcommand. Use a Kubernetes httpGet readiness/liveness probe against /-/ready on the Alloy HTTP port (12345) instead.

Building locally#

make alloy-image

This builds the multi-arch image (linux/amd64, linux/arm64) and smoke-tests alloy --version on both. Publishing to GHCR happens in CI, not from the Makefile.