# Nic Debugging

> Debugging and troubleshooting patterns for NIC. Use when diagnosing failures, tracing issues, investigating NGINX reload errors, config generation bugs, or controller sync problems.

- Skill: `nginx/nic-debugging` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nginx/nic-debugging`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nginx/nic-debugging/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: nginx (https://skillmd.com/u/nginx)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/nginx/nic-debugging

---


# Debugging and Troubleshooting

## Common Failure Modes

### NGINX Reload Failure

**Symptom:** Controller logs show "reload failed" or NGINX returns error status.

**Diagnosis:**

1. Check controller logs for the generated config that failed
2. Look for `nginx -t` output in logs — shows exact syntax error and line number
3. Common causes:
   - Unsanitized user string injected into config (missing `containsDangerousChars()` check)
   - Template guard missing (`{{- if }}` / `{{- with }}`) for optional field
   - Duplicate directive from conflicting policies
   - Invalid upstream when no endpoints available

**Fix pattern:**

- Find the template or config generation code that produced the bad directive
- Add validation to reject the input earlier, OR fix the template guard
- Verify with `make test` — snapshot tests catch most template output issues

### CRD Not Taking Effect

**Symptom:** User applies VirtualServer/Policy but NGINX config doesn't change.

**Diagnosis:**

1. Check CRD status: `kubectl get vs <name> -o yaml` — look at `.status.message`
2. Check controller logs for sync errors on that resource
3. Common causes:
   - Validation rejecting the resource (check `status.state: Invalid`)
   - Missing secret reference (TLS, JWT, OIDC secrets)
   - Policy referenced but not found in namespace
   - Resource conflicts (duplicate host/path)

### Controller Crash / Panic

**Symptom:** Pod restarts, panic in logs.

**Diagnosis:**

1. Check logs for the panic stack trace
2. Common causes:
   - Nil pointer on optional CRD field (forgot `*bool`/`*int` check)
   - Map access without nil check on `.Spec.X` field
   - Race condition in concurrent secret/config access
3. Look for the file:line in the stack trace → usually in `internal/configs/` or `internal/k8s/`

### Snapshot Test Failure

**Symptom:** `make test` fails with snapshot mismatch.

**Diagnosis:**

1. This means template output changed — could be intentional or regression
2. Review the diff shown in test output
3. If change is intentional: `make test-update-snaps`, then re-read `git diff -- '**/__snapshots__/**'` to confirm only the expected directives moved
4. If change is unintentional: your template edit had side effects — fix the template

**The inverse failure is more dangerous:** you edited a `.tmpl` and `make test-update-snaps` produced **no** diff. That is not a pass — it means no fixture sets the field your new branch depends on. Add the fixture in `template_test.go` / `templates_test.go` and regenerate.

## Log Locations

| Context | Location | What to look for |
| --- | --- | --- |
| Controller logs | Pod stdout/stderr | Sync errors, reload status, validation failures |
| NGINX error log | `/var/log/nginx/error.log` in container | Config syntax errors, upstream failures |
| NGINX access log | `/var/log/nginx/access.log` in container | Request routing verification |

## Validation and Diagnostic Tools

| Tool | Command | Purpose |
| --- | --- | --- |
| Config test | `nginx -t` (inside container) | Validate NGINX config syntax |
| CRD status | `kubectl get vs,vsr,ts,pol -A` | Check resource state |
| Controller logs | `kubectl logs <pod> -n nginx-ingress` | Runtime errors |
| Describe events | `kubectl describe vs <name>` | Kubernetes events for the resource |
| Generated config | `kubectl exec <pod> -- cat /etc/nginx/conf.d/<file>` | Inspect actual generated NGINX config |

## Debugging Workflow

1. **Reproduce** — Get the exact error. Is it a reload failure? Wrong routing? Crash?
2. **Assess security impact** — Before diving into the fix, ask:
   - Is this bug exploitable? (Can external input trigger it?)
   - Does the failure expose sensitive data in logs or error messages?
   - Could an attacker craft input to reach this code path?
   - If exploitable: flag for security review BEFORE fixing
3. **Locate the layer** — Use logs and status to determine:
   - Validation layer? → `status.state: Invalid` with reason
   - Config generation? → Generated config has wrong directives
   - Template? → Snapshot test shows the issue
   - Controller? → Sync error in logs, resource not processed
4. **Isolate** — Find minimum CRD/annotation that triggers the issue
5. **Fix** — Make the change in the correct layer (don't fix templates for validation bugs)
6. **Verify** — `make test` passes, snapshot output is correct
7. **Prevent** — Add a test case that would catch this regression (include negative/malicious input tests if the bug was in a validation path)

## Config Generation Debugging

When the generated NGINX config is wrong:

1. **Find the template struct** — Which struct feeds the template? Check `internal/configs/version2/http.go` (VS) or `internal/configs/version1/config.go` (Ingress)
2. **Find the config generator** — Where is the struct populated? Check `internal/configs/virtualserver.go` or `internal/configs/ingress.go`
3. **Find the template** — Which `.tmpl` file renders it? Check `internal/configs/version2/nginx-plus.virtualserver.tmpl` or the OSS variant
4. **Add a snapshot test** — Create a test case in the appropriate `_test.go` file with the input that triggers the bug, run `make test-update-snaps` to capture current (wrong) output, then fix and regenerate

## Common Gotchas When Debugging

- NGINX config errors show line numbers in the GENERATED file, not your template — map back manually
- Secret-related failures often show as "file not found" in NGINX logs (secret not written to filesystem yet)
- Policy ordering matters — first matching policy wins, check `generatePolicies()` logic
- Plus-only features will work in Plus template but silently produce invalid config in OSS template
- `containsDangerousChars()` failures are validation errors and typically result in `status.state: Invalid` — check the CRD status message and controller logs

## Security-Sensitive Debugging

When debugging an issue that involves user-provided input reaching NGINX config:

1. **Trace the input path** — From CRD field / annotation → validation → config struct → template → NGINX config file. Identify every point where sanitization SHOULD happen.
2. **Check for injection** — Can crafted input inject NGINX directives? Look for `;`, `{`, `}`, `$`, newlines, backticks in the user-controlled value.
3. **Verify the guard** — Does `containsDangerousChars()` or `ValidateEscapedString()` cover this path? If not, the bug is a security vulnerability.
4. **Never log secrets** — When debugging TLS/JWT/OIDC issues, mask credential values. Log key names and paths, not contents.
5. **Check RBAC** — If the issue involves unauthorized access, verify ServiceAccount permissions and RBAC role bindings before looking at code.

