Restructure docs: consolidate, recategorize, and extract
All checks were successful
Build Container (Nix) / detect (push) Successful in 2s
Build Container / detect (push) Successful in 2s

- Consolidate 4 Authentik Nix derivation docs into one card
  (authentik-nix-build-components.md)
- Merge build-grafana-container + build-grafana-sidecar into
  build-grafana-images.md
- Move agent-change-process from how-to/ to explanation/ (it's a
  methodology doc, not a task guide)
- Extract Caddy custom build section from reference card into
  how-to/deployment/build-caddy-with-plugins.md
- Move expose-service-publicly from how-to/ to tutorials/ (it's a
  comprehensive walkthrough, not a quick task reference)
- Update all wiki-link references across affected docs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erich Blume 2026-03-15 19:55:59 -07:00
commit f46a04b902
17 changed files with 246 additions and 321 deletions

View file

@ -1,14 +1,16 @@
--- ---
title: Agent Change Process title: Agent Change Process
modified: 2026-03-04 modified: 2026-03-15
last-reviewed: 2026-02-23 last-reviewed: 2026-02-23
tags: tags:
- how-to - explanation
- ai - ai
--- ---
# Agent Change Process # Agent Change Process
> **Note:** This article was drafted by AI and reviewed by Erich. I plan to rewrite all explanatory content in my own words - these serve as placeholders to establish the documentation structure.
How to classify and execute infrastructure changes, especially when working with AI agents that may lose context across sessions. How to classify and execute infrastructure changes, especially when working with AI agents that may lose context across sessions.
## Change Classification ## Change Classification

View file

