DEV Community

Cover image for Multi-Instance n8n Self-Hosting Guide
Bipin C
Bipin C

Posted on

Multi-Instance n8n Self-Hosting Guide

Docker, PostgreSQL, and Cloudflare Zero Trust Tunnels

Version: 2.0

Last Updated: January 2026

Target Audience: Development Team

This guide provides production-grade configurations for running multiple isolated n8n instances (Production and Development) on a remote Ubuntu server using Docker, with secure external access through Cloudflare Zero Trust tunnels. Each instance has its own database, encryption key, and subdomain.


Prerequisites: Remote Server Access

This guide assumes you have:

  • ✅ A remote Ubuntu server with Docker installed
  • ✅ SSH access to the server via Cloudflare Tunnel
  • ✅ SSH key authentication configured
  • ✅ A domain managed by Cloudflare

To connect to your server:

ssh myserver
Enter fullscreen mode Exit fullscreen mode

Note: All commands in the "SERVER" sections should be run after SSHing into your server. Commands marked "CLIENT" are run on your local machine.


Setup your SSH server access

Follow this guide to setup cloudflare tunnel SSH connection to your own server (Prerequisite) -> Guide


Table of Contents

  1. Architecture Overview
  2. Tunnel Strategy
  3. Ubuntu Desktop Server Preparation
  4. Create n8n Tunnel in Cloudflare
  5. Directory and File Setup
  6. Environment Variables Configuration
  7. Docker Compose Configuration
  8. Configure Public Hostnames
  9. Securing the Development Instance
  10. Deployment
  11. First-Time n8n Setup
  12. Verification and Testing
  13. Backup Procedures
  14. Troubleshooting
  15. Quick Reference

1. Architecture Overview

System Architecture

Key Design Principles

Principle Implementation
Data Isolation Separate PostgreSQL databases per n8n instance
Credential Isolation Unique encryption keys per instance
Network Segmentation Internal backend networks for databases
Zero Trust Access Cloudflare Access protects dev instance
No Port Exposure All access through Cloudflare Tunnels
Separate Tunnels SSH tunnel independent from n8n tunnel

2. Tunnel Strategy

You already have a Cloudflare Tunnel running for SSH access. For n8n, we'll create a separate tunnel because:

SSH Tunnel n8n Tunnel
Uses network_mode: host Uses Docker network
Routes to localhost:22 Routes to container names (n8n-prod:5678)
Runs standalone Runs in docker-compose with n8n
Already configured ✓ Will configure now

Your tunnels after setup:

Tunnel Purpose Hostnames
Existing (SSH) Remote server access ssh.yourdomain.com
New (n8n) n8n web interfaces n8n-prod.yourdomain.com, n8n-dev.yourdomain.com

3. Ubuntu Desktop Server Preparation

Run these commands on SERVER (via SSH)

ssh myserver
Enter fullscreen mode Exit fullscreen mode

3.1 Disable Sleep/Suspend (If Ubuntu Desktop)

Skip this section if running Ubuntu Server (headless).

# Mask sleep targets
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

# Configure logind
sudo nano /etc/systemd/logind.conf
Enter fullscreen mode Exit fullscreen mode

Add:

[Login]
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore
HandlePowerKey=ignore
HandleSuspendKey=ignore
IdleAction=ignore
Enter fullscreen mode Exit fullscreen mode

Apply:

sudo systemctl restart systemd-logind.service
Enter fullscreen mode Exit fullscreen mode

For GNOME desktop:

gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-type 'nothing'
gsettings set org.gnome.settings-daemon.plugins.power sleep-inactive-ac-timeout 0
gsettings set org.gnome.desktop.session idle-delay 0
Enter fullscreen mode Exit fullscreen mode

3.2 Verify Docker Installation

docker --version
docker compose version
Enter fullscreen mode Exit fullscreen mode

If Docker is not installed, see Appendix A: Docker Installation.


4. Create n8n Tunnel in Cloudflare

