Self-Hosted n8n with Docker Compose: The 2026 Production Guide
Master self-hosted n8n automation on your homelab with this step-by-step Docker Compose guide covering setup, hardening, and troubleshooting.
Introduction
n8n is a fair-code workflow automation tool that gives you granular control over your data and processes. Unlike cloud-only SaaS platforms, self-hosting n8n puts your workflows, credentials, and execution history behind your own firewall. For homelab enthusiasts, this means no per-execution fees, unlimited workflow runs, and the ability to integrate with internal services that should never leave your network, such as your Pi-hole, Proxmox cluster, or NAS.
In this guide, you will learn how to deploy n8n version 2.36.8 using Docker Compose. We will cover the essential prerequisites, a production-ready Docker Compose configuration, critical configuration settings, and a troubleshooting table for common pitfalls. We will also walk through advanced setups like reverse proxying with SSL and creating automated backups.
By the end of this guide, you will have a secure, persistent n8n installation that survives container recreations and reboots. You will also understand how to apply basic hardening principles without breaking the application. This guide assumes you are comfortable with the Linux command line and have a basic understanding of Docker concepts.
The version pinned in this guide is 2.36.8 (published 2026-08-28). Always check the official n8n GitHub releases page before pinning a version, as newer stable releases may include critical security patches or features you need.
Prerequisites
Before you begin, ensure your homelab server meets the following requirements. These are typical values for a single-user or small-team setup; your actual usage may vary based on the complexity of your workflows and the number of concurrent executions.
| Component | Minimum | Recommended | Notes |
|---|---|---|---|
| CPU | 1 vCPU | 2 vCPU | Workflows with heavy data transformation (e.g., JSON/CSV processing) will benefit from more cores. |
| RAM | 1 GB | 2-4 GB | n8n itself uses ~300 MB. The rest is used by the Node.js runtime and your external services (PostgreSQL, Redis). |
| Storage | 10 GB | 20 GB+ | This accounts for the Docker images, database growth, and workflow execution data. Use an SSD for better performance. |
| OS | Ubuntu 22.04+ / Debian 12+ | Any modern Linux distro | You need a 64-bit system with kernel support for Docker. |
| Software | Docker Engine 24+ | Docker Engine 26+ | Includes Docker Compose v2 plugin. |
| Network | Static IP or Dynamic DNS | - | Required for stable access and SSL certificate issuance. |
Important: Do not attempt to run this on a Raspberry Pi 1 or 2; the ARM architecture is supported, but the limited RAM will cause frequent OOM (Out of Memory) kills.
Step-by-Step Installation
Step 1: Create Project Directory and .env File
First, create a dedicated directory for n8n and navigate into it. Then, create a .env file to store your secrets. This file will be read by Docker Compose.
mkdir -p ~/n8n-docker && cd ~/n8n-docker
Now, create the .env file using your preferred text editor (e.g., nano .env). Paste the following content and change the passwords and user values immediately.
# n8n configuration
N8N_VERSION=2.36.8
N8N_HOST=n8n.yourdomain.com
N8N_PORT=5678
N8N_PROTOCOL=https
N8N_USER_FOLDER=/home/node
# Database configuration
POSTGRES_USER=n8n_user
POSTGRES_PASSWORD=change_this_strong_password_2026
POSTGRES_DB=n8n_db
# Timezone & execution data
GENERIC_TIMEZONE=UTC
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}
Security Warning: Never commit this .env file to a Git repository. Add it to your .gitignore file immediately. The passwords and secrets in this file are the keys to your automation kingdom.
Step 2: Create Docker Compose File
Create a docker-compose.yml file in the same directory. This configuration uses the n8nio/n8n image with the version from your .env file. It sets up a PostgreSQL database for persistent storage and a named volume for n8n's own data.
services:
n8n:
image: n8nio/n8n:${N8N_VERSION:-latest}
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_HOST=${N8N_HOST}
- N8N_PORT=${N8N_PORT}
- N8N_PROTOCOL=${N8N_PROTOCOL}
- NODE_ENV=production
- N8N_USER_FOLDER=${N8N_USER_FOLDER}
- GENERIC_TIMEZONE=${GENERIC_TIMEZONE}
- DB_TYPE=${DB_TYPE}
- DB_POSTGRESDB_HOST=${DB_POSTGRESDB_HOST}
- DB_POSTGRESDB_PORT=${DB_POSTGRESDB_PORT}
- DB_POSTGRESDB_DATABASE=${DB_POSTGRESDB_DATABASE}
- DB_POSTGRESDB_USER=${DB_POSTGRESDB_USER}
- DB_POSTGRESDB_PASSWORD=${DB_POSTGRESDB_PASSWORD}
volumes:
- n8n_data:/home/node/.n8n
depends_on:
- postgres
networks:
- n8n_network
postgres:
image: postgres:16-alpine
container_name: n8n-postgres
restart: unless-stopped
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 -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- n8n_network
volumes:
n8n_data:
postgres_data:
networks:
n8n_network:
driver: bridge
Note on User Permissions: The n8n container runs as the node user (UID 1000 by default). The Docker volume n8n_data will be created with root ownership on your host. To avoid permission issues, verify your host user's UID/GID by running id -u && id -g. If your host user is not UID 1000, you may need to adjust the volume ownership or run the container with a different user. Check the official n8n Docker documentation for the correct environment variables to set the user ID.
Step 3: Start the Stack
Pull the images and start the containers in detached mode.
docker compose up -d
Check the logs to ensure n8n starts without errors.
docker compose logs -f n8n
Wait for the log line that says Editor is now accessible via: http://localhost:5678. This indicates n8n is ready.
Step 4: Initial Setup and Owner Account
Open your browser and navigate to http://your-server-ip:5678. You will be prompted to create an owner account. Use a strong password and a valid email address. This account has full administrative rights over your n8n instance.
Step 5: Configure N8N Environment Variables (Optional)
For advanced configurations, you can add more environment variables to the n8n service in your docker-compose.yml. For example, to enable basic authentication for the API, add N8N_BASIC_AUTH_ACTIVE=true, N8N_BASIC_AUTH_USER=admin, and N8N_BASIC_AUTH_PASSWORD=your_password. Always refer to the official n8n documentation for the full list of environment variables.
Step 6: Set Up External Database Backups
Your workflows and credentials are stored in PostgreSQL. Back up the postgres_data volume or use pg_dump to create logical backups. Here is a simple cron job that dumps the database daily.
crontab -e
Add the following line to run a backup at 2 AM daily.
0 2 * * * docker exec n8n-postgres pg_dump -U ${POSTGRES_USER} ${POSTGRES_DB} | gzip > ~/n8n-backups/backup-$(date +\%Y-\%m-\%d).sql.gz
Note: Replace ${POSTGRES_USER} and ${POSTGRES_DB} with the actual values from your .env file. Ensure the ~/n8n-backups directory exists.
Advanced Configuration & Optimization
Reverse Proxy with SSL (Caddy)
Exposing n8n directly on port 5678 is fine for testing, but for production, you should use a reverse proxy. Caddy is a popular choice because it automatically manages Let's Encrypt SSL certificates. Here is a minimal Caddyfile configuration.
n8n.yourdomain.com {
reverse_proxy n8n:5678
}
In this setup, Caddy and n8n should be on the same Docker network. Update your docker-compose.yml to add Caddy as a service and remove the ports section from the n8n service, as Caddy will handle external traffic.
Security Hardening (Optional)
The following settings add layers of security but may break functionality if not configured correctly. Test them in a staging environment first.
| Setting | Description | Risk |
|---|---|---|
read_only: true |
Makes the container's filesystem read-only. | n8n may need to write to temporary directories. You must mount a writable volume for /tmp and /home/node/.n8n if you use this. |
| `security_opt: | ||
| - no-new-privileges:true` | Prevents privilege escalation. | Generally safe, but test with your workflows. |
| `cap_drop: | ||
| - ALL` | Removes all Linux capabilities. | n8n may require CHOWN or SETUID to function properly. You will need to add them back selectively. |
Warning: Do not copy these settings directly into your production file without understanding the implications. They can silently break file uploads, credential encryption, and other features.
Resource Limits
To prevent n8n from consuming all your homelab's resources, add resource limits to the n8n service.
services:
n8n:
deploy:
resources:
limits:
memory: 2G
cpus: '1.5'
These limits are not hard caps in Docker Compose v2 without Swarm, but they act as guidelines for the scheduler.
Troubleshooting Common Issues
| Error/Issue | Likely Cause | Solution |
|---|---|---|
Error: Connection refused when n8n tries to connect to Postgres |
Postgres container is not ready or credentials are wrong. | Check that the postgres service is running (docker compose ps) and that the environment variables in .env match those in docker-compose.yml. Wait for the Postgres healthcheck to pass. |
Permission denied when writing to /home/node/.n8n |
The Docker volume is owned by root or a different UID. | Run docker compose exec n8n id to see the user ID inside the container. On the host, run sudo chown -R 1000:1000 ~/n8n-docker (assuming the volume is a bind mount). For named volumes, you may need to copy data out and back in with correct ownership. |
Workflow execution stuck in 'Waiting' |
The default timezone is not set correctly, or the execution process is stuck. | Set GENERIC_TIMEZONE to your local timezone in .env. Restart the n8n container. If it persists, check the n8n logs for stack traces. |
n8n is unreachable after reboot |
The containers did not restart automatically. | Ensure you have restart: unless-stopped in your docker-compose.yml. Check docker compose ps after reboot. Also, ensure Docker service is enabled (sudo systemctl enable docker). |
SSL/TLS handshake errors when using webhooks |
Your reverse proxy is not configured for WebSocket upgrade or SSL termination. | For Caddy, ensure you are not stripping the Upgrade header. For Nginx, you need the proxy_set_header Upgrade $http_upgrade; and proxy_set_header Connection "upgrade"; directives. |
Image not found when pulling |
The version tag in .env does not exist. |
Verify the version on the official n8n Docker Hub page. Change N8N_VERSION to a valid tag like 2.36.8 or use latest. |
Conclusion
You now have a fully functional, self-hosted n8n instance running on Docker Compose. You have separated the application from its database, ensured persistence with volumes, and configured a basic backup strategy. This setup is a solid foundation for building complex automation without relying on external cloud services.
Remember that self-hosting is an ongoing process. Stay updated with n8n releases and security advisories. Regularly test your backups to ensure they are restorable. The flexibility and control you gain are well worth the maintenance effort.
FAQ
1. How do I update n8n to a newer version?
To update, change the N8N_VERSION variable in your .env file to the new version number. Then run docker compose pull followed by docker compose up -d. This will recreate the n8n container with the new image while preserving your data in the volumes.
2. Can I use SQLite instead of PostgreSQL?
Yes, n8n supports SQLite. For simple setups with low concurrency, you can remove the postgres service and change the database environment variables to use DB_TYPE=sqlite. However, for production use, PostgreSQL is strongly recommended for better concurrency handling and data integrity.
3. How do I expose n8n to the internet securely?
Use a reverse proxy like Caddy, Traefik, or Nginx with automatic HTTPS. Never expose the raw n8n port (5678) directly. Additionally, enable basic authentication via environment variables or set up SSO with OAuth2 to protect the editor UI.
4. How do I back up my n8n workflows and credentials?
Your workflows and credentials are stored in the database. Back up the postgres_data volume or use pg_dump as shown in Step 6. The n8n_data volume contains static files like binary data and cache, which are less critical but should also be backed up.
5. Why is my n8n instance slow when executing large workflows?
Performance is typically bound by CPU and memory. Large JSON payloads and complex data transformations are CPU-intensive. Check your resource limits and consider increasing the memory limit. Also, ensure your database is on an SSD. If you are using a Raspberry Pi, consider moving to a more powerful server.