Skip to content
Operating on Workflow Automation
Operating on Workflow Automation

Operating on Workflow Automation

Last updated 2026-07-15 ·47fbb51

This is the operational companion to Workflow Automation with n8n. That post covers the architecture and deployment. This one covers day-to-day operations for n8n instances.

    graph TB
    subgraph ns["n8n-01 namespace"]
        n8n["n8n Pod<br/>Workflow Engine"]
        pg["PostgreSQL Pod<br/>n8n Database"]
        n8n ---|"DB connection"| pg

        subgraph mounts["Persistent Storage"]
            direction TB
            config["Config PVC"]
            data["Data PVC"]
        end
        n8n --- config
        n8n --- data
    end

    subgraph svc["Service Layer"]
        direction TB
        lb["LoadBalancer Service<br/>External IP"]
        cluster["ClusterIP Service<br/>Internal"]
    end

    subgraph users["Access"]
        direction TB
        ui["Web UI<br/>/healthz"]
        api["REST API"]
        agent["Agent Session<br/>Sidecar"]
    end

    n8n --- cluster
    cluster --- lb
    lb --> ui
    lb --> api
    n8n -.- agent

    subgraph metrics["Observability"]
        direction TB
        probe["Blackbox Probe<br/>/healthz"]
        met["/metrics endpoint"]
    end
    n8n --- met
    probe -.->|"GET /healthz"| n8n
  

What Healthy Looks Like

  • The n8n pod is 1/1 Running on gpu-1.
  • The PostgreSQL pod is 1/1 Running in the same namespace.
  • The LoadBalancer Service shows an assigned external IP.
  • GET /healthz returns 200.
  • Metrics flow at GET /metrics.

Verify

# Both pods
kubectl -n n8n-01 get pods

# Health endpoint (from inside the cluster)
kubectl -n n8n-01 exec deploy/n8n -- wget -qO- localhost:5678/healthz

# Metrics
kubectl -n n8n-01 exec deploy/n8n -- wget -qO- localhost:5678/metrics | head -10

# Database
kubectl -n n8n-01 exec deploy/n8n-postgresql -- psql -U n8n -c "SELECT 1;"
$ kubectl -n n8n-01 get pods
NAME                                READY   STATUS    RESTARTS   AGE
n8n-7c9f5b6d8f-abc12               1/1     Running   0          14d
n8n-postgresql-0                    1/1     Running   0          14d

$ kubectl -n n8n-01 exec deploy/n8n -- wget -qO- localhost:5678/healthz
{"status":"ok"}

Steps

Restart n8n

kubectl -n n8n-01 rollout restart deployment/n8n
kubectl -n n8n-01 rollout status deployment/n8n

# Wait for health check
until kubectl -n n8n-01 exec deploy/n8n -- wget -qO- localhost:5678/healthz; do
  sleep 2
done

Add a New Instance

  1. Copy apps/n8n/ to apps/n8n-02/.
  2. Update values (namespace, PG credentials, external URL).
  3. Add the ArgoCD Application Custom ResourceAn object of a type Kubernetes did not ship with, added by a CRD. Frank's ArgoCD Applications, Rollouts and Tekton Pipelines are all CRs. in apps/root/templates/n8n-02.yaml.
  4. Commit — ArgoCD picks it up.

Upgrade n8n Version

# Update the image tag in apps/n8n/values.yaml
kubectl -n n8n-01 rollout restart deployment/n8n

# Check migrations ran
kubectl -n n8n-01 logs deploy/n8n --tail=30 | grep -i migration

Recover

n8n Pod Won’t Start

kubectl -n n8n-01 logs deploy/n8n --tail=50 | grep -i error

Common causes:

  • Database connection refused — PostgreSQL isn’t running yet. The n8n pod will crash-loop until the database is ready. Check kubectl -n n8n-01 get pods n8n-postgresql-0.
  • Migration failure — Schema migration failed on upgrade. Check the full migration log: kubectl -n n8n-01 logs deploy/n8n --tail=100 | grep -A 20 migration.
  • PersistentVolumeClaimA Kubernetes request for durable storage. The pod names a claim and the storage layer — Longhorn on Frank — binds real disk behind it, so the data outlives the pod. not mounting — Check events: kubectl -n n8n-01 describe pod -l app.kubernetes.io/name=n8n.

PostgreSQL Won’t Start

kubectl -n n8n-01 logs n8n-postgresql-0 --tail=30
kubectl -n n8n-01 describe pod n8n-postgresql-0 | grep -A 10 Events

Common causes:

  • Storage class missing or PVC stuck Pending.
  • Data corruption on unclean shutdown. Check the PG pod logs for FATAL: data directory ... has wrong ownership or FATAL: lock file "postmaster.pid" already exists.

If the lock file is stale:

kubectl -n n8n-01 exec n8n-postgresql-0 -- rm /var/lib/postgresql/data/postmaster.pid
kubectl -n n8n-01 delete pod n8n-postgresql-0

Agent Session Sidecar Issues

If the n8n instance has the agent-session sidecar and workflows that use Claude Code fail:

# Check sidecar status
kubectl -n n8n-01 get pods -l app.kubernetes.io/name=n8n -o jsonpath='{.items[0].status.containerStatuses}'

# Check if the workspace needs pre-trusting
kubectl -n n8n-01 exec deploy/n8n -c n8n -- \
  sh -c "ls ~/.claude/settings.json 2>/dev/null || echo 'no settings.json — may hit trust gate'"

The agent sidecar may need the workspace pre-trusted to skip the Claude Code trust gate. The fix was injecting a pre-seeded settings.json that whitelists the workspace (#542).

n8n Web UI Slow or Unresponsive

# Check resource usage
kubectl -n n8n-01 top pods

# Check PG connection count
kubectl -n n8n-01 exec n8n-postgresql-0 -- psql -U n8n -c "SELECT count(*) FROM pg_stat_activity;"

# Check n8n internal metrics
kubectl -n n8n-01 exec deploy/n8n -- wget -qO- localhost:5678/metrics | grep -i "request_duration\|error_total"

Missteps

What we assumedWhy it was wrongWhat it cost
n8n’s workspace trust gate respects a pre-existing settings.jsonIt does — but only if the file exists before n8n starts. If the image doesn’t ship it, every agent session hits the interactive trust prompt and hangs.Added the pre-seeded file to the image build.
An agent session sidecar can submit commands with simple stdin pipingn8n’s terminal expects bracketed-paste mode. Plain echo "command" | input arrives as literal characters.Switched to a driver that wraps the payload in bracketed-paste escape sequences.
PostgreSQL PVC data survives all pod restartsIt does for normal restarts, but a forced delete of the StatefulSet pod without terminating PG gracefully can leave a stale postmaster.pid lock file.Added recovery steps to the runbook.
More n8n instances can share the same PG clusterEach n8n instance needs its own database and user. Sharing a single PG across instances causes namespace collisions on migration.Template now includes a dedicated PG per instance.

Quick Reference

CommandWhat It Does
kubectl -n n8n-01 get podsCheck n8n + PG status
kubectl -n n8n-01 exec deploy/n8n -- wget -qO- localhost:5678/healthzHealth check
kubectl -n n8n-01 logs deploy/n8n | grep -i migrationCheck upgrade migration
kubectl -n n8n-01 rollout restart deployment/n8nRestart n8n
kubectl -n n8n-01 top podsResource usage
kubectl -n n8n-01 exec n8n-postgresql-0 -- psql -U n8n -c "SELECT 1;"DB connectivity

References