automation · 15 read

How to Set Up Self-Hosted n8n: Complete Installation Guide

Step-by-step guide to setting up self-hosted n8n with Docker, including SSL via Caddy, automated backups, monitoring, and production best practices.

How to Set Up Self-Hosted n8n: Complete Installation Guide

n8n is a powerful workflow automation tool that you can run on your own server. Unlike Zapier or Make, self-hosted n8n has no per-execution fees - pay only for your server.

I've deployed n8n for multiple clients and use it for my own automation needs. In my experience, self-hosting is worth the initial setup effort. This guide walks you through setting up n8n from download to production-ready deployment.

Why Self-Host n8n?

Before we start the installation, here's why you might choose self-hosted over n8n Cloud:

Cost savings: No per-execution fees. Run 100,000 workflows per month for the cost of a $20/month server.

Data privacy: Your data stays on your server. Critical for healthcare, finance, or any sensitive data.

Full control: Customize everything. No vendor limitations.

No vendor lock-in: Your workflows are yours. Export and migrate anytime.

The trade-off: You manage the infrastructure. I found it's surprisingly low-maintenance once set up properly - this guide shows you how.

What Do You Need Before Installing n8n?

Before you download and install n8n, you'll need:

  • A server (VPS) with at least 2GB RAM
  • Ubuntu 22.04 or newer (this guide uses Ubuntu)
  • A domain name pointed to your server
  • Basic command line knowledge
  • SSH access to your server

Recommended providers:

  • DigitalOcean ($12-24/month)
  • Hetzner ($5-10/month) - Best value
  • Linode ($12-24/month)
  • AWS Lightsail ($10-20/month)

Quick Start: n8n Download and Setup

For those who want to get running fast, here's the minimal setup:

# Install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Run n8n
docker run -d \
  --name n8n \
  --restart always \
  -p 5678:5678 \
  -v n8n_data:/home/node/.n8n \
  n8nio/n8n

Access n8n at http://your-server-ip:5678

This works but isn't production-ready. Keep reading for a proper setup.

Step 1: Prepare Your Server

Update System

sudo apt update && sudo apt upgrade -y

Install Docker

# Download and install Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sudo sh get-docker.sh

# Add your user to docker group
sudo usermod -aG docker $USER

# Log out and back in, then verify
docker --version

Install Docker Compose

sudo apt install docker-compose-plugin -y

# Verify
docker compose version

Configure Firewall

sudo ufw allow 22    # SSH
sudo ufw allow 80    # HTTP
sudo ufw allow 443   # HTTPS
sudo ufw enable

Step 2: Set Up n8n with Docker Compose

Create a directory for n8n:

mkdir -p ~/n8n && cd ~/n8n

Create the Docker Compose file:

nano docker-compose.yml

Paste this configuration:

version: '3.8'

services:
  n8n:
    image: n8nio/n8n
    container_name: n8n
    restart: always
    ports:
      - "5678:5678"
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=https://${N8N_HOST}/
      - GENERIC_TIMEZONE=${TIMEZONE}
      # Security
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
      # Database (optional - SQLite by default)
      # - DB_TYPE=postgresdb
      # - DB_POSTGRESDB_HOST=postgres
      # - DB_POSTGRESDB_DATABASE=n8n
      # - DB_POSTGRESDB_USER=n8n
      # - DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
      - ./local-files:/files
    networks:
      - n8n-network

networks:
  n8n-network:

volumes:
  n8n_data:

Create the environment file:

nano .env

Add your configuration:

N8N_HOST=n8n.yourdomain.com
TIMEZONE=America/New_York
N8N_USER=admin
N8N_PASSWORD=your-secure-password-here

Important: Use a strong password. This protects your entire automation system.

Step 3: Set Up SSL with Caddy

n8n needs HTTPS for webhooks to work reliably. We'll use Caddy as a reverse proxy - it handles SSL certificates automatically.

Add Caddy to your docker-compose.yml:

version: '3.8'

