Free Tutorial

Deploy Your Own n8n Server

A production-ready guide to deploying n8n workflow automation on AWS

1. Brief Overview

n8n is a powerful, open-source workflow automation tool that allows you to connect various applications and services to create complex, automated workflows with minimal code. Unlike proprietary services like Zapier or Integromat, n8n is self-hostable, giving you complete control over your data, workflows, and operational costs. This is particularly advantageous for businesses with strict data privacy requirements or those looking to scale their automation efforts without incurring exponentially rising subscription fees. Self-hosting n8n provides a secure, flexible, and cost-effective solution for automating business processes.

This tutorial provides a comprehensive, step-by-step guide to deploying a production-ready n8n instance on Amazon Web Services (AWS). We will focus on a robust and scalable setup using Docker Compose on an EC2 instance. This approach is chosen for its balance of power and simplicity, making it accessible even to those with moderate technical expertise. By the end of this guide, you will have a secure, scalable, and maintainable n8n server, complete with a custom domain, SSL encryption, and best practices for production environments. We will cover everything from setting up the AWS infrastructure to configuring n8n for optimal performance and security.

The tutorial is structured to be highly practical. We will start with the foundational concepts of n8n and its architecture, then dive into the hands-on process of launching an EC2 instance, installing Docker, and configuring our n8n application using a production-grade docker-compose.yml file. This setup will include separate containers for the main n8n application, a dedicated worker for executing workflows, a PostgreSQL database for persistent storage, and a Redis instance for efficient queue management. We will also integrate Caddy as a reverse proxy to automate SSL certificate issuance and renewal, ensuring your n8n instance is secure and accessible via a custom domain.

2. Key Concepts

3. Practical Code Examples

3.1. Setting up the AWS EC2 Instance

  1. Launch an EC2 Instance:
    • Log in to the AWS Management Console and navigate to the EC2 Dashboard.
    • Click "Launch Instance".
    • Name: n8n-server
    • Application and OS Images (AMI): Ubuntu 22.04 LTS
    • Instance Type: t3.medium (or larger for production workloads)
    • Key Pair: Create a new key pair and download the .pem file.
    • Network Settings:
      • Create a security group with the following inbound rules:
        • SSH (Port 22): From your IP
        • HTTP (Port 80): From Anywhere (0.0.0.0/0)
        • HTTPS (Port 443): From Anywhere (0.0.0.0/0)
    • Launch the instance.
  2. Connect to your EC2 instance via SSH:
    ssh -i /path/to/your-key.pem ubuntu@your-ec2-public-ip

3.2. Installing Docker and Docker Compose

# Update package lists
sudo apt update

# Install Docker
sudo apt install -y docker.io

# Start and enable Docker service
sudo systemctl start docker
sudo systemctl enable docker

# Install Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.5/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

