Kubernetes Deployment
Deploy the AICR API Server in your Kubernetes cluster for self-hosted recipe generation.
Overview
API Server deployment enables self-hosted recipe generation:
- Isolated deployment: Recipe data stays within your infrastructure
- Custom recipes: Modify embedded recipe data (see
recipes/) - High availability: Deploy multiple replicas with load balancing
- Observability: Prometheus
/metricsendpoint and structured logging
API Server scope:
- Recipe generation from query parameters (query mode)
- Does not capture snapshots (use agent Job or CLI)
- Generates bundles via
POST /v1/bundle - Does not analyze snapshots (query mode only)
Agent deployment (separate component):
- Kubernetes Job captures cluster configuration
- Writes snapshot to ConfigMap via Kubernetes API
- Requires RBAC: ServiceAccount with ConfigMap create/update permissions
- See Agent Deployment
Typical workflow:
- Deploy agent Job → Captures snapshot → Writes to ConfigMap
- CLI reads ConfigMap → Generates recipe → Writes to file or ConfigMap
- CLI reads recipe → Generates bundle → Writes to filesystem
- Apply bundle to cluster (Helm install, kubectl apply)
Quick Start
Helm chart: Not yet available. Use the manual manifests below.
Manual Deployment
1. Create Namespace
2. Create Deployment
3. Create Service
4. Create Ingress (Optional)
Capturing Snapshots (Agent)
The API server only generates recipes and bundles — it does not capture
cluster state. Snapshot capture is a separate concern handled by the AICR
agent Job, including its RBAC (ServiceAccount, Role, ClusterRole), the
privileged-mode requirement, ConfigMap storage (cm://<ns>/<name>), and the
full snapshot → recipe → bundle CLI flow. That material is documented
canonically in Agent Deployment and is not
duplicated here.
Configuration Options
Environment Variables
Note: These are the only environment variables the API server reads for criteria filtering and transport; server-side bundle signing (POST /v1/bundle?attest=true) reads an additional set documented in API Reference › Server-Side Signing. The four AICR_ALLOWED_* allowlists are parsed once at startup to restrict which criteria values the server will accept. Rate-limit, request-timeout, and body-size settings are compiled-in constants from pkg/defaults, not environment-tunable. The server uses structured JSON logging to stderr. The CLI supports three logging modes (CLI/Text/JSON), but the API server always uses JSON for consistent log aggregation.
Network Egress from the Vendor-Charts Path
POST /v1/bundle?vendor-charts=true performs server-side helm pull against
the repository URL declared by each component in the submitted recipe. Four
controls keep this endpoint safe by default:
- Opt-in gate. Off unless the operator sets
AICR_ALLOW_VENDOR_CHARTS=true. The bundle handler rejectsvendor-charts=truewith400when the server is not opted in, so an accidentally-exposed instance never performs egress on behalf of a request. - Repository egress policy. Even with opt-in, the vendor layer rejects repository hosts that resolve to loopback, link-local, RFC1918 / CGNAT / ULA private ranges, multicast, unspecified, or the well-known cloud- metadata IPs (169.254.169.254, 100.100.100.200, fd00:ec2::254, fe80::a9fe:a9fe).
- Index-yaml pre-check (HTTP(S) only). Before invoking
helm pull, the server fetches<repo>/index.yamlthrough a hardened HTTP client (bounded body, redirect-hops validated against the same egress policy), parses the entries for the requested chart+version, resolves relative URLs, and rejects the request if ANY declared tarball URL points at a disallowed host. This closes the classic “public index.yaml points at a private-network tarball” SSRF vector at pre-check time. - Artifact size cap. The pulled
.tgzis capped at 64 MiB — well above real charts, low enough to bound server memory.
Residual risks that require operator-side controls:
- DNS rebinding between the pre-check and helm’s own re-resolution when it actually fetches (helm re-resolves without exposing the resolved IP to us).
- HTTP redirects during helm’s tarball fetch — helm is a subprocess and its redirect hops are not visible to the pre-check.
- OCI protocol — the OCI distribution redirect chain (manifest → blob GETs, which registries commonly redirect to a CDN URL) is not intercepted.
- Resolver divergence — the pre-check re-implements Helm’s semver
constraint resolution to select which chart-version entry from the
fetched
index.yamlto egress-check. Helm itself re-fetches the index and re-resolves independently when it actually pulls, so the URL the pre-check egress-validated can differ from the URL Helm pulls if the index changes between calls or if the two resolvers pick differently under ambiguous inputs — a defense-in-depth check that can bit-rot as Helm evolves.
For all four, the operational control is a Kubernetes NetworkPolicy on
the aicrd pod or an equivalent egress firewall that allow-lists only the
public chart registries the deployment needs. If you cannot enforce that
network boundary, keep AICR_ALLOW_VENDOR_CHARTS off and front the server
with authenticated ingress. A follow-up will move the tarball fetch
in-process to close these residuals without needing a network-layer
control.
ConfigMap for Custom Recipe Data (Advanced)
Note: The
aicrdHTTP server resolves recipes from the binary’s embedded catalog only — it is not wired to consume an external recipe-data overlay at runtime, so there is no ConfigMap mount or environment variable that injects custom recipe data into the server.Custom recipe data (
overlays/*.yaml,validators/catalog.yaml, …) is layered on top of the embedded catalog via theaicrCLI’s--data <dir>flag (see Data Extension). To serve customized data over HTTP today, bake the additional overlays/catalog entries into a customaicrdimage rather than mounting them at runtime. (Note that the data layout is a directory tree, and ConfigMap data keys must match[-._a-zA-Z0-9]+— they cannot contain/, so a flat ConfigMap cannot represent theoverlays/…layout in any case.)
High Availability
Horizontal Pod Autoscaler
Pod Disruption Budget
Monitoring
Prometheus ServiceMonitor
Grafana Dashboard
Key panels:
- Request rate (by status code)
- Request duration (p50, p95, p99)
- Error rate
- Rate limit rejections
- Active connections
Security
Network Policies
Pod Security Standards
RBAC (If API server needs K8s access)
Troubleshooting
Check Pod Status
Check Service
Check Ingress
Performance Issues
Connection Refused
- Check service exists:
kubectl get svc -n aicr - Check endpoints:
kubectl get endpoints -n aicr - Check pod is ready:
kubectl get pods -n aicr - Check readiness probe:
kubectl describe pod -n aicr <pod-name>
Rate Limiting
Rate-limit settings are compiled-in constants from pkg/defaults; the
server does not read RATE_LIMIT/RATE_BURST (or any rate-limit) environment
variables. To change the effective limits, front the server with an
ingress/gateway that enforces its own rate limit (see the Ingress example
above, which sets nginx.ingress.kubernetes.io/rate-limit), or build a custom
aicrd image with adjusted pkg/defaults values.
Rate-limit rejections surface in the aicr_rate_limit_rejects_total metric and
as HTTP 429 responses with the X-RateLimit-* headers.
Upgrading
Rolling Update
The aicrd server is stateless — it holds no persistent data, so there is
nothing to back up beyond the manifests in this guide (keep them in version
control). Standard Kubernetes patterns apply unchanged for blue-green/canary
rollouts, backup/restore of resource definitions, and right-sizing requests
and limits (start small — see the requests/limits in the
Deployment above — and adjust from kubectl top
output or a Vertical Pod Autoscaler). Refer to the upstream
Kubernetes documentation
for these; none require AICR-specific handling.
See Also
- API Reference - API endpoint documentation
- Automation - CI/CD integration
- Data Flow - Understanding data architecture
- API Server Architecture - Internal architecture