Untouched `uv init` placeholder (print("Hello from n8n!")). Not
imported, not wired as an entry point, and documented in this repo's
own README/CLAUDE.md as leftover scaffolding not on the build path.
|
||
|---|---|---|
| .gitignore | ||
| .python-version | ||
| compose.yml | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
n8n
n8n 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://<host>:5678/ (or via the Traefik path-prefix
route — see "Routing" below). Webhooks fire to WEBHOOK_URL per
.env.
Run it
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://<host>:5678/. Tailscale-only reachable because the host itself is Tailscale-fronted. - Through Traefik (committed state): path-prefix
/n8nonus-test-authy.taila7f44e.ts.net, with a separate/n8n/apiroute 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 externaltraefiknetwork and the labels block from the committedcompose.ymlshould be restored.
Gotchas
:latestimage tag. No pin. n8n minor releases occasionally require workflow re-saves or credential re-entry; check release notes before pulling.WEBHOOK_URLmust match ingress. If you put n8n behind Traefik at/n8n/,WEBHOOK_URLmust behttps://<host>/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 fromN8N_BASIC_AUTH_PASSWORD. - Don't commit
.env. Contains real PBS integration credentials (Portainer API key, Google Chat webhook, healer webhook). The.gitignorealready 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.