3.3. Configuring n8n with Docker Compose

  1. Create a project directory:
    mkdir n8n-production
    cd n8n-production
  2. Create a .env file:
    nano .env
    Add the following content, replacing the placeholder values:
    # n8n basic config
    GENERIC_TIMEZONE=Europe/London
    N8N_BASIC_AUTH_ACTIVE=true
    N8N_BASIC_AUTH_USER=admin
    N8N_BASIC_AUTH_PASSWORD=your_strong_admin_password
    
    # Database configuration (PostgreSQL)
    DB_TYPE=postgresdb
    DB_POSTGRESDB_HOST=postgres
    DB_POSTGRESDB_PORT=5432
    DB_POSTGRESDB_DATABASE=n8n
    DB_POSTGRESDB_USER=n8n_user
    DB_POSTGRESDB_PASSWORD=your_strong_db_password
    
    # Redis configuration
    QUEUE_TYPE=redis
    QUEUE_REDIS_HOST=redis
    QUEUE_REDIS_PORT=6379
    
    # N8N URL configuration
    N8N_EDITOR_BASE_URL=https://n8n.yourdomain.com
    WEBHOOK_URL=https://n8n.yourdomain.com
    
    # Encryption key for credentials
    N8N_ENCRYPTION_KEY=your_very_strong_encryption_key_here_at_least_32_chars
    
    # Optional: Disable telemetry for privacy
    N8N_TELEMETRY_ENABLED=false
  3. Create the docker-compose.yml file:
    nano docker-compose.yml
    Add the following content:
    version: '3.8'
    
    services:
      postgres:
        image: postgres:15
        restart: always
        environment:
          POSTGRES_USER: ${DB_POSTGRESDB_USER}
          POSTGRES_PASSWORD: ${DB_POSTGRESDB_PASSWORD}
          POSTGRES_DB: ${DB_POSTGRESDB_DATABASE}
        volumes:
          - postgres_data:/var/lib/postgresql/data
        healthcheck:
          test: ["CMD-SHELL", "pg_isready -U ${DB_POSTGRESDB_USER} -d ${DB_POSTGRESDB_DATABASE}"]
          interval: 5s
          timeout: 5s
          retries: 5
    
      redis:
        image: redis:7-alpine
        restart: always
        volumes:
          - redis_data:/data
        healthcheck:
          test: ["CMD", "redis-cli", "ping"]
          interval: 5s
          timeout: 5s
          retries: 5
    
      n8n:
        image: n8nio/n8n:latest
        restart: always
        environment:
          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}
          QUEUE_TYPE: ${QUEUE_TYPE}
          QUEUE_REDIS_HOST: ${QUEUE_REDIS_HOST}
          QUEUE_REDIS_PORT: ${QUEUE_REDIS_PORT}
          N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE}
          N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER}
          N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD}
          GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
          N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL}
          WEBHOOK_URL: ${WEBHOOK_URL}
          N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
          N8N_TELEMETRY_ENABLED: ${N8N_TELEMETRY_ENABLED}
        volumes:
          - n8n_data:/home/node/.n8n
        depends_on:
          postgres:
            condition: service_healthy
          redis:
            condition: service_healthy
    
      n8n-worker:
        image: n8nio/n8n:latest
        restart: always
        command: n8n worker
        environment:
          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}
          QUEUE_TYPE: ${QUEUE_TYPE}
          QUEUE_REDIS_HOST: ${QUEUE_REDIS_HOST}
          QUEUE_REDIS_PORT: ${QUEUE_REDIS_PORT}
          GENERIC_TIMEZONE: ${GENERIC_TIMEZONE}
          N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY}
          N8N_TELEMETRY_ENABLED: ${N8N_TELEMETRY_ENABLED}
        volumes:
          - n8n_data:/home/node/.n8n
        depends_on:
          postgres:
            condition: service_healthy
          redis:
            condition: service_healthy
    
      caddy:
        image: caddy:2
        restart: always
        ports:
          - "80:80"
          - "443:443"
        volumes:
          - ./Caddyfile:/etc/caddy/Caddyfile
          - caddy_data:/data
        command: caddy run --config /etc/caddy/Caddyfile
    
    volumes:
      postgres_data:
      redis_data:
      n8n_data:
      caddy_data:
  4. Create the Caddyfile:
    nano Caddyfile
    Add the following, replacing n8n.yourdomain.com with your domain:
    n8n.yourdomain.com {
        reverse_proxy n8n:5678
    }

3.4. Launching n8n

sudo docker-compose up -d

Expected Output:

[+] Running 6/6
 ✔ Network n8n-production_default  Created
 ✔ Volume "n8n-production_postgres_data"  Created
 ✔ Volume "n8n-production_redis_data"  Created
 ✔ Volume "n8n-production_n8n_data"  Created
 ✔ Volume "n8n-production_caddy_data"  Created
 ✔ Container n8n-production-postgres-1      Started
 ✔ Container n8n-production-redis-1         Started
 ✔ Container n8n-production-n8n-1           Started
 ✔ Container n8n-production-n8n-worker-1    Started
 ✔ Container n8n-production-caddy-1         Started

4. Best Practices

5. Common Pitfalls to Avoid

6. Next Steps and Additional Resources

← Back to Tutorials