@ -1,50 +0,0 @@
---
title: Generate Authentik API Clients
modified: 2026-03-01
last-reviewed: 2026-03-01
tags:
- how-to
- authentik
- nix
---
# Generate Authentik API Clients
Build Go and TypeScript API client bindings from authentik's OpenAPI spec (`schema.yml`). These are build-time inputs for the Go server and web UI respectively.
## Context
Authentik maintains a separate repo ([`goauthentik/client-go`](https://github.com/goauthentik/client-go)) with pre-generated Go client code. The nixpkgs derivation fetches this and injects it into the Go vendor directory via a setup hook (`apiGoVendorHook`). The TypeScript client is generated inline from `schema.yml` using `openapi-generator-cli`.
Both clients are generated from the same `schema.yml` OpenAPI spec in the main authentik repo.
## What to Do
1. Create a Nix derivation (`client-go`) that generates Go API client bindings from `schema.yml` using `openapi-generator-cli`
2. Create a Nix derivation (`client-ts`) that generates TypeScript fetch client bindings from the same spec
3. Create a setup hook (`apiGoVendorHook`) that replaces `goauthentik.io/api/v3` in the Go vendor directory with the generated client
4. Verify the generated code compiles (Go: `go build`, TypeScript: type-check with `tsc`)
## Key Details
- Source spec: `schema.yml` in the authentik repo root
- Go client replaces `vendor/goauthentik.io/api/v3/` in the server build (via `api-go-vendor-hook.nix`)
- TypeScript client replaces `web/node_modules/@goauthentik/api/` in the web UI build (symlinked in `webui.nix`)
## Testing on Ringtail
The `test-build.nix` harness in `containers/authentik/` supports individual component builds:
```fish
set tmpdir (ssh ringtail 'mktemp -d /tmp/authentik-test.XXXXXX')
scp containers/authentik/*.nix ringtail:$tmpdir/
ssh ringtail "cd $tmpdir && nix-build test-build.nix -A client-go --extra-experimental-features 'nix-command flakes'"
ssh ringtail "cd $tmpdir && nix-build test-build.nix -A client-ts --extra-experimental-features 'nix-command flakes'"
ssh ringtail "rm -rf $tmpdir"
```
## Related
- [[build-authentik-from-source]] — Parent goal
- [[authentik-go-server-derivation]] — Consumer of Go client
- [[authentik-web-ui-derivation]] — Consumer of TypeScript client

View file

@ -1,50 +0,0 @@
---
title: Build Authentik Go Server
modified: 2026-03-02
last-reviewed: 2026-03-02
tags:
- how-to
- authentik
- nix
---
# Build Authentik Go Server
Build the Go HTTP server binary (`cmd/server`) that serves the web UI, REST API, and spawns gunicorn for the Django backend.
## Context
The Go server is built with `buildGoModule` from the `cmd/server` subpackage. It's a Cobra-based binary that:
- Serves static web assets and the REST API
- Runs an embedded reverse proxy outpost
- Spawns `gounicorn` (gunicorn) to run the Django application
- Manages health checks
The nixpkgs derivation patches store paths into two Go source files so the compiled binary can find Python lifecycle scripts and web assets at runtime.
## What to Do
1. Create a `buildGoModule` derivation for `cmd/server` from the authentik source
2. Inject the generated Go API client into the vendor directory (via `apiGoVendorHook`)
3. Apply `substituteInPlace` patches to hardcode Nix store paths:
- `internal/gounicorn/gounicorn.go`: `./lifecycle``${authentik-django}/lifecycle`
- `web/static.go`: `./web``${webAssetsPath}` (the webui derivation)
- `internal/web/static.go`: `./web``${webAssetsPath}` (the webui derivation)
4. Compute the `vendorHash` — note that the hook replaces vendored API code *after* hash verification, so the hash reflects `go.sum` only
5. Rename the output binary from `server` to `authentik`
6. Verify: `./authentik --help` runs successfully
## Key Details
- Go module: `goauthentik.io`
- Subpackage: `./cmd/server`
- CGO: disabled
- The `vendorHash` must be computed with the vendor replacement hook excluded (`overrideModAttrs`)
- Outpost binaries (`cmd/ldap`, `cmd/proxy`, `cmd/radius`) are separate and not needed for basic deployment
## Related
- [[build-authentik-from-source]] — Parent goal
- [[authentik-api-client-generation]] — Provides Go client (prerequisite)
- [[authentik-python-backend-derivation]] — Provides lifecycle scripts and web assets (prerequisite)

View file

@ -0,0 +1,136 @@
---
title: Authentik Nix Build Components
modified: 2026-03-15
last-reviewed: 2026-03-15
tags:
- how-to
- authentik
- nix
---
# Authentik Nix Build Components
Detailed reference for the four Nix derivations that make up the authentik from-source build. See [[build-authentik-from-source]] for the parent overview and version update workflow.
## API Client Generation
Go and TypeScript API client bindings generated from authentik's OpenAPI spec (`schema.yml`).
Authentik maintains a separate repo ([`goauthentik/client-go`](https://github.com/goauthentik/client-go)) with pre-generated Go client code. The nixpkgs derivation fetches this and injects it into the Go vendor directory via a setup hook (`apiGoVendorHook`). The TypeScript client is generated inline from `schema.yml` using `openapi-generator-cli`.
**What to do:**
1. Create a Nix derivation (`client-go`) that generates Go API client bindings from `schema.yml` using `openapi-generator-cli`
2. Create a Nix derivation (`client-ts`) that generates TypeScript fetch client bindings from the same spec
3. Create a setup hook (`apiGoVendorHook`) that replaces `goauthentik.io/api/v3` in the vendor directory with the generated client
4. Verify the generated code compiles (Go: `go build`, TypeScript: type-check with `tsc`)
**Key details:**
- Source spec: `schema.yml` in the authentik repo root
- Go client replaces `vendor/goauthentik.io/api/v3/` in the server build (via `api-go-vendor-hook.nix`)
- TypeScript client replaces `web/node_modules/@goauthentik/api/` in the web UI build (symlinked in `webui.nix`)
## Python Backend
`authentik-django` — the Python/Django application that forms the core backend.
Authentik 2026.2.0 requires Python 3.14 (`requires-python = "==3.14.*"`). Instead of carrying individual overrides for each broken nixpkgs python314 package, we use **`uv`** to install Python dependencies from PyPI, where upstream maintainers have already published Python 3.14-compatible wheels.
### Approach: uv sync FOD + autoPatchelfHook
Nix builds are sandboxed with no network access. The pattern is:
1. **Fixed-output derivation (FOD)**`uv sync --frozen` fetches and installs all dependencies into a venv. FODs are allowed network access because the output hash is declared upfront. Compiled `.so` files reference Nix store paths (RPATHs to libxml2, krb5, etc.), which FODs must not contain, so we strip references with `remove-references-to` and delete `bin/` and `.pyc` files.
2. **Main derivation** — copies the FOD's `lib/python3.14/site-packages/`, recreates `bin/` with proper python symlinks, restores `pyvenv.cfg`, and runs `autoPatchelfHook` to re-link `.so` files against the correct Nix store libraries.
**Why not `uv pip download` + `uv pip install --no-index`?** `uv pip download` does not exist in uv 0.9.29 (nixpkgs). And the download-only approach has further complications with sdist-only packages (psycopg-c, gssapi) that must be compiled anyway.
**What to do:**
1. Create the FOD (`python-deps.nix`) that runs `uv sync --frozen --no-install-project --no-install-workspace --no-dev`, then strips all Nix store references from the output
2. Create the main derivation (`authentik-django.nix`) that copies the FOD's site-packages, recreates venv, runs `autoPatchelfHook`, copies in-tree workspace packages, and applies path patches
3. Verify: `$out/bin/python3.14 -c "import authentik"` succeeds
**Key details:**
- Nix provides: `python314`, `uv`, system libraries (`libxml2`, `libxslt`, `openssl`, `libffi`, `zlib`, etc.)
- PyPI provides: all Python packages (via pre-built `cp314` wheels where available, sdist builds otherwise)
- The FOD hash must be recomputed when `uv.lock` changes
- The 4 in-tree packages are installed from monorepo source, not PyPI
- Standard `djangorestframework` 3.16.1 from PyPI (no longer forked as of 2026.2.0)
### Lessons Learned
| Issue | Fix |
|-------|-----|
| `pg_config` not found for psycopg-c | Use `pkgs.postgresql.pg_config` (separate derivation), not `pkgs.postgresql` |
| gssapi `gss_acquire_cred_impersonate_name` undeclared | `NIX_CFLAGS_COMPILE="-include gssapi/gssapi_ext.h"` |
| xmlsec linker error `-lltdl` | Add `pkgs.libtool` to buildInputs (provides libltdl) |
| psycopg-c needs `libpq` | Add `pkgs.libpq` to buildInputs |
| Static `refTargets` list missed 6 store refs | Dynamic discovery: `grep -aohE '/nix/store/...'` finds all refs |
| autoPatchelfHook can't find libraries | `buildInputs` in main derivation must include all libraries that `.so` files link against |
| `FieldError: Cannot resolve keyword 'group_id'` | Django migration ordering bug — add explicit dependency via `substituteInPlace`. Upstream [#19616](https://github.com/goauthentik/authentik/issues/19616) |
The `uv sync` completes in ~3.5 minutes. Dynamic reference discovery finds 19 unique store paths. `autoPatchelfHook` in the main derivation resolves all NEEDED entries with 0 unsatisfied dependencies.
## Web UI
Lit-based TypeScript web frontend built with esbuild + rollup.
As of 2026.2.0, the main build uses **esbuild** (via wireit) and the SFE sub-package uses **rollup**. Two-phase Nix build:
1. **`webui-deps.nix`** — Fixed-output derivation that runs `npm ci` to fetch Node dependencies. Platform-specific output hash.
2. **`webui.nix`** — Copies deps, patches in the generated TypeScript API client (`client-ts`), patches shebangs, runs `npm run build` (wireit/esbuild) and `npm run build:sfe` (rollup).
**Key details:**
- **Node.js:** `nodejs_24` (authentik requires Node >= 24, npm >= 11.6.2)
- **Build time:** ~33s on ringtail (x86_64-linux)
- **FOD hash:** Platform-specific — updates needed on each version bump
- **Output:** `$out/dist/` (JS/CSS bundles) and `$out/authentik/` (static icons)
- **Docusaurus website** (`/help` endpoint) is not built — optional
**Key lessons:**
- The 2026.2.0 build switched from rollup to esbuild for the main frontend
- The version string in `packages/core/version/node.js` uses a JSON import-with-assertion that must be patched to hardcode the version
- `NODE_OPTIONS=--openssl-legacy-provider` is needed for compatibility
- Workspace packages have separate `node_modules/` — the FOD must collect all of them
## Go Server
The Go HTTP server binary (`cmd/server`) that serves the web UI, REST API, and spawns gunicorn for the Django backend.
**What to do:**
1. Create a `buildGoModule` derivation for `cmd/server` from the authentik source
2. Inject the generated Go API client into the vendor directory (via `apiGoVendorHook`)
3. Apply `substituteInPlace` patches to hardcode Nix store paths for lifecycle scripts and web assets
4. Compute the `vendorHash` — the hook replaces vendored API code *after* hash verification
5. Rename the output binary from `server` to `authentik`
**Key details:**
- Go module: `goauthentik.io`, subpackage: `./cmd/server`
- CGO: disabled
- The `vendorHash` must be computed with the vendor replacement hook excluded (`overrideModAttrs`)
- Outpost binaries (`cmd/ldap`, `cmd/proxy`, `cmd/radius`) are separate and not needed for basic deployment
## Testing on Ringtail
The `test-build.nix` harness in `containers/authentik/` supports individual component builds:
```fish
set tmpdir (ssh ringtail 'mktemp -d /tmp/authentik-test.XXXXXX')
scp containers/authentik/*.nix ringtail:$tmpdir/
ssh ringtail "cd $tmpdir && nix-build test-build.nix -A client-go --extra-experimental-features 'nix-command flakes'"
ssh ringtail "cd $tmpdir && nix-build test-build.nix -A assembled --extra-experimental-features 'nix-command flakes'"
ssh ringtail "rm -rf $tmpdir"
```
## Related
- [[build-authentik-from-source]] — Parent overview and version update workflow
- [[mirror-authentik-build-deps]] — Supply chain mirrors for source repos
- [[deploy-authentik]] — Deployment goal

View file

@ -1,76 +0,0 @@
---
title: Build Authentik Python Backend
modified: 2026-03-01
last-reviewed: 2026-03-02
tags:
- how-to
- authentik
- nix
---
# Build Authentik Python Backend
Build `authentik-django` — the Python/Django application that forms the core backend of authentik.
## Context
Authentik 2026.2.0 requires Python 3.14 (`requires-python = "==3.14.*"`). The nixpkgs reference derivation (2025.12.4) builds all 60+ Python deps through nix's `python3.override` with `packageOverrides`. This approach breaks on Python 3.14 because many nixpkgs python314 packages haven't been updated — astor, dacite, exceptiongroup, and pydantic-core all fail to build.
Instead of carrying individual overrides for each broken package, we use **`uv`** to install Python dependencies from PyPI, where upstream maintainers have already published Python 3.14-compatible wheels. Nix provides only the Python interpreter and system libraries.
## Approach: uv sync FOD + autoPatchelfHook
Nix builds are sandboxed with no network access. The pattern is:
1. **Fixed-output derivation (FOD)**`uv sync --frozen` fetches and installs all dependencies into a venv. FODs are allowed network access because the output hash is declared upfront. Compiled `.so` files reference Nix store paths (RPATHs to libxml2, krb5, etc.), which FODs must not contain, so we strip references with `remove-references-to` and delete `bin/` and `.pyc` files.
2. **Main derivation** — copies the FOD's `lib/python3.14/site-packages/`, recreates `bin/` with proper python symlinks, restores `pyvenv.cfg`, and runs `autoPatchelfHook` to re-link `.so` files against the correct Nix store libraries.
**Why not `uv pip download` + `uv pip install --no-index`?** `uv pip download` does not exist in uv 0.9.29 (nixpkgs). And the download-only approach has further complications with sdist-only packages (psycopg-c, gssapi) that must be compiled anyway.
## What to Do
1. Create the FOD (`python-deps.nix`) that runs `uv sync --frozen --no-install-project --no-install-workspace --no-dev`, then strips all Nix store references from the output
2. Create the main derivation (`authentik-django.nix`) that:
- Copies the FOD's site-packages
- Recreates venv `bin/` and `pyvenv.cfg`
- Runs `autoPatchelfHook` to restore `.so` RPATHs
- Copies 4 in-tree workspace packages directly into site-packages
- Copies `authentik/` and `lifecycle/` into site-packages
- Copies `opencontainers` from `fetchFromGitHub` into site-packages
3. Apply `substituteInPlace` patches for Nix store paths in `settings.py`, `default.yml`, `email/utils.py`
4. Copy lifecycle scripts, `manage.py`, blueprints into the output
5. Verify: `$out/bin/python3.14 -c "import authentik"` succeeds
## Key Details
- Nix provides: `python314`, `uv`, system libraries (`libxml2`, `libxslt`, `openssl`, `libffi`, `zlib`, etc.)
- PyPI provides: all Python packages (via pre-built `cp314` wheels where available, sdist builds otherwise)
- The FOD hash must be recomputed when `uv.lock` changes
- `manylinux` wheels bundle some `.so` files — acceptable for a container image
- The 4 in-tree packages are installed from monorepo source, not PyPI
- Standard `djangorestframework` 3.16.1 from PyPI (no longer forked as of 2026.2.0)
## Lessons Learned
Build issues encountered and resolved:
| Issue | Fix |
|-------|-----|
| `pg_config` not found for psycopg-c | Use `pkgs.postgresql.pg_config` (separate derivation), not `pkgs.postgresql` |
| gssapi `gss_acquire_cred_impersonate_name` undeclared | `NIX_CFLAGS_COMPILE="-include gssapi/gssapi_ext.h"` — function is in `gssapi_ext.h`, not auto-included |
| xmlsec linker error `-lltdl` | Add `pkgs.libtool` to buildInputs (provides libltdl) |
| psycopg-c needs `libpq` | Add `pkgs.libpq` to buildInputs |
| Static `refTargets` list missed 6 store refs | Replaced with dynamic discovery: `grep -aohE '/nix/store/...'` finds all refs, `remove-references-to` strips them |
| `xargs grep` exit code 123 under `pipefail` | Wrap pipeline in `{ ... \|\| true; }` — grep returning 1 (no match) causes xargs to return 123 |
| `grep -aoE` includes filename prefix in output | Use `grep -aohE` (`-h` suppresses filenames) to get clean store paths |
| autoPatchelfHook can't find libraries | `buildInputs` in main derivation must include all libraries that `.so` files link against |
| `FieldError: Cannot resolve keyword 'group_id'` on startup | Django migration ordering bug: `authentik_rbac/0010` (drops `Role.group_id`) can run before `authentik_core/0056` (reads it). Add explicit dependency via `substituteInPlace` on the migration file. Upstream [#19616](https://github.com/goauthentik/authentik/issues/19616) |
The `uv sync` completes in ~3.5 minutes. Dynamic reference discovery finds 19 unique store paths and strips all of them. After stripping, `remove-references-to` mangles hashes to `eeee...` bytes — about 40 files still "contain" `/nix/store/` strings but with invalid hashes, which is expected and harmless. `autoPatchelfHook` in the main derivation resolves all NEEDED entries with 0 unsatisfied dependencies.
Build verified: `$out/bin/python3.14 -c "import authentik"` succeeds, along with all key dependencies (django 5.2.11, lxml, xmlsec, psycopg, guardian, opencontainers).
## Related
- [[build-authentik-from-source]] — Parent goal
- [[authentik-go-server-derivation]] — Depends on this for lifecycle scripts and web assets

View file

@ -1,41 +0,0 @@
---
title: Build Authentik Web UI
modified: 2026-03-01
last-reviewed: 2026-03-02
tags:
- how-to
- authentik
- nix
---
# Build Authentik Web UI
Build the Lit-based TypeScript web frontend for authentik.
## Overview
The web UI lives in `web/` in the authentik repo. As of 2026.2.0, the main build uses **esbuild** (via wireit) and the SFE sub-package uses **rollup**. The Nix build uses a two-phase approach:
1. **`webui-deps.nix`** — Fixed-output derivation that runs `npm ci` to fetch Node dependencies. Platform-specific output hash (npm downloads architecture-specific native binaries for esbuild, rollup, and SWC).
2. **`webui.nix`** — Copies deps, patches in the generated TypeScript API client (`client-ts`), patches shebangs, then runs `npm run build` (wireit/esbuild) and `npm run build:sfe` (rollup). Output includes `dist/` and `authentik/` static directories.
## Build Details
- **Node.js:** `nodejs_24` (authentik requires Node >= 24, npm >= 11.6.2)
- **Build time:** ~33s on ringtail (x86_64-linux)
- **FOD hash:** Platform-specific — will need updating on each authentik version bump
- **Output:** `$out/dist/` (JS/CSS bundles) and `$out/authentik/` (static SVG/PNG icons)
- **Consumed by:** Go server (`authentik-server.nix` via `webui` parameter) for static file serving, and `authentik-django.nix` for email template icon paths
- **Docusaurus website** (`/help` endpoint) is not built — optional and can be added later
## Key Lessons
- The 2026.2.0 build switched from rollup to esbuild for the main frontend. Only the SFE sub-package still uses rollup.
- The version string in `packages/core/version/node.js` uses a JSON import-with-assertion that doesn't resolve in the Nix sandbox — must be patched to hardcode the version.
- `NODE_OPTIONS=--openssl-legacy-provider` is needed for compatibility.
- Workspace packages have separate `node_modules/` directories — the FOD must collect all of them via `find`.
## Related
- [[build-authentik-from-source]] — Parent goal
- [[authentik-api-client-generation]] — Provides TypeScript client (prerequisite)

View file

@ -21,7 +21,7 @@ The nix-container-builder runner on ringtail resolves `nixpkgs` via the NixOS ni
Authentik has four build components assembled by `containers/authentik/default.nix`: Authentik has four build components assembled by `containers/authentik/default.nix`:
1. **API client generation** (`client-go.nix`, `client-ts.nix`) — Go and TypeScript bindings generated from `schema.yml` (OpenAPI) 1. **API client generation** (`client-go.nix`, `client-ts.nix`) — Go and TypeScript bindings generated from `schema.yml` (OpenAPI)
2. **Python backend** (`authentik-django.nix`) — Django application with 60+ Python dependencies installed via `uv` from PyPI (see [[authentik-python-backend-derivation]]) 2. **Python backend** (`authentik-django.nix`) — Django application with 60+ Python dependencies installed via `uv` from PyPI (see [[authentik-nix-build-components#Python Backend]])
3. **Web UI** (`webui.nix`) — Lit-based TypeScript frontend built with esbuild + rollup 3. **Web UI** (`webui.nix`) — Lit-based TypeScript frontend built with esbuild + rollup
4. **Go server** (`authentik-server.nix`) — HTTP server binary that serves the web UI and spawns gunicorn for Django 4. **Go server** (`authentik-server.nix`) — HTTP server binary that serves the web UI and spawns gunicorn for Django

View file

@ -32,4 +32,4 @@ Previously, `authentik-community/django-rest-framework` (a DRF fork) was also ne
## Related ## Related
- [[build-authentik-from-source]] — Parent goal - [[build-authentik-from-source]] — Parent goal
- [[authentik-api-client-generation]] — Consumes client-go mirror - [[authentik-nix-build-components]] — Consumes client-go mirror

View file

@ -0,0 +1,38 @@
---
title: Build Caddy with Plugins
modified: 2026-03-15
last-reviewed: 2026-03-15
tags:
- how-to
- caddy
- networking
---
# Build Caddy with Plugins
Caddy is built from source using `xcaddy` with two plugins:
- `github.com/caddy-dns/gandi` — ACME DNS-01 challenges via Gandi API
- `github.com/mholt/caddy-l4` — Layer 4 (TCP/UDP) proxying
## How to Build
```bash
# Source and build location (mirrored on forge)
~/code/3rd/caddy/bin/caddy
# Build via mise task in the caddy clone
cd ~/code/3rd/caddy && mise run build
```
## Forge Mirrors
- `mirrors/caddy`
- `mirrors/caddy-gandi`
- `mirrors/xcaddy`
- `mirrors/caddy-l4`
## Related
- [[caddy]] — Service reference card
- [[routing]] — Service routing architecture

View file

@ -1,40 +0,0 @@
---
title: Build Grafana Container
modified: 2026-02-28
last-reviewed: 2026-02-28
tags:
- how-to
- grafana
- containers
---
# Build Grafana Container
Home-built Grafana container image published to `registry.ops.eblu.me/blumeops/grafana`.
## How It Works
The Dockerfile at `containers/grafana/Dockerfile` downloads the official Grafana OSS tarball for the target architecture (arm64/amd64), installs it into Alpine, and sets up standard paths.
To build and push a new version:
```fish
# Update version in Dockerfile
# ARG CONTAINER_APP_VERSION=12.3.3
mise run container-build-and-release grafana
```
## Gotchas
- **Tarball directory name:** Extracts to `grafana-<version>` (e.g. `grafana-12.3.3`), *not* `grafana-v<version>`.
- **Binary PATH:** The binary lives at `bin/grafana` inside the extracted directory. The Dockerfile sets `ENV PATH="/usr/share/grafana/bin:$PATH"`.
- **UID 472:** Matches the official Grafana image for PVC ownership compatibility.
## Related
- [[grafana]] — Service reference card
- [[upgrade-grafana]] — Migration context
- [[kustomize-grafana-deployment]] — Kustomize manifest structure
- [[build-grafana-sidecar]] — Home-built sidecar container
- [[build-container-image]] — Standard container build workflow

View file

@ -0,0 +1,60 @@
---
title: Build Grafana Images
modified: 2026-03-15
last-reviewed: 2026-03-15
tags:
- how-to
- grafana
- containers
---
# Build Grafana Images
Home-built container images for Grafana and its dashboard sidecar, published to `registry.ops.eblu.me/blumeops/`.
## Grafana
**Dockerfile:** `containers/grafana/Dockerfile`
**Image:** `registry.ops.eblu.me/blumeops/grafana`
Downloads the official Grafana OSS tarball for the target architecture (arm64/amd64), installs it into Alpine, and sets up standard paths.
```fish
# Update version in Dockerfile
# ARG CONTAINER_APP_VERSION=12.3.3
mise run container-build-and-release grafana
```
**Gotchas:**
- **Tarball directory name:** Extracts to `grafana-<version>` (e.g. `grafana-12.3.3`), *not* `grafana-v<version>`.
- **Binary PATH:** The binary lives at `bin/grafana` inside the extracted directory. The Dockerfile sets `ENV PATH="/usr/share/grafana/bin:$PATH"`.
- **UID 472:** Matches the official Grafana image for PVC ownership compatibility.
## Grafana Sidecar
**Dockerfile:** `containers/grafana-sidecar/Dockerfile`
**Image:** `registry.ops.eblu.me/blumeops/grafana-sidecar`
Clones the [kiwigrid/k8s-sidecar](https://github.com/kiwigrid/k8s-sidecar) source from the forge mirror, installs Python dependencies into a venv, and copies the application into a minimal Alpine runtime image.
```fish
# Update version in Dockerfile
# ARG CONTAINER_APP_VERSION=1.28.0
mise run container-build-and-release grafana-sidecar
```
**Gotchas:**
- **Pinned to v1.28.0:** v2.x has a 135% memory regression ([#462](https://github.com/kiwigrid/k8s-sidecar/issues/462)) and `readOnlyRootFilesystem` crashloop ([#3936](https://github.com/grafana/helm-charts/issues/3936)). Upgrade separately after upstream fixes land.
- **UID 65534:** Matches upstream's `nobody` user convention for non-root execution.
- **Forge mirror name:** `mirrors/kiwigrid-grafana-sidecar` (not `k8s-sidecar`).
## Related
- [[grafana]] — Service reference card
- [[upgrade-grafana]] — Migration context and future upgrade steps
- [[kustomize-grafana-deployment]] — Kustomize manifest structure
- [[build-container-image]] — Standard container build workflow

View file

@ -1,39 +0,0 @@
---
title: Build Grafana Sidecar
modified: 2026-03-03
last-reviewed: 2026-03-03
tags:
- how-to
- grafana
- containers
---
# Build Grafana Sidecar
Home-built k8s-sidecar container image published to `registry.ops.eblu.me/blumeops/grafana-sidecar`.
## How It Works
The Dockerfile at `containers/grafana-sidecar/Dockerfile` clones the [kiwigrid/k8s-sidecar](https://github.com/kiwigrid/k8s-sidecar) source from the forge mirror, installs Python dependencies into a venv, and copies the application into a minimal Alpine runtime image.
To build and push a new version:
```fish
# Update version in Dockerfile
# ARG CONTAINER_APP_VERSION=1.28.0
mise run container-build-and-release grafana-sidecar
```
## Gotchas
- **Pinned to v1.28.0:** v2.x has a 135% memory regression ([#462](https://github.com/kiwigrid/k8s-sidecar/issues/462)) and `readOnlyRootFilesystem` crashloop ([#3936](https://github.com/grafana/helm-charts/issues/3936)). Upgrade separately after upstream fixes land.
- **UID 65534:** Matches upstream's `nobody` user convention for non-root execution.
- **Forge mirror name:** `mirrors/kiwigrid-grafana-sidecar` (not `k8s-sidecar`).
## Related
- [[grafana]] — Service reference card
- [[build-grafana-container]] — Home-built Grafana container
- [[kustomize-grafana-deployment]] — Kustomize manifest structure
- [[build-container-image]] — Standard container build workflow

View file

@ -34,5 +34,5 @@ Grafana is deployed via plain Kustomize manifests in `argocd/manifests/grafana/`
## Related ## Related
- [[upgrade-grafana]] — Migration context - [[upgrade-grafana]] — Migration context
- [[build-grafana-sidecar]] — Home-built sidecar container - [[build-grafana-images]] — Home-built container images
- [[grafana]] — Service reference card - [[grafana]] — Service reference card

View file

@ -43,6 +43,5 @@ The SQLite PVC is disposable — dashboards come from ConfigMaps and datasources
## Related ## Related
- [[grafana]] — Service reference card - [[grafana]] — Service reference card
- [[build-grafana-container]] — Building the container image - [[build-grafana-images]] — Building the container images
- [[build-grafana-sidecar]] — Building the dashboard sidecar image
- [[kustomize-grafana-deployment]] — Kustomize manifest structure - [[kustomize-grafana-deployment]] — Kustomize manifest structure

View file

@ -87,20 +87,7 @@ Caddy has no authentication layer — it is a plain reverse proxy. Access contro
## Custom Build ## Custom Build
Caddy is built from source using `xcaddy` with two plugins: Custom `xcaddy` build with Gandi DNS and L4 plugins. See [[build-caddy-with-plugins]] for build instructions and forge mirror details.
- `github.com/caddy-dns/gandi` — ACME DNS-01 challenges via Gandi API
- `github.com/mholt/caddy-l4` — Layer 4 (TCP/UDP) proxying
```bash
# Source and build location (mirrored on forge)
~/code/3rd/caddy/bin/caddy
# Build via mise task in the caddy clone
cd ~/code/3rd/caddy && mise run build
```
Forge mirrors: `mirrors/caddy`, `mirrors/caddy-gandi`, `mirrors/xcaddy`, `mirrors/caddy-l4`.
## Related ## Related

View file

@ -59,8 +59,7 @@ Optional annotation: `grafana_folder: "FolderName"`
## Related ## Related
- [[build-grafana-container]] - Home-built container image - [[build-grafana-images]] - Home-built container images (Grafana + sidecar)
- [[build-grafana-sidecar]] - Home-built sidecar container
- [[kustomize-grafana-deployment]] - Kustomize manifest structure - [[kustomize-grafana-deployment]] - Kustomize manifest structure
- [[authentik]] - OIDC identity provider for SSO - [[authentik]] - OIDC identity provider for SSO
- [[migrate-grafana-to-authentik]] - How SSO was migrated from Dex to Authentik - [[migrate-grafana-to-authentik]] - How SSO was migrated from Dex to Authentik

View file

@ -1,9 +1,9 @@
--- ---
title: Expose a Service Publicly title: Expose a Service Publicly
modified: 2026-03-03 modified: 2026-03-15
last-reviewed: 2026-03-03 last-reviewed: 2026-03-03
tags: tags:
- how-to - tutorials
- fly-io - fly-io
- tailscale - tailscale
- networking - networking