Skip to content
Back to blog

Deploying n8n on Cloudflare: A Self-Hosted Automation Guide

Learn how to deploy n8n with Docker and Cloudflare Tunnel for secure, self-hosted automation with SSL, custom domains, and best practices.

MH
Mahmoud Hashem
· August 25, 2024 · 7 min read
Server infrastructure for self-hosted automation deployment

Introduction

Self-hosting n8n gives you unlimited workflow executions, complete data control, and no per-task costs. But setting up a secure, accessible deployment requires handling networking, SSL, and domain configuration. Cloudflare Tunnel provides an elegant solution—creating a secure connection from your server to Cloudflare’s network without exposing any ports to the public internet.

This guide walks through deploying n8n with Docker, securing it with Cloudflare Tunnel, and configuring a custom domain with automatic SSL.

Why Cloudflare Tunnel?

Traditional self-hosting requires opening ports on your firewall and managing SSL certificates. Cloudflare Tunnel (formerly Argo Tunnel) eliminates these requirements:

  • No open ports: The tunnel creates an outbound connection from your server to Cloudflare
  • Automatic SSL: Cloudflare handles SSL/TLS certificates
  • DDoS protection: Cloudflare’s network protects your instance
  • No IP exposure: Your server’s IP address is never exposed
  • Easy custom domains: Configure domains through Cloudflare DNS

Prerequisites

Before starting, you need:

  • A server (VPS, dedicated server, or local machine) with Docker installed
  • A Cloudflare account with a registered domain
  • Basic familiarity with Docker and command-line tools

Step 1: Deploy n8n with Docker

Create the Docker Compose Configuration

# docker-compose.yml
version: '3.8'

services:
  n8n:
    image: n8nio/n8n:latest
    restart: always
    ports:
      - "127.0.0.1:5678:5678"
    environment:
      # Basic configuration
      - N8N_HOST=n8n.yourdomain.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - N8N_EDITOR_BASE_URL=https://n8n.yourdomain.com
      - WEBHOOK_URL=https://n8n.yourdomain.com/
      
      # Security
      - N8N_ENCRYPTION_KEY=${N8N_ENCRYPTION_KEY}
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD}
      
      # Database (PostgreSQL for production)
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=${POSTGRES_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      
      # Execution settings
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=168
      - N8N_METRICS=true
      
      # Timezone
      - GENERIC_TIMEZONE=America/New_York
      
    volumes:
      - n8n_data:/home/node/.n8n
    depends_on:
      - postgres

  postgres:
    image: postgres:16-alpine
    restart: always
    environment:
      - POSTGRES_DB=n8n
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    # Don't expose ports - only accessible within Docker network

volumes:
  n8n_data:
  postgres_data:

Create the Environment File

# .env file
N8N_ENCRYPTION_KEY=generate-a-random-32-char-string-here
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=your-secure-password
POSTGRES_USER=n8n
POSTGRES_PASSWORD=your-secure-db-password

Generate a secure encryption key:

openssl rand -hex 32

Start the Services

# Start n8n and PostgreSQL
docker compose up -d

# Check that services are running
docker compose ps

# View logs
docker compose logs -f n8n

n8n should now be running on localhost:5678. Note that we bound the port to 127.0.0.1 only, so it’s not accessible from outside the server.

Step 2: Install and Configure Cloudflare Tunnel

Install cloudflared

# On Ubuntu/Debian
curl -L https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o cloudflared.deb
sudo dpkg -i cloudflared.deb

# Verify installation
cloudflared --version

Authenticate with Cloudflare

cloudflared tunnel login

This opens a browser window to authenticate with your Cloudflare account. Select the domain you want to use for n8n.

Create a Tunnel

# Create a named tunnel
cloudflared tunnel create n8n-tunnel

# This creates a tunnel with a unique ID and a credentials file
# Note the tunnel ID and credentials file path

Configure the Tunnel

Create a configuration file for the tunnel:

# ~/.cloudflared/config.yml
tunnel: <your-tunnel-id>
credentials-file: /root/.cloudflared/<your-tunnel-id>.json

ingress:
  - hostname: n8n.yourdomain.com
    service: http://localhost:5678
  - service: http_status:404

Create DNS Record

# Create a CNAME record pointing to the tunnel
cloudflared tunnel route dns n8n-tunnel n8n.yourdomain.com

This automatically creates a CNAME record in your Cloudflare DNS pointing n8n.yourdomain.com to the tunnel.

Test the Tunnel

# Run the tunnel in the foreground to test
cloudflared tunnel run n8n-tunnel

Open a browser and navigate to https://n8n.yourdomain.com. You should see the n8n setup page.

Step 3: Run Cloudflare Tunnel as a Service

Create a Systemd Service

# /etc/systemd/system/cloudflared.service
[Unit]
Description=Cloudflare Tunnel for n8n
After=network.target

[Service]
TimeoutStartSec=0
Type=notify
ExecStart=/usr/bin/cloudflared tunnel --config /root/.cloudflared/config.yml run n8n-tunnel
Restart=on-failure
RestartSec=5s

[Install]
WantedBy=multi-user.target

Enable and Start the Service

# Reload systemd
sudo systemctl daemon-reload

# Enable the service to start on boot
sudo systemctl enable cloudflared

# Start the service
sudo systemctl start cloudflared

# Check status
sudo systemctl status cloudflared

Step 4: Configure Cloudflare Settings

SSL/TLS Settings

In your Cloudflare dashboard:

  1. Navigate to SSL/TLS settings
  2. Set encryption mode to Full (not Flexible, which can cause redirect loops)
  3. Enable Always Use HTTPS
  4. Enable Automatic HTTPS Rewrites

Access Policies (Optional)

Restrict access to specific IP addresses or require Cloudflare authentication:

# In Cloudflare Zero Trust dashboard
# Access > Applications > Add Application
# Type: Self-hosted
# Domain: n8n.yourdomain.com

# Policy:
# - Allow specific emails or email domains
# - Or allow specific IP ranges

Rate Limiting

Protect your n8n instance from abuse:

# Cloudflare Rate Limiting Rule
# If: requests to n8n.yourdomain.com/*
# Then: rate limit to 100 requests per 10 seconds per IP
# Action: Block for 60 seconds

Step 5: Security Best Practices

Secure the Docker Deployment

# Add to docker-compose.yml for additional security
services:
  n8n:
    # Run as non-root user
    user: "node:node"
    
    # Read-only filesystem where possible
    read_only: false
    
    # Drop all capabilities
    cap_drop:
      - ALL
    
    # No new privileges
    security_opt:
      - no-new-privileges:true
    
    # Resource limits
    deploy:
      resources:
        limits:
          memory: 1G
          cpus: '1.0'

Environment Security

# Secure the .env file
chmod 600 .env
chown root:root .env

# Regularly rotate the encryption key (requires re-encrypting credentials)
# Back up the .env file securely

Database Security

# PostgreSQL security in docker-compose.yml
services:
  postgres:
    # Don't expose any ports
    # Only accessible within Docker network
    
    # Add health check
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U n8n"]
      interval: 10s
      timeout: 5s
      retries: 5

Regular Backups

#!/bin/bash
# backup-n8n.sh - Run daily via cron

BACKUP_DIR="/backups/n8n"
DATE=$(date +%Y%m%d_%H%M%S)

# Backup PostgreSQL
docker compose exec -T postgres pg_dump -U n8n n8n | gzip > \
  "$BACKUP_DIR/db_$DATE.sql.gz"

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

# Keep only last 30 days of backups
find $BACKUP_DIR -type f -mtime +30 -delete
# Add to crontab
0 2 * * * /path/to/backup-n8n.sh

Step 6: Monitoring and Maintenance

Health Checks

#!/bin/bash
# health-check.sh

# Check if n8n is responding
HTTP_STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:5678)