Do this in your web browser (on CLIENT)

  1. Open Cloudflare Zero Trust Dashboard
  2. Go to Networks → Tunnels
  3. Click Create a tunnel
  4. Select Cloudflared as connector type
  5. Name it: n8n-stack (or similar)
  6. Select Docker as environment
  7. Copy the token from the command shown (starts with eyJ...)

Save this token - you'll add it to your .env file in the next section.

Don't configure public hostnames yet - we'll do that after deployment.


5. Directory and File Setup

Run these commands on SERVER (via SSH)

ssh myserver
Enter fullscreen mode Exit fullscreen mode

5.1 Create Project Directory

mkdir -p ~/n8n-stack
cd ~/n8n-stack
Enter fullscreen mode Exit fullscreen mode

5.2 Create .gitignore

cat > .gitignore << 'EOF'
.env
*.backup
*.dump
*.sql
*.log
EOF
Enter fullscreen mode Exit fullscreen mode

5.3 Directory Structure

After setup, your directory will look like:

~/n8n-stack/
├── docker-compose.yml    # Service definitions
├── .env                  # Secrets (chmod 600)
├── .gitignore            # Excludes .env from git
└── backup.sh             # Backup script (optional)
Enter fullscreen mode Exit fullscreen mode

6. Environment Variables Configuration

Run these commands on SERVER (via SSH)

6.1 Generate Secure Values

cd ~/n8n-stack

# Generate encryption keys (save these!)
echo "Production Encryption Key: $(openssl rand -hex 32)"
echo "Development Encryption Key: $(openssl rand -hex 32)"

# Generate database passwords
echo "Production DB Password: $(openssl rand -base64 24)"
echo "Development DB Password: $(openssl rand -base64 24)"
Enter fullscreen mode Exit fullscreen mode

Copy these values - you'll need them for the .env file.

6.2 Create Environment File

nano .env
Enter fullscreen mode Exit fullscreen mode

Add the following (replace placeholders with your values):

# ============================================
# PRODUCTION DATABASE
# ============================================
POSTGRES_PROD_USER=n8n_prod
POSTGRES_PROD_PASSWORD=YOUR_GENERATED_PROD_DB_PASSWORD
POSTGRES_PROD_DB=n8n_production

# ============================================
# DEVELOPMENT DATABASE
# ============================================
POSTGRES_DEV_USER=n8n_dev
POSTGRES_DEV_PASSWORD=YOUR_GENERATED_DEV_DB_PASSWORD
POSTGRES_DEV_DB=n8n_development

# ============================================
# n8n PRODUCTION
# ============================================
N8N_PROD_HOST=n8n-prod.yourdomain.com
N8N_PROD_ENCRYPTION_KEY=YOUR_GENERATED_PROD_ENCRYPTION_KEY

# ============================================
# n8n DEVELOPMENT
# ============================================
N8N_DEV_HOST=n8n-dev.yourdomain.com
N8N_DEV_ENCRYPTION_KEY=YOUR_GENERATED_DEV_ENCRYPTION_KEY

# ============================================
# SHARED CONFIGURATION
# ============================================
TIMEZONE=Asia/Kolkata

# ============================================
# CLOUDFLARE TUNNEL (n8n tunnel, NOT SSH tunnel)
# ============================================
TUNNEL_TOKEN=YOUR_N8N_TUNNEL_TOKEN_FROM_STEP_4
Enter fullscreen mode Exit fullscreen mode

Replace:

  • yourdomain.com → Your actual domain
  • YOUR_GENERATED_* → Values from step 6.1
  • YOUR_N8N_TUNNEL_TOKEN_FROM_STEP_4 → Token from Cloudflare dashboard
  • TIMEZONE → Your timezone (e.g., America/New_York, Europe/London)

6.3 Secure the File

chmod 600 .env
ls -la .env
# Should show: -rw------- (only owner can read/write)
Enter fullscreen mode Exit fullscreen mode

6.4 Critical Warnings

⚠️ ENCRYPTION KEY WARNINGS

  1. Set keys BEFORE first launch - they're stored in the database on first run
  2. NEVER change keys after credentials are saved - you'll lose all stored credentials permanently
  3. Back up your .env file securely - without encryption keys, credential recovery is impossible
  4. Use DIFFERENT keys for prod and dev - prevents cross-instance issues

