Choose Path A, Path B, or migrate
| Path | Use when | Trap |
|---|---|---|
| Path A — marketplace n8n image | Fast discovery on Kamatera app landing | Opaque internals; still your security duty |
| Path B — blank Ubuntu + Compose | Operator-owned pins, DB, proxy, key | You own day-two ops |
| Existing install | Export/migrate; do not dual-stack | Second stack on same DB/domain |
Conditional size: consider Type B 2 GB for deliberate proof; consider 4 GB+ when OS+DB+proxy measurements need it—no guaranteed capacity. Hourly for short tests; monthly if always-on; terminate leftovers.
Path A — marketplace discovery and harden
- Inventory containers/services/ports/admin URL/version as installed.
- Restrict admin/UI to HTTPS + auth; close accidental public 5678/DB ports.
- Locate data directories/volumes; prove workflow JSON export + DB dump before go-live.
- If internals are opaque, prefer Path B for production ownership—or accept managed/support scope explicitly.
Do not run Path B docker compose commands against an unknown Path A layout without discovery.
Path B — discovery, project layout, and stable key
Scope-safe discovery (docker compose ls, listeners, mounts)—no secret dumps. Create /opt/aixvps/n8n (or your chosen fixed path), secrets, and backup directories. Verify image UID/GID from the selected upstream before chown.
Generate a unique Postgres password and one stable n8n encryption key. Store both in a password manager plus an off-host copy. .env mode 600; ship only a blank .env.example. Key loss breaks credential decryption—never regenerate casually on restart/update/restore.
Path B — Compose: private n8n, private Postgres, one proxy
At deploy time choose intentionally validated n8nio/n8n, supported PostgreSQL, and one reverse proxy (Caddy or Nginx)—pin versions from current official docs, not from memory. Private network; named volumes; no public 5432; publish n8n only through the proxy layout (container proxy or 127.0.0.1:5678).
- Healthchecks,
depends_on,restart: unless-stopped, resource bounds as appropriate. - DNS ready; public 80/443 only; inspect host firewall + Docker effective listeners.
- Canonical HTTPS URL/webhook settings per current n8n docs; timezone; concurrency/pruning as operator-labeled choices.
# Structural check only — never print expanded secrets.
docker compose config --quiet
ss -tulpn | egrep ':80|:443|:5678|:5432' || true
Validate, day-two, upgrade, and triage
Bring the stack up; create the owner; run a harmless no-credential workflow; restart and confirm workflow + credential decryption persistence.
Acceptance receipt: pins/digests, canonical URL, exposure map, key location, DB health, RAM/disk before-after, first restore rehearsal date.
Day two: health/logs/disk/execution growth/TLS expiry/backup freshness. Prune by RPO—not arbitrary “delete daily.”
Upgrades: control writers → current export → verify restore of the final cutoff before destructive rollback; change pins intentionally; never use marketplace rebuild as a normal “update.”
| Symptom | Check | Do not |
|---|---|---|
| DB unhealthy | Config/mount/resources | Deleting volumes to fix |
| Credentials fail after restart | Encryption key mismatch | Regenerating the key |
| 502 / TLS errors | Proxy/DNS/backend | Opening 5678 publicly |
| OOM / disk pressure | Concurrency/history/data growth | Ignoring prune + exports |
Backup handoff — app-consistent, not snapshot-only
On a defined cutoff: workflow JSON export + Postgres dump + encryption key backup. Optional Extended daily backup / snapshots are extra layers—not substitutes. Full restore/terminate gate lives on the billing guide.
Final checklist before valuable automations: HTTPS, key protected, concurrency bounded, independent restore proven, terminate calendar if this is an hourly trial.