services:
  caddy:
    image: caddy:2
    container_name: caddy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
      - caddy_config:/config
    networks:
      - n8n-network

  n8n:
    image: n8nio/n8n
    container_name: n8n
    restart: always
    # Remove ports - Caddy handles external access
    # ports:
    #   - "5678:5678"
    environment:
      - N8N_HOST=${N8N_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - NODE_ENV=production
      - WEBHOOK_URL=https://${N8N_HOST}/
      - GENERIC_TIMEZONE=${TIMEZONE}
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
    volumes:
      - n8n_data:/home/node/.n8n
      - ./local-files:/files
    networks:
      - n8n-network

networks:
  n8n-network:

volumes:
  n8n_data:
  caddy_data:
  caddy_config:

Create the Caddyfile:

nano Caddyfile

Add:

n8n.yourdomain.com {
    reverse_proxy n8n:5678
}

Replace n8n.yourdomain.com with your actual domain.

Step 4: Start n8n

docker compose up -d

Check that everything is running:

docker compose ps

You should see both caddy and n8n with status "Up".

Access n8n at https://n8n.yourdomain.com

Step 5: Configure n8n

Initial Setup

On first access:

  1. Log in with the credentials from your .env file
  2. Create your owner account (this is different from basic auth)
  3. Set up your first workflow

Essential Settings

Go to Settings → Personal:

  • Set your timezone correctly
  • Configure your name and email

Go to Settings → Community nodes (optional):

  • Enable community nodes for additional integrations

Security Hardening

Add these environment variables for additional security:

environment:
  # ... existing vars ...
  - N8N_BLOCK_ENV_ACCESS_IN_NODE=true
  - N8N_PERSONALIZATION_ENABLED=false
  - EXECUTIONS_DATA_PRUNE=true
  - EXECUTIONS_DATA_MAX_AGE=168  # 7 days in hours

Step 6: Set Up Backups

Your n8n data is stored in Docker volumes. Set up automatic backups:

Create backup script:

nano ~/n8n/backup.sh

Add:

#!/bin/bash
BACKUP_DIR="/home/$USER/n8n-backups"
DATE=$(date +%Y-%m-%d_%H-%M-%S)

mkdir -p $BACKUP_DIR

# Export workflows
docker exec n8n n8n export:workflow --all --output=/files/workflows-$DATE.json

# Export credentials (encrypted)
docker exec n8n n8n export:credentials --all --output=/files/credentials-$DATE.json

# Backup the volume
docker run --rm \
  -v n8n_n8n_data:/data \
  -v $BACKUP_DIR:/backup \
  alpine tar czf /backup/n8n-data-$DATE.tar.gz -C /data .

# Move exported files
mv ~/n8n/local-files/workflows-$DATE.json $BACKUP_DIR/
mv ~/n8n/local-files/credentials-$DATE.json $BACKUP_DIR/

# Keep only last 7 backups
ls -tp $BACKUP_DIR/*.tar.gz | tail -n +8 | xargs -I {} rm -- {}
ls -tp $BACKUP_DIR/workflows-*.json | tail -n +8 | xargs -I {} rm -- {}
ls -tp $BACKUP_DIR/credentials-*.json | tail -n +8 | xargs -I {} rm -- {}

echo "Backup completed: $DATE"

Make it executable and schedule:

chmod +x ~/n8n/backup.sh

# Add to crontab - daily at 2 AM
(crontab -l 2>/dev/null; echo "0 2 * * * /home/$USER/n8n/backup.sh") | crontab -

Step 7: Set Up Monitoring

Basic Health Check

Add a simple health check script:

nano ~/n8n/health-check.sh
#!/bin/bash
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" https://n8n.yourdomain.com/healthz)

if [ "$HEALTH" != "200" ]; then
    echo "n8n is down! Status: $HEALTH" | mail -s "n8n Alert" your@email.com
    # Or send to Slack/Discord webhook
fi

Execution Monitoring

Create a workflow in n8n that:

  1. Runs on schedule (daily)
  2. Queries the n8n API for failed executions
  3. Sends alert if failures exceed threshold

Production Configuration

Using PostgreSQL Instead of SQLite

For production with high volume, use PostgreSQL:

services:
  postgres:
    image: postgres:15
    container_name: n8n-postgres
    restart: always
    environment:
      - POSTGRES_USER=n8n
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_DB=n8n
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - n8n-network

  n8n:
    # ... existing config ...
    environment:
      # ... existing vars ...
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${DB_PASSWORD}
    depends_on:
      - postgres

Add to .env:

DB_PASSWORD=your-database-password

Queue Mode for High Volume

For many concurrent executions, enable queue mode with Redis:

services:
  redis:
    image: redis:7
    container_name: n8n-redis
    restart: always
    networks:
      - n8n-network

  n8n:
    environment:
      - EXECUTIONS_MODE=queue
      - QUEUE_BULL_REDIS_HOST=redis
      - QUEUE_HEALTH_CHECK_ACTIVE=true

Resource Limits

Add resource limits to prevent runaway workflows:

n8n:
  # ... existing config ...
  deploy:
    resources:
      limits:
        memory: 2G
        cpus: '2'
      reservations:
        memory: 512M

How Do You Update n8n?

Check Current Version

docker exec n8n n8n --version

Update Process

cd ~/n8n

# Pull latest image
docker compose pull

# Restart with new image
docker compose up -d

# Check logs for issues
docker compose logs -f n8n

Rollback if Needed

# Stop current version
docker compose down

# Use specific version
# Edit docker-compose.yml: image: n8nio/n8n:0.236.0
docker compose up -d

Troubleshooting

n8n Won't Start

Check logs:

docker compose logs n8n

Common issues:

  • Port already in use: Change the port in docker-compose.yml
  • Permission issues: Check volume permissions
  • Memory issues: Increase server RAM or add swap

Webhooks Not Working

Verify:

  1. SSL is working: curl -I https://n8n.yourdomain.com
  2. WEBHOOK_URL is correct in .env
  3. Firewall allows 443

Executions Failing

Check:

  • Execution logs in n8n UI
  • Docker logs: docker compose logs -f n8n
  • Resource usage: docker stats

Database Connection Issues

For PostgreSQL:

# Check if postgres is running
docker compose ps postgres

# Check connection
docker exec n8n-postgres psql -U n8n -d n8n -c "SELECT 1;"

Where Do You Download n8n?

Official Sources

  • Docker Hub: docker pull n8nio/n8n
  • GitHub: https://github.com/n8n-io/n8n
  • npm: npm install -g n8n (not recommended for production)

Version Pinning

For production, pin to specific versions:

image: n8nio/n8n:1.20.0

Check releases: https://github.com/n8n-io/n8n/releases

Next Steps

Now that n8n is running:

  1. Create your first workflow: Start with something simple like a webhook-to-Slack notification
  2. Import community workflows: Many templates available on n8n.io
  3. Set up error notifications: Create a workflow that alerts on execution failures
  4. Document your setup: Keep notes for future maintenance

Related resources:

Summary

Self-hosted n8n setup involves:

  1. Server with Docker
  2. Docker Compose configuration
  3. SSL via Caddy reverse proxy
  4. Backup automation
  5. Monitoring setup
  6. Regular updates

Total setup time: 30-60 minutes for a production-ready deployment.

The ongoing maintenance is minimal - occasional updates and monitoring. The cost savings compared to per-execution pricing make self-hosting worthwhile for any serious automation use.

Frequently Asked Questions

How do you install n8n on a server?

The recommended method is Docker Compose: install Docker on a VPS, create a docker-compose.yml file defining the n8n service, add a Caddy reverse proxy for SSL, then run docker compose up -d. A production-ready deployment takes 30-60 minutes from start to finish.

What are the system requirements for self-hosted n8n?

A small VPS with at least 2GB of RAM running Ubuntu 22.04 or newer is enough for most setups. You also need a domain name pointed at the server and SSH access. For high-volume use, increase RAM and switch from SQLite to PostgreSQL.

How much does it cost to self-host n8n?

n8n itself is free and open-source. The only cost is the server, which runs roughly $5-24 per month depending on the provider. Hetzner is the best value at $5-10 per month; DigitalOcean and Linode sit higher. There are no per-execution fees.

Is self-hosted n8n secure?

It can be, with the right setup. Enable basic authentication, run behind HTTPS via a reverse proxy, set N8N_BLOCK_ENV_ACCESS_IN_NODE=true, and keep n8n updated for security patches. Regular backups of the data volume protect against data loss.

Should you use SQLite or PostgreSQL with n8n?

SQLite is fine for light use and is the default - no extra setup needed. Switch to PostgreSQL for production with high execution volume, where SQLite can become a bottleneck. PostgreSQL also handles concurrent access better when running n8n in queue mode.

How do you back up a self-hosted n8n instance?

Back up the Docker data volume at /home/node/.n8n and export workflows and credentials with the n8n export commands. A simple cron job running daily, keeping the last seven backups, covers most needs. Store backups off the server for safety.


Need help with n8n setup or building complex workflows? Check out my n8n automation services or let's talk about your automation needs.

Need help with automation?

Let's discuss your project.

Book a discovery call