How to Deploy and Host Your Own n8n Server: Step-by-Step Guide with Docker, PostgreSQL & Caddy
👤 Subhodip Ghosh •
📅 August 13, 2026 •
👁️ 55 views
• 🔄 Updated August 18, 2026
Relying on SaaS automation tools like Zapier or Make gets expensive fast as your task volumes grow. A single runaway loop can spike your monthly bill, and sending customer data through third-party servers presents data privacy risks.
[n8n](https://n8n.io) (pronounced *n-eight-n*) is a fair-code, self-hosted workflow automation platform. It provides a visual canvas with hundreds of pre-built integrations, custom code execution, and AI agent capabilities—without charging you per-execution SaaS fees.
By hosting n8n on your own server, your data stays strictly under your control, and you eliminate per-task SaaS pricing models. While your actual execution capacity will depend on your server's CPU, RAM, and database performance, self-hosting gives you complete freedom to scale as your automation needs grow. This guide walks you through building a solid, production-oriented n8n server using **Docker Compose**, a **PostgreSQL** database, and **Caddy** for automatic SSL certificate management.
---
## Why Self-Host n8n over Zapier or Make?
Before jumping into the command line, here is how a self-hosted n8n server compares to traditional SaaS platforms:
- **Fair-Code Source Availability:** You can inspect the source code, build custom nodes, host it on your own server, and tailor it to your workflow needs without per-task charges.
- **Node-Based Workflow Editor:** Drag-and-drop triggers (webhooks, cron schedules, app listeners) and action nodes to transform data and execute API calls visually.
- **JavaScript & Python Code Execution:** Write inline JavaScript or Python inside n8n Code nodes to handle complex data parsing, filtering, and custom logic.
- **Complete Data Sovereignty:** API keys, database credentials, and sensitive customer payloads remain safely inside your isolated private cloud.
---
## How n8n Works Under the Hood
n8n uses an event-driven runtime to process automation tasks:
1. **Triggers:** Every workflow starts with a trigger node. This can listen for incoming HTTP requests (Webhooks), run at set intervals (Cron schedules), or poll third-party APIs (like Gmail, GitHub, or Stripe).
2. **Directed Graph of Nodes:** The visual canvas links nodes in a directed graph. Each node receives incoming data, performs an operation (e.g., transforming payload, calling an external API, or querying a database), and outputs the results.
3. **JSON Array Pipeline:** Nodes exchange data as arrays of JSON objects. Every node processes the incoming JSON payload and passes the modified payload downstream.
4. **Sequential Execution Engine:** The engine executes nodes step-by-step, handles conditional IF/ELSE branching, logs execution history, and allows you to debug or replay failed runs.
### Production Architecture & Network Flow
Here is how traffic, SSL termination, network isolation, and database persistence flow through the Docker stack:
```mermaid
graph TD
Client([External Webhooks & Clients]) -->|HTTPS:443| Caddy[Caddy Reverse Proxy]
Browser([Admin UI Browser]) -->|HTTPS:443| Caddy
Caddy -->|HTTP over Docker Network:5678| n8n[n8n Container]
n8n -->|Port 5432 Internal| Postgres[(PostgreSQL 16 Database)]
n8n <-->|Volume Mount| DataDir[(~/n8n-docker/data)]
Postgres <-->|Volume Mount| PGDir[(~/n8n-docker/postgres_data)]
```
**Network Flow Explanation:**
- **External Traffic:** Caddy listens on public ports `80` (HTTP redirect) and `443` (HTTPS). Caddy automatically provisions and renews TLS/SSL certificates from Let's Encrypt using your configured `USER_EMAIL`.
- **Internal Proxying:** Caddy terminates TLS at the edge and proxies requests internally over Docker's private bridge network to `n8n:5678` using standard HTTP. n8n itself does not run HTTPS internally.
- **Loopback Binding:** In `docker-compose.yml`, n8n binds to `127.0.0.1:5678:5678`. This keeps port 5678 accessible locally on the host for testing while preventing direct public exposure over the open internet.
- **Database Isolation:** PostgreSQL runs on port 5432 inside the private Docker network and is not exposed to host ports, protecting your data from external database scans.
---
## Key Features in Modern n8n
n8n has expanded far beyond simple webhook triggers:
- **AI Agent Nodes:** Build autonomous AI agents using LLMs (OpenAI, Anthropic, or local models via Ollama) and grant them access to custom n8n tool nodes.
- **Human-in-the-Loop:** Pause workflows mid-execution to wait for manual approval or input via Slack, email, or custom web forms before continuing.
- **Canvas Organization:** Group nodes with sticky notes and visual color blocks to keep large enterprise workflows organized and clean.
### Building AI Agents in n8n (Advanced AI Nodes)
Modern n8n includes native, LangChain-powered AI nodes that allow you to construct autonomous AI agents directly on your visual canvas:
1. **AI Agent Node (The Core Engine):** Connects a language model to memory buffer sub-nodes and external tool nodes. The agent autonomously decides which tool to call based on user prompts.
2. **Language Model Sub-Nodes:** Connect cloud LLM providers (e.g., `OpenAI Chat Model`, `Anthropic Chat Model`) or self-hosted local models via `Ollama` for 100% private data inference.
3. **Tool Sub-Nodes:** Equip your AI agent with capabilities to execute real actions—such as querying your PostgreSQL database, searching vector stores (Qdrant, Pinecone), sending Slack messages, or calling custom HTTP APIs.
4. **Memory Nodes:** Attach `Window Buffer Memory` or `Postgres Chat Memory` so the AI agent retains context across multi-turn user conversations.
---
## Hardware & System Prerequisites
Before starting the setup, ensure you have the following prerequisites ready:
1. **Cloud Server (VPS):** A Linux server running Ubuntu 22.04/24.04 LTS or Debian 12 with SSH access (`root` or `sudo` privileges).
- *Minimum:* 1 vCPU, 2 GB RAM (suitable for personal automations).
- *Recommended:* 2 vCPU, 4 GB RAM, and SSD storage (essential for AI nodes, Queue mode, or production workloads).
- *Note:* Actual hardware requirements depend on your workflow complexity, dataset sizes, and execution concurrency.
2. **Domain Name & DNS Control:** A domain or subdomain (e.g., `automation.yourdomain.com`) with access to your DNS management console.
3. **Public IP Address:** A static public IPv4 address assigned to your server.
4. **Open Network Ports:** Ports `80` (HTTP) and `443` (HTTPS) open for web traffic, and port `22` open for SSH access.
5. **Docker & Docker Compose:** Docker containerizes application environments, while **Docker Compose** orchestrates multi-container applications (n8n, PostgreSQL, and Caddy together) using a single `docker-compose.yml` file.
---
## Step-by-Step n8n Docker Setup Guide
This guide configures n8n alongside PostgreSQL (for database stability over SQLite) and Caddy (for zero-touch Let's Encrypt HTTPS certificates).
### Step 1: Configure DNS Settings
Log into your domain registrar or DNS host (like Cloudflare, Route53, or Namecheap) and create an **A Record**:
- **Name / Host:** `automation` (or your preferred subdomain)
- **Target Value:** Your VPS's public IP address
*(Note: If using Cloudflare, you can temporarily disable the proxy feature—gray cloud icon—during initial setup so Caddy can pass Let's Encrypt HTTP-01 domain validation).*
### Step 2: Connect to VPS, Enable Firewall & Install Docker
SSH into your server:
```bash
ssh root@YOUR_SERVER_IP
```
Set up basic firewall protection with UFW to allow SSH, HTTP, and HTTPS traffic:
```bash
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw enable
```
Install Docker and the Docker Compose plugin:
```bash
sudo apt update && sudo apt upgrade -y
sudo apt install docker.io docker-compose-plugin -y
```
### Step 3: Create Directory Structure & Set Container Permissions
Create directories for configuration files and database volumes:
```bash
mkdir -p ~/n8n-docker/data
mkdir -p ~/n8n-docker/postgres_data
cd ~/n8n-docker
```
The official n8n container runs internally as a non-root user (`node` with UID/GID 1000). Set permissions on the n8n data folder so the container can write user settings and binary attachments:
```bash
sudo chown -R 1000:1000 ~/n8n-docker/data
```
### Step 4: Create the Environment File (`.env`)
Create `.env` to centralize domain names, secret keys, and database credentials:
```bash
nano .env
```
Paste the following variables (replace placeholder values with your domain, admin email, and strong passwords):
```ini
# Domain & Admin Settings
DOMAIN_NAME=automation.yourdomain.com
SUBDOMAIN=automation
[email protected]
# System Secrets
# Generate a secure key with: openssl rand -hex 24
N8N_ENCRYPTION_KEY=your_generated_encryption_key_here
N8N_PORT=5678
# Database Credentials
POSTGRES_USER=n8n_db_user
POSTGRES_PASSWORD=your_secure_db_password_here
POSTGRES_DB=n8n_database
# Production Environment & Reverse Proxy Settings
GENERIC_TIMEZONE=UTC
N8N_PROTOCOL=https
NODE_ENV=production
WEBHOOK_URL=https://automation.yourdomain.com
N8N_SECURE_COOKIE=true
# SMTP Configuration (Configure to send password resets & invite emails)
N8N_EMAIL_MODE=smtp
N8N_SMTP_HOST=smtp.mailgun.org
N8N_SMTP_PORT=587
[email protected]
N8N_SMTP_PASS=your_smtp_password_here
N8N_SMTP_SENDER="n8n Automation <[email protected]>"
N8N_SMTP_SSL=false
```
Save with `Ctrl + O`, press `Enter`, and exit with `Ctrl + X`.
> [!IMPORTANT]
> **Encryption Key Safety:** Keep `N8N_ENCRYPTION_KEY` backed up in a secure password manager. Do not randomly regenerate it on an existing setup—changing this key will render previously saved credentials (API keys, OAuth tokens) permanently unreadable.
### Step 5: Write `docker-compose.yml`
Create the compose configuration file:
```bash
nano docker-compose.yml
```
Paste this production configuration:
```yaml
services:
postgres:
image: postgres:16-alpine
restart: always
environment:
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- POSTGRES_DB=${POSTGRES_DB}
volumes:
- ./postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -h localhost -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 5s
timeout: 5s
retries: 10
caddy:
image: caddy:latest
restart: always
ports:
- "80:80"
- "443:443"
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile
- caddy_data:/data
- caddy_config:/config
environment:
- DOMAIN_NAME=${DOMAIN_NAME}
- USER_EMAIL=${USER_EMAIL}
n8n:
image: docker.n8n.io/n8nio/n8n:latest
restart: always
ports:
- "127.0.0.1:5678:5678"
environment:
- DB_TYPE=postgresdb
- DB_POSTGRESDB_HOST=postgres
- DB_POSTGRESDB_PORT=5432
- DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
- DB_POSTGRESDB_USER=${POSTGRES_USER}
- DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
- N8N_HOST=${DOMAIN_NAME}
- N8N_PORT=${N8N_PORT}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- NODE_ENV=${NODE_ENV}
- WEBHOOK_URL=${WEBHOOK_URL}
- N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- N8N_SECURE_COOKIE=${N8N_SECURE_COOKIE}
- N8N_EMAIL_MODE=${N8N_EMAIL_MODE}
- N8N_SMTP_HOST=${N8N_SMTP_HOST}
- N8N_SMTP_PORT=${N8N_SMTP_PORT}
- N8N_SMTP_USER=${N8N_SMTP_USER}
- N8N_SMTP_PASS=${N8N_SMTP_PASS}
- N8N_SMTP_SENDER=${N8N_SMTP_SENDER}
- N8N_SMTP_SSL=${N8N_SMTP_SSL}
depends_on:
postgres:
condition: service_healthy
volumes:
- ./data:/home/node/.n8n
volumes:
caddy_data:
caddy_config:
```
Save and exit.
> [!TIP]
> **Image Tag Pinning Note:** While using `:latest` tags is acceptable for testing and tutorials, production environments should consider pinning n8n and Caddy to specific tested version tags (e.g., `docker.n8n.io/n8nio/n8n:1.75.0`). Pinning image tags prevents automatic updates from introducing breaking changes during container restarts.
### Step 6: Create the `Caddyfile`
Caddy handles reverse proxying and automated SSL certificate issuance. Create the `Caddyfile`:
```bash
nano Caddyfile
```
Add this reverse proxy block:
```caddyfile
{
email {$USER_EMAIL}
}
{$DOMAIN_NAME} {
reverse_proxy n8n:5678
}
```
Save and exit.
*Explanation:* Caddy uses `{$USER_EMAIL}` and `{$DOMAIN_NAME}` from your `.env` file. It automatically requests Let's Encrypt SSL certificates for your domain and proxies incoming HTTPS requests to `n8n:5678` over the shared Docker bridge network.
### Step 7: Launch Containers
Start the services in detached background mode:
```bash
docker compose up -d
```
Docker will download the images, initialize PostgreSQL, provision Let's Encrypt SSL certificates, and start the n8n application container.
### Step 8: Set Up Owner Credentials
Navigate to `https://automation.yourdomain.com` in your browser. Create your administrator account credentials to access the canvas.
> [!WARNING]
> **Complete account setup immediately.** The first person to visit a fresh n8n web URL becomes the instance owner. Register your admin account immediately after launching containers to prevent unauthorized registration.
### Step 9: How to Create and Configure a Webhook in n8n (Verification)
To verify that your SSL proxy, container networking, and database logging are working properly:
1. **Create a New Workflow:** Click **+ New Workflow** inside the n8n dashboard canvas.
2. **Add a Webhook Trigger Node:** Click **+ Add First Step**, select **Webhook**, and configure its settings:
- **HTTP Method:** Select `POST` (or `GET`).
- **Path:** Enter a custom endpoint slug (e.g., `my-test-webhook`).
- **Authentication:** Select `None` for testing.
- **Respond Mode:** Select **When Last Node Finishes** or **Immediately**.
3. **Test vs. Production URLs:**
- **Test URL (`/webhook-test/...`):** Used during workflow development. Requires clicking *Listen for Test Event* in the editor.
- **Production URL (`/webhook/...`):** Active 24/7 once you toggle the workflow switch from *Inactive* to **Active**.
4. **Trigger & Test the Webhook:** Toggle your workflow to **Active**, copy the **Production URL**, and send a test POST request from your terminal:
```bash
curl -X POST https://automation.yourdomain.com/webhook/my-test-webhook \
-H "Content-Type: application/json" \
-d '{"status": "testing n8n webhook", "success": true}'
```
If you receive an HTTP `200 OK` response with `{"message": "Workflow executed successfully"}`, your reverse proxy, SSL certificates, container networking, and PostgreSQL logging matrix are fully operational!
---
## Production Best Practices & Maintenance
### Security Best Practices Checklist
To keep your self-hosted n8n server secure against unauthorized access:
- **Strong Credentials:** Use strong, unique passwords for `POSTGRES_PASSWORD` and your n8n owner account.
- **Loopback Port Binding:** In `docker-compose.yml`, bind port `5678` strictly to loopback (`127.0.0.1:5678:5678`). This ensures n8n is accessible only through Caddy's HTTPS proxy.
- **Enforce Non-Root Execution:** Run the container under non-privileged UID/GID permissions (`chown -R 1000:1000 ~/n8n-docker/data`) to prevent container breakout risks.
- **Enable HTTPS & Secure Cookies:** Set `N8N_PROTOCOL=https` and `N8N_SECURE_COOKIE=true` in `.env` to enforce encrypted session cookies.
- **Configure Firewall (UFW):** Allow incoming traffic only on ports `22` (SSH), `80` (HTTP), and `443` (HTTPS).
- **Run Security Audits:** If supported by your n8n version, run n8n's built-in security audit tool:
```bash
docker compose exec n8n n8n audit
```
### How to Configure SMTP Email for Notifications
Configure SMTP if you want n8n to send password-reset emails, user invitations, and workflow failure notifications.
1. **Obtain Credentials:** Get your host (`smtp.mailgun.org`), port (`587`), username, password, and sender address from your email provider.
2. **Update `.env`:** Set `N8N_EMAIL_MODE=smtp` and populate your `N8N_SMTP_*` credentials.
3. **Map Variables:** Ensure `docker-compose.yml` maps `N8N_SMTP_*` variables into the `n8n` container service.
4. **Apply & Test:** Run `docker compose up -d` and test email delivery via **Settings > Email** in the n8n dashboard.
### Automated Database & File Backup System
To protect your self-hosted instance from hardware failure or corrupted deployments, implement automated daily backups for both PostgreSQL records and user binary volumes.
#### Manual PostgreSQL Backup Command
To back up your database, execute `pg_dump` via Docker Compose. Ensure `n8n_db_user` matches `POSTGRES_USER` and `n8n_database` matches `POSTGRES_DB` in your `.env` file:
```bash
# 1. Export PostgreSQL database (-T disables pseudo-TTY allocation for clean redirect)
docker compose exec -T postgres pg_dump -U n8n_db_user n8n_database > ~/n8n-docker/backup_$(date +%Y%m%d).sql
# 2. Archive user binary data directory
tar -czf ~/n8n-docker/n8n_data_$(date +%Y%m%d).tar.gz -C ~/n8n-docker data
```
#### Manual PostgreSQL Restore Procedure
To restore a PostgreSQL database backup file into your running container:
```bash
cat ~/n8n-docker/backup_20260813.sql | docker compose exec -T postgres psql -U n8n_db_user -d n8n_database
```
*Restore Notes:*
- Ensure `n8n_db_user` and `n8n_database` match your `.env` configuration.
- Restoring a backup overwrites existing database data. Test restores periodically in a separate development/test environment before executing on production.
#### Automated Daily Cron Backup Script
Create an automated backup script `~/n8n-docker/backup.sh`:
```bash
nano ~/n8n-docker/backup.sh
```
Paste this backup and retention script:
```bash
#!/bin/bash
BACKUP_DIR="$HOME/n8n-docker/backups"
DATE=$(date +%Y%m%d_%H%M%S)
mkdir -p "$BACKUP_DIR"
# 1. Dump PostgreSQL database
docker compose -f "$HOME/n8n-docker/docker-compose.yml" exec -T postgres pg_dump -U n8n_db_user n8n_database > "$BACKUP_DIR/db_$DATE.sql"
# 2. Archive binary data directory
tar -czf "$BACKUP_DIR/data_$DATE.tar.gz" -C "$HOME/n8n-docker" data
# 3. Prune local backups older than 7 days
find "$BACKUP_DIR" -type f -mtime +7 -delete
```
Make the script executable and schedule it to run daily at 2:00 AM via crontab:
```bash
chmod +x ~/n8n-docker/backup.sh
(crontab -l 2>/dev/null; echo "0 2 * * * /bin/bash $HOME/n8n-docker/backup.sh") | crontab -
```
> [!IMPORTANT]
> **Disaster Recovery & Off-Site Retention:** Storing backups on the same VPS server only protects against software corruption. For true disaster recovery (server crash, provider outage, disk failure), automatically sync backup archives to an off-site location (e.g. AWS S3, Backblaze B2, or a remote rsync target).
### Safe Upgrade & Maintenance Workflow
To update n8n safely without risking downtime or data loss:
1. **Create Backups:** Run `pg_dump` and archive your `data` folder.
2. **Review Changelog:** Check the official n8n release notes for breaking changes.
3. **Pull New Images:** Fetch updated Docker images:
```bash
docker compose pull
```
4. **Recreate Containers:** Restart the stack to apply new images:
```bash
docker compose up -d
```
*(Note: `docker compose pull` downloads updated image layers, while `docker compose up -d` recreates the container with the new image. They are two separate operations).*
5. **Verify Health:** Inspect logs using `docker compose logs -f n8n` and test your workflows in the UI.
> [!CAUTION]
> **Avoid Destructive Commands:** Never run `docker compose down -v` on a production server. The `-v` flag deletes named Docker volumes, destroying your PostgreSQL database and persistent n8n data.
### Importing External NPM Packages in Code Nodes
By default, n8n sandboxes JavaScript Code nodes. Setting `NODE_FUNCTION_ALLOW_EXTERNAL=axios,lodash` in `.env` allows those packages to be imported in Code nodes **only if they are installed in the container runtime**.
Setting the environment variable does not install packages automatically. To install custom npm packages, build a custom Dockerfile:
```dockerfile
FROM docker.n8n.io/n8nio/n8n:latest
USER root
RUN cd /usr/local/lib/node_modules/n8n && npm install axios lodash
USER node
```
*Warning:* Do not manually `npm install` inside a running container via `docker exec`. Those changes will disappear as soon as the container is recreated during updates.
---
## Troubleshooting Common Setup Issues
### Useful Diagnostic Commands
- **Check Container Status:**
```bash
docker compose ps
```
- **Inspect Container Logs:**
```bash
docker compose logs -f n8n
docker compose logs -f caddy
docker compose logs -f postgres
```
- **Verify DNS Resolution:**
```bash
dig automation.yourdomain.com
# or: nslookup automation.yourdomain.com
```
### 1. 502 Bad Gateway Error from Caddy
* **Symptom:** Visiting your domain returns a `502 Bad Gateway` error page.
* **Potential Causes & Fixes:**
- *n8n Still Booting:* n8n takes 15–30 seconds to run database migrations on startup. Check logs with `docker compose logs -f n8n`.
- *Database Connection Failure:* If n8n restarts continuously, check that `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` match between `.env` and `docker-compose.yml`.
- *Docker Network Mismatch:* Ensure Caddy and n8n are running in the same Docker Compose stack.
- *Wrong Reverse Proxy Target:* Ensure `Caddyfile` proxies to `n8n:5678`.
### 2. Out of Memory (OOM) Container Crashes
* **Symptom:** n8n stops unexpectedly when processing large datasets or AI workflows on a 1 GB VPS.
* **Fix:** Enable a 2 GB Linux swap file to absorb RAM spikes:
```bash
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
```
### 3. Webhook URL Mismatches & Mixed Content Warnings
* **Symptom:** Webhooks generated inside n8n default to `http://` instead of `https://`.
* **Fix:** Confirm `WEBHOOK_URL=https://automation.yourdomain.com` is set in `.env` without a trailing slash, then restart containers:
```bash
docker compose up -d
```
---
## Don't Want to Manage Infrastructure? (Managed Option)
Self-hosting n8n requires managing server security, UFW firewalls, Docker Compose stacks, Let's Encrypt SSL renewals, PostgreSQL backups, and resource monitoring.
If you prefer a managed option without maintaining underlying Linux servers, you can host n8n using [SiliconPin n8n Cloud Hosting](https://siliconpin.com/services/n8n-cloud-hosting). Powered by rootless [SiliconPin Pods](https://siliconpin.com/siliconpin-pod), it provides dedicated multi-threaded execution, managed PostgreSQL sidecars, automatic SSL, and instant snapshot backups (`sp backup`).
### Quick Deployment via `sp` CLI:
```bash
# 1. Install sp CLI & authenticate
curl -fsSL https://siliconpin.com/downloads/sp/install.sh | bash
sp login --token=$SP_TOKEN
# 2. Deploy n8n pod & attach PostgreSQL sidecar
sp deploy --name=<application-name> --port=5678
sp deployments attach db postgresql <deployment-id>
# 3. Configure environment variables
sp env set N8N_ENCRYPTION_KEY=your_generated_encryption_key_here
sp env set WEBHOOK_URL=https://n8n-app.yourname.sp.net
sp env set GENERIC_TIMEZONE=UTC
# 4. Optional: Snapshot backups & local HTTPS tunneling
sp backup <deployment-id>
sp tunnel 5678
```
---
## Frequently Asked Questions (FAQ)
### Q: Can I use n8n for free?
**A:** Yes! Under n8n's Sustainable Use License (Fair-Code model), self-hosting n8n is **100% free** for personal use, internal business automation, and custom workflow testing. You only pay for your own VPS hosting infrastructure. (A paid commercial license is only required if you resell n8n as a paid white-label managed service to external clients).
### Q: What is Docker Compose and why is it used for n8n?
**A:** **Docker Compose** is an orchestration tool for defining and running multi-container Docker applications. Instead of running complex separate commands for n8n, PostgreSQL, and Caddy, Docker Compose lets you define all services, environment variables, networks, and persistent volumes in a single `docker-compose.yml` file and launch everything using `docker compose up -d`.
### Q: What are AI agent nodes in n8n and how do they work?
**A:** AI agent nodes in n8n leverage the LangChain framework to build autonomous AI agents. An **AI Agent Node** serves as the reasoning engine connected to sub-nodes: **Language Models** (OpenAI, Anthropic, local Ollama), **Memory** (Window Buffer or Postgres Chat Memory), and **Tools** (PostgreSQL queries, Slack, HTTP API calls, Vector store RAG retrieval).
### Q: What are the limitations of using SQLite with n8n in production?
**A:** SQLite is convenient for local testing, but PostgreSQL is generally the better choice for production workloads due to:
1. **Single-Writer File Locking:** SQLite locks the entire database file during writes, causing `SQLITE_BUSY: database is locked` errors under concurrent webhooks.
2. **No Multi-Worker Queue Mode Scaling:** SQLite files cannot be shared across multiple container workers.
3. **Database Corruption Risks:** Host crashes or disk space exhaustion during a write operation can corrupt `.n8n/database.sqlite`.
4. **Performance Degradation:** As execution logs grow past tens of thousands of records, SQLite write speeds drop significantly compared to PostgreSQL WAL.
### Q: How do I scale n8n for high execution volumes?
**A:** For high execution volume, you can scale n8n by switching to Queue Mode:
```plaintext
Main n8n Engine --> Redis (Message Broker) --> Worker Containers --> PostgreSQL
```
Redis acts as a queue broker, distributing workflow executions across multiple worker containers. A simple single-instance Docker Compose setup is sufficient for most small to medium automation workloads.
### Q: How do I set up automated backups for n8n?
**A:** Create an automated bash script that executes `docker compose exec -T postgres pg_dump -U n8n_db_user n8n_database > db_backup.sql` and archives the `~/n8n-docker/data` directory into a `.tar.gz` file. Schedule the script to run daily at off-peak hours using Linux `crontab` (e.g., `0 2 * * * /bin/bash /path/to/backup.sh`), with automated pruning to remove backups older than 7 days.
### Q: How do I configure SMTP email in n8n for password resets?
**A:** Configure SMTP if you want n8n to send password-reset emails, user invitations, and workflow failure notifications. Set `N8N_EMAIL_MODE=smtp`, `N8N_SMTP_HOST`, `N8N_SMTP_PORT=587`, `N8N_SMTP_USER`, `N8N_SMTP_PASS`, and `N8N_SMTP_SENDER` in your `.env` file, map them into `docker-compose.yml`, and restart your containers (`docker compose up -d`).
### Q: How do I create and test a webhook in n8n?
**A:** Open the n8n workflow canvas, add a **Webhook** node, and set your desired HTTP Method (`GET`, `POST`, etc.) and path slug. During workflow building, use the **Test URL** (`/webhook-test/...`) alongside the "Listen for Test Event" button to inspect incoming JSON data. Once verified, toggle the workflow switch to **Active** and switch your external service (Stripe, GitHub, Typeform, etc.) to use the **Production URL** (`/webhook/...`).
### Q: What are the security best practices for n8n?
**A:** Key security best practices include:
- Binding n8n to local loopback `127.0.0.1:5678` so traffic is forced through Caddy's encrypted HTTPS proxy.
- Setting `N8N_SECURE_COOKIE=true` and `N8N_PROTOCOL=https` in `.env`.
- Configuring UFW firewall to allow only ports 22, 80, and 443.
- Running containers under non-privileged UID/GID permissions (`1000:1000`).
- Backing up `N8N_ENCRYPTION_KEY` in a secure password manager.
- Completing owner account registration immediately upon deployment.
### Q: What happens if I lose my `N8N_ENCRYPTION_KEY`?
**A:** If you lose this key, n8n cannot decrypt your stored credentials (API keys, OAuth tokens, database passwords). You will have to manually re-enter credentials for all integrations. Always store your key in a password manager.
### Q: How do I migrate SQLite data to PostgreSQL?
**A:** Configure your PostgreSQL n8n instance using the exact same `N8N_ENCRYPTION_KEY` as your SQLite instance. Export your workflows and credentials using the n8n UI (or via CLI `n8n export:workflow` and `n8n export:credentials`), then import them into the new PostgreSQL-backed instance.
### Q: Can I use custom npm packages inside Code nodes?
**A:** Yes. Set `NODE_FUNCTION_ALLOW_EXTERNAL=axios,lodash` in your `.env` file and ensure those npm packages are installed inside the n8n container (typically using a custom container Dockerfile build).
---
## Summary
Self-hosting n8n gives you total data privacy, custom flexibility, and freedom from per-execution SaaS fees. By pairing Docker Compose with PostgreSQL and Caddy, you get a secure, production-oriented automation server ready for real-world workflows.
Discussion
No comments yet. Be the first to start the discussion.