7. Docker Compose Configuration

Run on SERVER (via SSH)

cd ~/n8n-stack
nano docker-compose.yml
Enter fullscreen mode Exit fullscreen mode

Paste the complete configuration:

services:
  # ============================================
  # PRODUCTION INSTANCE
  # ============================================
  postgres-prod:
    image: postgres:16-alpine
    container_name: n8n-postgres-prod
    restart: unless-stopped
    environment:
      - POSTGRES_USER=${POSTGRES_PROD_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PROD_PASSWORD}
      - POSTGRES_DB=${POSTGRES_PROD_DB}
    volumes:
      - postgres_prod_data:/var/lib/postgresql/data
    networks:
      - backend-prod
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_PROD_USER} -d ${POSTGRES_PROD_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  n8n-prod:
    image: docker.n8n.io/n8nio/n8n:latest
    container_name: n8n-prod
    restart: unless-stopped
    environment:
      # Database Configuration
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres-prod
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=${POSTGRES_PROD_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_PROD_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_PROD_PASSWORD}
      # n8n Configuration
      - N8N_HOST=${N8N_PROD_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://${N8N_PROD_HOST}/
      - N8N_ENCRYPTION_KEY=${N8N_PROD_ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=${TIMEZONE}
      # Performance Settings
      - NODE_OPTIONS=--max-old-space-size=2048
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=336
      - EXECUTIONS_DATA_PRUNE_MAX_COUNT=50000
      # Monitoring
      - N8N_METRICS=true
    volumes:
      - n8n_prod_data:/home/node/.n8n
    networks:
      - frontend
      - backend-prod
    depends_on:
      postgres-prod:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:5678/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  # ============================================
  # DEVELOPMENT INSTANCE
  # ============================================
  postgres-dev:
    image: postgres:16-alpine
    container_name: n8n-postgres-dev
    restart: unless-stopped
    environment:
      - POSTGRES_USER=${POSTGRES_DEV_USER}
      - POSTGRES_PASSWORD=${POSTGRES_DEV_PASSWORD}
      - POSTGRES_DB=${POSTGRES_DEV_DB}
    volumes:
      - postgres_dev_data:/var/lib/postgresql/data
    networks:
      - backend-dev
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_DEV_USER} -d ${POSTGRES_DEV_DB}"]
      interval: 10s
      timeout: 5s
      retries: 5
      start_period: 30s

  n8n-dev:
    image: docker.n8n.io/n8nio/n8n:latest
    container_name: n8n-dev
    restart: unless-stopped
    environment:
      # Database Configuration
      - DB_TYPE=postgresdb
      - DB_POSTGRESDB_HOST=postgres-dev
      - DB_POSTGRESDB_PORT=5432
      - DB_POSTGRESDB_DATABASE=${POSTGRES_DEV_DB}
      - DB_POSTGRESDB_USER=${POSTGRES_DEV_USER}
      - DB_POSTGRESDB_PASSWORD=${POSTGRES_DEV_PASSWORD}
      # n8n Configuration
      - N8N_HOST=${N8N_DEV_HOST}
      - N8N_PORT=5678
      - N8N_PROTOCOL=https
      - WEBHOOK_URL=https://${N8N_DEV_HOST}/
      - N8N_ENCRYPTION_KEY=${N8N_DEV_ENCRYPTION_KEY}
      - GENERIC_TIMEZONE=${TIMEZONE}
      # Performance Settings (lower limits for dev)
      - NODE_OPTIONS=--max-old-space-size=1024
      - N8N_DEFAULT_BINARY_DATA_MODE=filesystem
      - EXECUTIONS_DATA_PRUNE=true
      - EXECUTIONS_DATA_MAX_AGE=72
      - EXECUTIONS_DATA_PRUNE_MAX_COUNT=5000
      # Monitoring
      - N8N_METRICS=true
    volumes:
      - n8n_dev_data:/home/node/.n8n
    networks:
      - frontend
      - backend-dev
    depends_on:
      postgres-dev:
        condition: service_healthy
    healthcheck:
      test: ["CMD", "wget", "--spider", "-q", "http://localhost:5678/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 60s

  # ============================================
  # CLOUDFLARE TUNNEL (for n8n access)
  # ============================================
  cloudflared:
    image: cloudflare/cloudflared:latest
    container_name: n8n-cloudflared
    restart: unless-stopped
    command: tunnel --no-autoupdate run --token ${TUNNEL_TOKEN}
    networks:
      - frontend
    depends_on:
      - n8n-prod
      - n8n-dev

# ============================================
# NETWORKS
# ============================================
networks:
  frontend:
    driver: bridge
  backend-prod:
    driver: bridge
    internal: true
  backend-dev:
    driver: bridge
    internal: true

# ============================================
# VOLUMES
# ============================================
volumes:
  postgres_prod_data:
  postgres_dev_data:
  n8n_prod_data:
  n8n_dev_data:
Enter fullscreen mode Exit fullscreen mode

Save and exit (Ctrl+O, Enter, Ctrl+X).

7.1 Configuration Differences: Prod vs Dev

Setting Production Development Reason
max-old-space-size 2048 MB 1024 MB Dev needs less memory
EXECUTIONS_DATA_MAX_AGE 336 hrs (14 days) 72 hrs (3 days) Less retention for dev
EXECUTIONS_DATA_PRUNE_MAX_COUNT 50,000 5,000 Lower limits for dev

8. Configure Public Hostnames

Do this in your web browser (on CLIENT)

After the containers are running, configure the tunnel routing:

  1. Go to Cloudflare Zero Trust Dashboard
  2. Navigate to Networks → Tunnels
  3. Click on your n8n-stack tunnel
  4. Go to Public Hostname tab
  5. Click Add a public hostname

Production Hostname

Field Value
Subdomain n8n-prod
Domain Select your domain
Path (leave empty)
Type HTTP
URL n8n-prod:5678

Click Save hostname.

Development Hostname

Click Add a public hostname again:

Field Value
Subdomain n8n-dev
Domain Select your domain
Path (leave empty)
Type HTTP
URL n8n-dev:5678

Click Save hostname.

Understanding Service URLs

URL Correct? Explanation
n8n-prod:5678 Docker DNS resolves container names
localhost:5678 Points to cloudflared container itself
192.168.x.x:5678 ⚠️ Works but IP may change

9. Securing the Development Instance

Do this in your web browser (on CLIENT)

Protect the dev instance with Cloudflare Access so only you can access it.

9.1 Create Access Application

  1. Go to Access → Applications
  2. Click Add an application
  3. Select Self-hosted
  4. Configure:
Field Value
Application name n8n Development
Session duration 24 hours
Application domain n8n-dev.yourdomain.com
Path (leave empty)
  1. Click Next

9.2 Create Access Policy

Field Value
Policy name Dev Access
Action Allow

Include rule:

  • Selector: Emails
  • Value: your.email@example.com

Click Next, then Add application.

9.3 Configure Webhook Bypass

Webhooks need to receive external requests without authentication. Since Cloudflare Access doesn't support path-based rules within a policy, you must create a separate application for webhooks.

Create a Separate Webhook Application

  1. Go to Access → Applications
  2. Click Add an application
  3. Select Self-hosted
  4. Configure:
Field Value
Application name n8n Dev Webhooks
Session duration 24 hours
Application domain n8n-dev.yourdomain.com
Path webhook/*

Note: The Path field appears below the domain field when creating the application.

  1. Click Next

Add Bypass Policy

Field Value
Policy name Bypass All
Action Bypass

Include rule:

  • Selector: Everyone
  • Value: Everyone
  1. Click NextAdd application

How It Works

You now have two applications for dev:

Application Domain/Path Policy
n8n Development n8n-dev.yourdomain.com Allow (your email)
n8n Dev Webhooks n8n-dev.yourdomain.com/webhook/* Bypass (Everyone)

Cloudflare evaluates the most specific path first, so:

  • Requests to /webhook/* → Bypass (no auth required)
  • All other requests → Require authentication

Note for Production: If your production instance (n8n-prod.yourdomain.com) is public without Access protection, webhooks work automatically — no bypass application needed.


10. Deployment

Run these commands on SERVER (via SSH)

ssh myserver
cd ~/n8n-stack
Enter fullscreen mode Exit fullscreen mode

10.1 Validate Configuration

# Check .env file permissions
ls -la .env

# Validate docker-compose syntax
docker compose config --quiet && echo "✓ Configuration valid"
Enter fullscreen mode Exit fullscreen mode

10.2 Start Services

docker compose up -d
Enter fullscreen mode Exit fullscreen mode

10.3 Monitor Startup

# Watch all logs (Ctrl+C to exit)
docker compose logs -f

# Or watch specific service
docker compose logs -f n8n-prod
Enter fullscreen mode Exit fullscreen mode

10.4 Verify All Containers Running

docker compose ps
Enter fullscreen mode Exit fullscreen mode

Expected output:

NAME               STATUS                   PORTS
n8n-cloudflared    Up X minutes
n8n-dev            Up X minutes (healthy)
n8n-postgres-dev   Up X minutes (healthy)
n8n-postgres-prod  Up X minutes (healthy)
n8n-prod           Up X minutes (healthy)
Enter fullscreen mode Exit fullscreen mode

Wait until all show (healthy) status (1-2 minutes).


11. First-Time n8n Setup

Do this in your web browser (on CLIENT)

11.1 Production Instance

  1. Open https://n8n-prod.yourdomain.com
  2. Create your owner account:
    • Email
    • First name, Last name
    • Strong password
  3. Complete the setup wizard

11.2 Development Instance

  1. Open https://n8n-dev.yourdomain.com
  2. Authenticate with Cloudflare Access (if configured)
  3. Create a separate owner account

⚠️ Security: The first user to access each instance becomes the owner. Complete this immediately after deployment.


12. Verification and Testing

Run on SERVER (via SSH) unless noted

12.1 Test Container Health

# Test n8n health endpoints
docker exec n8n-prod wget -qO- http://localhost:5678/healthz
docker exec n8n-dev wget -qO- http://localhost:5678/healthz
Enter fullscreen mode Exit fullscreen mode

12.2 Test Tunnel Connectivity

# Check cloudflared can reach n8n containers
docker exec n8n-cloudflared wget -qO- --timeout=5 http://n8n-prod:5678/healthz
docker exec n8n-cloudflared wget -qO- --timeout=5 http://n8n-dev:5678/healthz
Enter fullscreen mode Exit fullscreen mode

12.3 Test Database Connectivity

# Check PostgreSQL
docker exec n8n-postgres-prod pg_isready -U n8n_prod -d n8n_production
docker exec n8n-postgres-dev pg_isready -U n8n_dev -d n8n_development
Enter fullscreen mode Exit fullscreen mode

12.4 Test Webhooks

Do this from CLIENT

  1. In n8n-prod, create a workflow with a Webhook node
  2. Set method to POST
  3. Copy the Production URL
  4. Activate the workflow
  5. Test from your client terminal:
curl -X POST https://n8n-prod.yourdomain.com/webhook-test/your-webhook-path \
  -H "Content-Type: application/json" \
  -d '{"test": "Hello from webhook!"}'
Enter fullscreen mode Exit fullscreen mode

13. Backup Procedures

Run on SERVER (via SSH)

13.1 Manual Backup

cd ~/n8n-stack

# Production database
docker exec n8n-postgres-prod pg_dump -U n8n_prod -d n8n_production | gzip > backup_prod_$(date +%Y%m%d).sql.gz

# Development database
docker exec n8n-postgres-dev pg_dump -U n8n_dev -d n8n_development | gzip > backup_dev_$(date +%Y%m%d).sql.gz

# Environment file (CRITICAL - contains encryption keys)
cp .env .env.backup.$(date +%Y%m%d)
Enter fullscreen mode Exit fullscreen mode

13.2 Automated Backup Script

cat > backup.sh << 'EOF'
#!/bin/bash
set -e

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

mkdir -p "$BACKUP_DIR"

echo "[$(date)] Starting backup..."

# Production
docker exec n8n-postgres-prod pg_dump -U n8n_prod -F c -d n8n_production > "$BACKUP_DIR/prod_$DATE.dump"

# Development  
docker exec n8n-postgres-dev pg_dump -U n8n_dev -F c -d n8n_development > "$BACKUP_DIR/dev_$DATE.dump"

# Environment file
cp ~/n8n-stack/.env "$BACKUP_DIR/env_$DATE.backup"

# Cleanup old backups
find "$BACKUP_DIR" -name "*.dump" -mtime +$RETENTION_DAYS -delete
find "$BACKUP_DIR" -name "*.backup" -mtime +$RETENTION_DAYS -delete

echo "[$(date)] Backup complete!"
ls -lh "$BACKUP_DIR" | tail -5
EOF

chmod +x backup.sh
Enter fullscreen mode Exit fullscreen mode

13.3 Schedule Daily Backups

crontab -e
Enter fullscreen mode Exit fullscreen mode

Add:

0 2 * * * /home/$USER/n8n-stack/backup.sh >> /home/$USER/n8n-stack/backup.log 2>&1
Enter fullscreen mode Exit fullscreen mode

13.4 Restore from Backup

# Stop n8n first
docker compose stop n8n-prod

# Restore production database
gunzip -c backup_prod_20260122.sql.gz | docker exec -i n8n-postgres-prod psql -U n8n_prod -d n8n_production

# Start n8n
docker compose start n8n-prod
Enter fullscreen mode Exit fullscreen mode

14. Troubleshooting

Common Issues

Issue Cause Solution
502 Bad Gateway Container not healthy docker compose logs n8n-prod - wait for healthy
Webhooks show localhost WEBHOOK_URL wrong Check .env has correct public URL
"Connection refused" Database not ready Wait for postgres to be healthy
Can't access dev instance Not in Access policy Add your email to Cloudflare Access
Tunnel not connecting Wrong token Verify TUNNEL_TOKEN in .env

Useful Commands

# Check all container status
docker compose ps

# View logs
docker compose logs -f cloudflared
docker compose logs -f n8n-prod

# Restart a service
docker compose restart n8n-prod

# Rebuild everything
docker compose down
docker compose up -d

# Check resource usage
docker stats

# Access container shell
docker exec -it n8n-prod /bin/sh
Enter fullscreen mode Exit fullscreen mode

Network Debugging

# Test DNS resolution inside cloudflared
docker exec n8n-cloudflared nslookup n8n-prod
docker exec n8n-cloudflared nslookup n8n-dev

# Check network configuration
docker network ls
docker network inspect n8n-stack_frontend
Enter fullscreen mode Exit fullscreen mode

15. Quick Reference

Your Endpoints

Service URL
SSH Access ssh myserver
n8n Production https://n8n-prod.yourdomain.com
n8n Development https://n8n-dev.yourdomain.com

Daily Operations (from CLIENT)

# Connect to server
ssh myserver

# Check status
cd ~/n8n-stack && docker compose ps

# View logs
docker compose logs -f

# Restart services
docker compose restart

# Update n8n
docker compose pull
docker compose up -d
Enter fullscreen mode Exit fullscreen mode

File Locations (on SERVER)

File Path
Docker Compose ~/n8n-stack/docker-compose.yml
Environment ~/n8n-stack/.env
Backups ~/n8n-backups/
SSH Tunnel ~/cloudflare-tunnel/

Important Reminders

  • Backup encryption keys - Store .env securely offline
  • Don't change encryption keys after first run
  • Create owner accounts immediately after deployment
  • Test webhooks before using in production
  • Keep SSH tunnel separate from n8n tunnel

Appendix A: Docker Installation

If Docker is not installed on your server:

# Remove old versions
for pkg in docker.io docker-compose docker-compose-v2 podman-docker containerd runc; do
  sudo apt-get remove -y $pkg 2>/dev/null
done

# Add Docker repository
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

sudo apt-get update

# Install Docker
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

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

# Enable on boot
sudo systemctl enable docker.service
sudo systemctl enable containerd.service

# Verify
docker run hello-world
Enter fullscreen mode Exit fullscreen mode

Top comments (0)