n8n is a fair-code workflow automation tool — think Zapier or Make, but self-hosted, with hundreds of integrations and the ability to drop into raw code nodes when the visual builder runs out of road. Self-hosting it is popular because you keep your data and avoid per-task pricing. Here's how to do it durably.
Don't run it on SQLite in production
n8n defaults to SQLite, which is fine for trying it on a laptop and a trap in production. Container filesystems are ephemeral; a restart wipes your workflows and execution history. The first real decision is to point n8n at an external PostgreSQL database:
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=your-managed-pg-host
DB_POSTGRESDB_PORT=5432
DB_POSTGRESDB_DATABASE=n8n
DB_POSTGRESDB_USER=...
DB_POSTGRESDB_PASSWORD=...On PandaStack you can attach a managed PostgreSQL and map the injected DATABASE_URL components into these variables, so your workflows and credentials live on a backed-up database rather than inside the container.
The encryption key is sacred
n8n encrypts stored credentials (API keys, OAuth tokens) with N8N_ENCRYPTION_KEY. If you don't set it, n8n generates one and writes it to disk inside the container — which means a fresh container generates a new key and all your saved credentials become undecryptable. Set it explicitly as a secret and never change it:
N8N_ENCRYPTION_KEY=a-long-stable-random-secretThis single variable causes more self-hosting heartbreak than anything else. Treat it like a database password.
Webhook and host configuration
Many n8n workflows trigger on inbound webhooks. n8n needs to know its own public URL to generate correct webhook addresses:
N8N_HOST=n8n.yourdomain.com
N8N_PROTOCOL=https
WEBHOOK_URL=https://n8n.yourdomain.com/
N8N_PORT=5678If WEBHOOK_URL is wrong, the URLs n8n hands to external services point nowhere and triggers silently never fire. Set it to your custom domain. PandaStack issues SSL automatically for custom domains, so https works out of the box.
Single instance vs queue mode
n8n runs in two execution topologies:
| Mode | Components | When |
|---|---|---|
| Regular | One process does everything | Low/medium volume |
| Queue | Main + worker(s) + Redis | High volume, long-running, parallel |
In queue mode, the main instance accepts triggers and pushes executions onto a Redis queue; separate worker processes consume them. This isolates a slow workflow from blocking everything else and lets you scale workers independently.
EXECUTIONS_MODE=queue
QUEUE_BULL_REDIS_HOST=your-managed-redis-hostStart in regular mode. Move to queue mode (main service + a separate worker service sharing the same Postgres and a managed Redis) when execution latency or concurrency becomes a problem.
Pruning execution data
n8n stores every execution. Without pruning, your Postgres grows unbounded. Enable data pruning:
EXECUTIONS_DATA_PRUNE=true
EXECUTIONS_DATA_MAX_AGE=336 # hours (14 days)Go-live checklist
- External Postgres configured (not SQLite)
N8N_ENCRYPTION_KEYset as a stable secretWEBHOOK_URL/N8N_HOSTset to your custom domain- HTTPS enforced; SSL automatic
- Execution pruning enabled
- Queue mode + Redis once volume grows
References
- [n8n self-hosting docs](https://docs.n8n.io/hosting/)
- [n8n environment variables](https://docs.n8n.io/hosting/configuration/environment-variables/)
- [n8n queue mode](https://docs.n8n.io/hosting/scaling/queue-mode/)
- [n8n database configuration](https://docs.n8n.io/hosting/configuration/supported-databases-settings/)
n8n deploys cleanly on PandaStack as a container service with a managed PostgreSQL attached — and managed Redis is right there when you graduate to queue mode. The free tier covers a personal automation setup; start at [dashboard.pandastack.io](https://dashboard.pandastack.io).