Troubleshooting
Common issues and solutions
Common problems and their solutions when using the DB Query Operator.
Connection Issues
Error: "connection refused"
Symptom:
Error: failed to connect to database: dial tcp :5432: connect: connection refusedCauses:
- Database not accessible from cluster
- Incorrect host/port in secret
- Network policies blocking traffic
Solutions:
Check Database Secret
kubectl get secret postgres-credentials -n my-namespace -o yamlVerify fields:
hostorfqdn-uriis correctportmatches database (usually5432)- Credentials are valid
Test Connection from Pod
kubectl run -it --rm debug --image=postgres:15 --restart=Never -- \
psql -h db-host.namespace.svc.cluster.local -U myuser -d mydbCheck Network Policies
kubectl get networkpolicies -A
kubectl describe networkpolicy <policy-name> -n <namespace>Error: "FATAL: password authentication failed"
Symptom:
Error: failed to authenticate: FATAL: password authentication failed for user "myuser"Solutions:
-
Verify Secret Keys:
kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 -d -
Check Secret Ref Configuration:
spec: database: connectionSecretRef: name: my-secret passwordKey: password # Must match secret key name usernameKey: username -
Test Credentials Manually:
PGPASSWORD="actual-password" psql -h host -U username -d database
Error: "no such host"
Symptom:
Error: lookup db-host.wrong-namespace: no such hostCause: Incorrect hostname for cross-namespace database access.
Solution: Use URI-based connection with FQDN:
spec:
database:
connectionSecretRef:
name: postgres-creds
namespace: database-namespace
uriKey: fqdn-uri # CloudNativePG provides thisOr specify full hostname:
# In secret
host: postgres-cluster-rw.database-namespace.svc.cluster.localQuery Errors
Error: "cannot insert multiple commands into a prepared statement"
Symptom:
Error executing query: cannot insert multiple commands into a prepared statementCause: Using multi-statement query with older operator version.
Solution: Upgrade to v0.6.0+ which supports multi-statement queries via pgx.Batch:
helm upgrade db-query-operator \
oci://ghcr.io/konnektr-io/charts/db-query-operator \
--version 0.6.0 \
-n dbqo-systemExample Working Query:
spec:
query: |
SET statement_timeout = '30s';
SET search_path = myschema, public;
SELECT id, name, config
FROM my_table
WHERE enabled = true;Error: "relation does not exist"
Symptom:
Error: relation "my_table" does not existSolutions:
-
Verify Table Name:
\dt -- List tables in psql SELECT tablename FROM pg_tables WHERE schemaname = 'public'; -
Check Schema:
spec: query: | SET search_path = my_schema, public; SELECT * FROM my_table; -
Verify Database Name: Ensure
databaseKeyin secret points to correct database.
Error: "column does not exist"
Symptom:
Error: column "my_column" does not existSolutions:
-
Check Column Names:
SELECT column_name FROM information_schema.columns WHERE table_name = 'my_table'; -
Case Sensitivity: PostgreSQL folds unquoted identifiers to lowercase:
-- Wrong (if column is actually "myColumn") SELECT MyColumn FROM table; -- Right SELECT "MyColumn" FROM table; -- Or rename in query SELECT my_column FROM table;
Template Rendering Errors
Error: "template: :X:Y: executing template"
Symptom:
Error rendering template: template: :12:5: executing "template" at <.Row.missing_field>:
map has no entry for key "missing_field"Cause: Template references field not returned by query.
Solutions:
-
Verify Query Returns Field:
-- Check actual query output SELECT * FROM my_table LIMIT 1; -
Use Default Value:
resourceTemplate: | data: value: {{ .Row.optional_field | default "default-value" }} -
Conditional Rendering:
resourceTemplate: | {{- if .Row.optional_field }} data: value: {{ .Row.optional_field }} {{- end }}
Error: "yaml: line X: did not find expected key"
Symptom: Resources not created, YAML parsing errors in logs.
Cause: Template renders invalid YAML.
Solutions:
-
Test Template Locally:
# Save template to file cat > template.yaml << 'EOF' apiVersion: v1 kind: ConfigMap metadata: name: test-{{ .Row.id }} data: value: {{ .Row.value }} EOF # Test with sample data echo '{"id": "123", "value": "test"}' | \ jq -r 'to_entries | map(.key + "=" + (.value | tostring)) | .[]' | \ ... # Use Go template engine -
Quote String Values:
# Wrong data: value: {{ .Row.text }} # Right data: value: {{ .Row.text | quote }} -
Check for Special Characters:
# Escape special YAML characters data: value: {{ .Row.text | quote }} json: {{ .Row.data | toJson | quote }}
Resource Management Issues
Resources Not Created
Check DatabaseQueryResource Status:
kubectl describe databasequeryresource my-dbqr -n my-namespaceCommon Causes:
-
Query Returns No Rows:
- Verify query in database directly
- Check WHERE clauses
-
Template Rendering Failed:
- Check operator logs for template errors
- Validate template syntax
-
RBAC Permissions Missing:
# Check operator logs kubectl logs -n dbqo-system deployment/db-query-operator | grep "forbidden"Solution: Update operator RBAC or gvkPattern:
helm upgrade db-query-operator ... \ --set gvkPattern="v1/ConfigMap;apps/v1/Deployment"
Resources Not Pruned
Symptom: Resources remain after removing database rows.
Solutions:
-
Enable Pruning:
spec: prune: true # Default, but verify -
Check Owner References (same-namespace only):
kubectl get configmap my-resource -o yaml | grep -A5 ownerReferences -
Cross-Namespace Resources: Pruning for cross-namespace resources uses labels/tracking:
# Check if resource is tracked kubectl get configmap -n other-namespace my-resource -o yaml | grep "managed-by"
Resources Keep Reverting
Symptom: Manual kubectl edit changes are reverted.
Cause: Server-Side Apply - operator owns those fields.
Solutions:
-
Update Database Instead:
UPDATE my_table SET value = 'new-value' WHERE id = 'my-resource'; -
Change Non-Managed Fields (safe):
kubectl label configmap my-resource custom-label=value kubectl annotate configmap my-resource custom-annotation=value -
Disable Operator for Resource:
-- Remove from query results temporarily UPDATE my_table SET enabled = false WHERE id = 'my-resource';
Performance Issues
High Database Load
Symptoms:
- High CPU/memory on database
- Slow query responses
- Connection pool exhaustion
Solutions:
-
Enable Change Detection:
spec: changeDetection: enabled: true tableName: "my_table" timestampColumn: "updated_at" -
Increase Poll Interval:
spec: pollInterval: "5m" # From 30s -
Optimize Query:
-- Add indexes CREATE INDEX idx_enabled ON my_table(enabled) WHERE enabled = true; CREATE INDEX idx_updated ON my_table(updated_at); -- Use indexed columns in WHERE SELECT * FROM my_table WHERE enabled = true; -
Limit Result Set:
-- Only active records SELECT * FROM my_table WHERE enabled = true; -- Recent changes only SELECT * FROM my_table WHERE updated_at > NOW() - INTERVAL '24 hours';
Slow Resource Creation
Symptoms:
- Long reconciliation times
- Resources appear slowly
Solutions:
-
Simplify Template:
# Avoid complex logic in templates # Move logic to SQL query instead -
Check Network Latency:
kubectl run -it --rm debug --image=nicolaka/netshoot -- \ time curl -k https://kubernetes.default.svc -
Review Operator Logs:
kubectl logs -n dbqo-system deployment/db-query-operator -f
CRD and RBAC Issues
Error: "no matches for kind DatabaseQueryResource"
Cause: CRD not installed.
Solution:
# Check CRD
kubectl get crd databasequeryresources.konnektr.io
# Install/upgrade operator
helm upgrade --install db-query-operator \
oci://ghcr.io/konnektr-io/charts/db-query-operator \
-n dbqo-system --create-namespace \
--set gvkPattern="v1/ConfigMap"Error: "forbidden: User cannot create resource"
Symptom:
Error: failed to apply resource: forbidden: User "system:serviceaccount:dbqo-system:db-query-operator"
cannot create resource "deployments" in API group "apps"Cause: Operator ServiceAccount lacks RBAC permissions.
Solution:
-
Add Resource Type to gvkPattern:
helm upgrade db-query-operator ... \ --set gvkPattern="v1/ConfigMap;apps/v1/Deployment" -
For Cross-Namespace Resources, manually create ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: db-query-operator-cross-ns rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["get", "list", "create", "update", "patch", "delete"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: db-query-operator-cross-ns roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: db-query-operator-cross-ns subjects: - kind: ServiceAccount name: db-query-operator namespace: dbqo-system
Debugging Checklist
When troubleshooting, check in order:
-
DatabaseQueryResource Status:
kubectl describe databasequeryresource <name> -n <namespace> -
Operator Logs:
kubectl logs -n dbqo-system deployment/db-query-operator --tail=100 -f -
Database Connectivity:
kubectl run -it --rm debug --image=postgres:15 -- \ psql "$(kubectl get secret <secret> -o jsonpath='{.data.fqdn-uri}' | base64 -d)" -
Query Results:
-- Run query directly in database SELECT * FROM my_table; -
Template Rendering:
- Copy template to local file
- Test with sample data
- Validate resulting YAML
-
Resource Creation:
kubectl get <resource-kind> -A -l managed-by=db-query-operator -
RBAC Permissions:
kubectl auth can-i create deployments \ --as=system:serviceaccount:dbqo-system:db-query-operator \ -n <target-namespace>
Getting Help
Logs to Collect
# Operator logs
kubectl logs -n dbqo-system deployment/db-query-operator --tail=200 > operator.log
# DBQR description
kubectl describe databasequeryresource <name> -n <namespace> > dbqr.yaml
# CRD definition
kubectl get databasequeryresource <name> -n <namespace> -o yaml > dbqr-spec.yamlInformation to Provide
When reporting issues:
- Operator version (
helm list -n dbqo-system) - Kubernetes version (
kubectl version) - Database type and version
- Sanitized DBQR spec (remove credentials)
- Operator logs showing error
- Query results (sample)
Community Support
- GitHub Issues: github.com/konnektr-io/db-query-operator/issues
- Documentation: docs.konnektr.io/docs/db-query-operator
Frequently Asked Questions
Can I use multiple databases?
Yes, create separate DatabaseQueryResource objects with different connectionSecretRef.
Does it support MySQL/MongoDB/etc?
Currently PostgreSQL only. Other databases planned for future releases.
Can one DBQR create multiple resource types?
No, one DBQR creates one resource type (GVK). Create multiple DBQRs for different types.
What happens if the database is down?
The operator logs errors and retries on next pollInterval. Existing resources remain unchanged.
Can I use with read replicas?
Yes, point connection secret to read replica host for read-only queries.
Does it support database transactions?
No, each query auto-commits. Use database-level constraints for consistency.
Can templates call external APIs?
No, templates are pure Go templates with Sprig functions. No network calls.
How do I rotate database credentials?
Update the Secret referenced by connectionSecretRef. Operator will reconnect automatically on next reconciliation.
Next Steps
- Review Examples for working configurations
- See Advanced Topics for optimization strategies
- Check Configuration Reference for all options