Skip to content
Back to blog

Deploying n8n on Cloudflare Pages: A Self-Hosted Automation Stack

A complete guide to deploying a self-hosted n8n instance alongside a static site on Cloudflare Pages, with Docker and a reverse proxy.

MH
Mahmoud Hashem
· March 15, 2024 · 5 min read
Server deployment

Why self-host n8n?

n8n Cloud is convenient, but self-hosting gives you unlimited executions, full data control, and zero per-execution costs. For a business running dozens of workflows, self-hosting can save hundreds of dollars per month.

In this guide, I’ll walk through deploying a production-ready n8n instance that pairs well with a static site on Cloudflare Pages.

Architecture overview

Internet → Cloudflare (DNS + SSL) → VPS

                              Nginx Reverse Proxy
                              ↙            ↘
                        n8n (Docker)    Static Files

                        PostgreSQL (Docker)

                        Redis (Docker)

Prerequisites

  • A VPS (I recommend Hetzner CX22 at €4.5/month or DigitalOcean $6/month)
  • A domain name
  • A Cloudflare account (free tier works)
  • Docker and Docker Compose installed on the VPS

Step 1: Set up DNS on Cloudflare

  1. Add your domain to Cloudflare

  2. Create an A record pointing to your VPS IP:

    • automation.yourdomain.comyour.vps.ip
    • Enable the orange cloud (proxy) for DDoS protection and SSL
  3. Create another A record for your static site:

    • yourdomain.com → Cloudflare Pages (configured separately)

Step 2: Docker Compose setup

Create a docker-compose.yml file:

version: '3.8'

services:
  postgres:
    image: postgres:15-alpine
    restart: always
    environment:
      POSTGRES_DB: n8n
      POSTGRES_USER: n8n
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    healthcheck:
      test: ['CMD-SHELL', 'pg_isready -U n8n']
      interval: 10s
      timeout: 5s
      retries: 5

  redis:
    image: redis:7-alpine
    restart: always
    command: redis-server --requirepass ${REDIS_PASSWORD}
    volumes:
      - redis_data:/data

  n8n:
    image: n8nio/n8n:latest
    restart: always
    depends_on:
      postgres:
        condition: service_healthy
      redis:
        condition: service_started
    environment:
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres
      - DB_POSTGRESDB_DATABASE=n8n
      - DB_POSTGRESDB_USER=n8n
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
      - N8N_HOST=automation.yourdomain.com
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://automation.yourdomain.com/
      - EXECUTIONS_MODE=queue
      - REDIS_HOST=redis
      - REDIS_PASSWORD=${REDIS_PASSWORD}
      - N8N_BASIC_AUTH_ACTIVE=true
      - N8N_BASIC_AUTH_USER=${N8N_USER}
      - N8N_BASIC_AUTH_PASSWORD=${N8N_PASSWORD}
      - N8N_ENCRYPTION_KEY=${ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=America/New_York
    volumes:
      - n8n_data:/home/node/.n8n
    ports:
      - "127.0.0.1:5678:5678"

volumes:
  postgres_data:
  redis_data:
  n8n_data:

Step 3: Environment variables

Create a .env file:

POSTGRES_PASSWORD=your_secure_password_here
REDIS_PASSWORD=your_redis_password_here
N8N_USER=admin
N8N_PASSWORD=your_secure_n8n_password
ENCRYPTION_KEY=your_32_char_encryption_key_here

Generate a secure encryption key:

openssl rand -hex 32

Step 4: Nginx reverse proxy

Install Nginx on the VPS:

sudo apt install nginx

Create a configuration file at /etc/nginx/sites-available/n8n:

server {
    listen 80;
    server_name automation.yourdomain.com;
    
    # Redirect to HTTPS
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name automation.yourdomain.com;
    
    # SSL certificates (managed by Cloudflare)
    ssl_certificate /etc/ssl/certs/cloudflare.crt;
    ssl_certificate_key /etc/ssl/private/cloudflare.key;
    
    # Cloudflare real IP
    set_real_ip_from 173.245.48.0/20;
    set_real_ip_from 103.21.244.0/22;
    set_real_ip_from 103.22.200.0/22;
    set_real_ip_from 104.16.0.0/13;
    set_real_ip_from 104.24.0.0/14;
    real_ip_header CF-Connecting-IP;
    
    # n8n proxy
    location / {
        proxy_pass http://127.0.0.1:5678;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # WebSocket support
        proxy_read_timeout 86400;
    }
    
    # Webhook endpoint (no basic auth for public webhooks)
    location /webhook/ {
        proxy_pass http://127.0.0.1:5678/webhook/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Enable the site:

sudo ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

Step 5: SSL with Cloudflare

Since Cloudflare proxies your traffic, you can use Cloudflare’s SSL. Set the SSL mode to “Full” in Cloudflare dashboard.

For the origin certificate, generate one from Cloudflare’s dashboard:

  1. Go to SSL/TLS → Origin Server
  2. Create Certificate
  3. Save the certificate and key to your VPS

Step 6: Start the stack

docker-compose up -d

Check that everything is running:

docker-compose ps
docker-compose logs n8n

Navigate to https://automation.yourdomain.com and you should see the n8n login page.

Step 6: Backup strategy

Set up automated backups with a cron job:

#!/bin/bash
# backup.sh

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

mkdir -p $BACKUP_DIR

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

# Backup n8n data
tar czf $BACKUP_DIR/n8n_data_$DATE.tar.gz -C ./n8n_data .

# Keep only last 7 days
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete

# Upload to S3-compatible storage (optional)
# aws s3 sync $BACKUP_DIR s3://your-bucket/n8n-backups/

Add to crontab:

0 2 * * * /path/to/backup.sh

Monitoring

Health check endpoint

n8n provides a health check endpoint at /healthz. Use it with a monitoring service:

curl https://automation.yourdomain.com/healthz
# Returns: {"status":"ok"}

Uptime monitoring

I use Uptime Kuma (also self-hosted) to monitor:

  • n8n health endpoint
  • PostgreSQL connectivity
  • Redis connectivity
  • Disk space on the VPS

Log monitoring

# View n8n logs
docker-compose logs -f n8n

# View PostgreSQL logs
docker-compose logs -f postgres

Security checklist

  • Firewall configured (ufw) — only allow 80, 443, and SSH
  • SSH key authentication enabled, password auth disabled
  • Fail2ban installed for SSH protection
  • n8n basic auth enabled
  • Strong encryption key set
  • SSL configured via Cloudflare
  • Regular backups configured
  • n8n updated to latest version
  • Docker images updated regularly

Updating n8n

docker-compose pull
docker-compose up -d

This pulls the latest n8n image and restarts the container. The database and data volumes are preserved.

Conclusion

Self-hosting n8n gives you unlimited automation power at a fraction of the cost of cloud alternatives. With Docker, Nginx, and Cloudflare, you can have a production-ready setup in under an hour. The key is to invest in proper security, backups, and monitoring from the start.

#n8n #Cloudflare #Docker #Deployment

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.