# n8n [n8n](https://n8n.io/) workflow automation deployment for the homelab. Single-container stack with external persistent volume for workflows, credentials, and execution history. ## Service | Service | Image | Container | Host port | |---------|------------------------------------|-----------|-----------| | `n8n` | `docker.n8n.io/n8nio/n8n:latest` | `n8n` | 5678 | n8n is reached at `http://:5678/` (or via the Traefik path-prefix route — see "Routing" below). Webhooks fire to `WEBHOOK_URL` per `.env`. ## Run it ```bash docker compose up -d docker compose logs -f n8n ``` ## Configuration — `.env`-driven All knobs are in `.env` (gitignored). Required keys (names only — values live in the file on the host): | Key | Purpose | |--------------------------------|---------------------------------------------------------| | `N8N_HOST` | Public hostname (Tailscale magic-DNS in this homelab) | | `N8N_PORT` | Listen port (5678) | | `N8N_PROTOCOL` | `https` when fronted by Traefik / Tailscale | | `N8N_PATH` | URL path prefix (`/n8n/` when using the Traefik route) | | `GENERIC_TIMEZONE` | e.g. `America/New_York` | | `N8N_SECURE_COOKIE` | `true` for HTTPS-fronted deployments | | `N8N_BASIC_AUTH_ACTIVE` | `true` / `false` | | `N8N_BASIC_AUTH_USER` | Basic-auth username | | `N8N_BASIC_AUTH_PASSWORD` | Basic-auth password (rotate via .env, not via UI) | | `WEBHOOK_URL` | Outward-facing webhook base (must match real ingress) | | `PBS_*` | Workflow-side integration creds (Google Chat, Portainer, monitor base URLs, healer webhook) | Rotate any of these by editing `.env` and restarting: `docker compose up -d` (compose will recreate the container). **Never commit `.env`** — it's listed in `.gitignore` for this reason. If you need a checked-in template, create `.env.example` with the keys but no values. ## Persistence — external volume ``` volumes: n8n_data: name: n8n_server_n8n_data external: true ``` The volume is **external** with an explicit `name:` (`n8n_server_n8n_data`) that pins it to the data originally created by a prior compose stack called `n8n_server`. Don't remove the `name:` override — Docker would create a new empty volume and n8n would boot with no workflows, credentials, or history. Backup: `docker run --rm -v n8n_server_n8n_data:/data -v $PWD:/backup alpine tar czf /backup/n8n-backup-$(date +%F).tgz -C /data .` ## Routing (homelab-specific) In this homelab, n8n is reachable two ways depending on which compose state you're running: - **Direct host port** (current working-copy default): browse to `http://:5678/`. Tailscale-only reachable because the host itself is Tailscale-fronted. - **Through Traefik** (committed state): path-prefix `/n8n` on `us-test-authy.taila7f44e.ts.net`, with a separate `/n8n/api` route that bypasses the Authelia middleware so API-key clients don't double-auth. If you re-enable this, the container needs to be on the external `traefik` network and the labels block from the committed `compose.yml` should be restored. ## Gotchas - **`:latest` image tag.** No pin. n8n minor releases occasionally require workflow re-saves or credential re-entry; check release notes before pulling. - **`WEBHOOK_URL` must match ingress.** If you put n8n behind Traefik at `/n8n/`, `WEBHOOK_URL` must be `https:///n8n/` — n8n bakes that URL into the webhooks it shows in the UI, so a mismatch silently breaks third-party integrations even though the workflow page looks right. - **The basic-auth password lives in `.env`.** Don't change it through the UI — there's nothing to change in the UI; it's enforced by the HTTP layer from `N8N_BASIC_AUTH_PASSWORD`. - **Don't commit `.env`.** Contains real PBS integration credentials (Portainer API key, Google Chat webhook, healer webhook). The `.gitignore` already excludes it; keep it that way. - The repo also contains some leftover uv-init artifacts (`main.py`, `pyproject.toml`, `uv.lock`, `.python-version`). These are not part of the deployment.