if [ "$HTTP_STATUS" != "200" ] && [ "$HTTP_STATUS" != "302" ]; then
    echo "n8n is down! HTTP status: $HTTP_STATUS"
    # Send alert
    curl -X POST "https://alerts.example.com/api" \
         -d "message=n8n is down (HTTP $HTTP_STATUS)"
    
    # Try to restart
    docker compose restart n8n
fi

# Check if tunnel is running
if ! systemctl is-active --quiet cloudflared; then
    echo "Cloudflare tunnel is down!"
    systemctl restart cloudflared
fi

Update n8n

# Pull the latest image
docker compose pull

# Recreate containers with the new image
docker compose up -d

# Verify the update
docker compose logs n8n | tail -20

Monitor Resource Usage

# Check Docker resource usage
docker stats

# Check disk space
df -h

# Check Docker volume sizes
docker system df -v

Troubleshooting

Common Issues

n8n shows “connection refused”: Ensure n8n is running and the port is correct in the tunnel config.

Redirect loops: Set Cloudflare SSL mode to “Full” not “Flexible”.

Webhooks not working: Ensure WEBHOOK_URL is set correctly to your public URL.

Tunnel disconnects: Check the cloudflared service logs and ensure your server has a stable internet connection.

# Check tunnel logs
journalctl -u cloudflared -f

# Check n8n logs
docker compose logs -f n8n

Conclusion

Deploying n8n with Cloudflare Tunnel provides a secure, self-hosted automation platform without exposing your server to the public internet. The combination of Docker for containerization, PostgreSQL for reliable data storage, and Cloudflare Tunnel for secure access creates a production-ready setup that costs only a few dollars per month in hosting.

The key advantages—no open ports, automatic SSL, DDoS protection, and unlimited workflow executions—make this setup ideal for individuals and small teams who want the power of n8n without the limitations of cloud plans. With proper backups, monitoring, and security configuration, your self-hosted n8n instance will run reliably and securely for the long term.

#n8n #Cloudflare #DevOps

Related articles

Let's build something great together

Ready to automate your business processes and save hundreds of hours every month? Let's talk about your project.