commit 8ff4b8c1798993f933354f8cf1eea9455437226a Author: AiratTop Date: Sun Oct 5 14:41:49 2025 +0300 First commit diff --git a/.env b/.env new file mode 100644 index 0000000..3a0df2f --- /dev/null +++ b/.env @@ -0,0 +1,33 @@ +# The top level domain to serve from +DOMAIN_NAME=example.com + +# The subdomain to serve from +SUBDOMAIN=n8n + +# DOMAIN_NAME and SUBDOMAIN combined decide where n8n will be reachable from +# above example would result in: https://n8n.example.com + +# Optional timezone to set which gets used by Cron-Node by default +# If not set New York time will be used +GENERIC_TIMEZONE=Europe/Moscow + +# Secret key to encrypt credentials (generate a new one for production) +N8N_KEY=4uguWxkSXX8g6biqCcof7VqPNW225v + +# The version of n8n to use +N8N_VERSION=latest + +# The email address to use for the SSL certificate creation +SSL_EMAIL=user@example.com + +# Redis password (generate a new one for production) +REDIS_PWD=nmhKmoREm87FpAmodaVQzE8Gorco64 + +# PostgreSQL password (generate a new one for production) +PSQL_PWD=vwbQ8pSWqM34tU5MC7Qadk4W6Uq4eR + +# SMTP settings for sending emails from n8n (e.g., for password recovery) +SMTP_HOST=smtp.example.com +SMTP_PORT=465 +SMTP_USER=user@example.com +SMTP_PWD=replace-with-your-smtp-password diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6591d5f --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +*/.DS_Store +data/ \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..efd8ef6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Airat Halitov + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..ca33cdb --- /dev/null +++ b/README.md @@ -0,0 +1,83 @@ +# n8n Self-Hosted with Docker + +This repository provides a Docker Compose setup for running a self-hosted n8n instance with scaling capabilities. + +## Features + +- **Works out of the box:** Just configure the `.env` file and run the start script. +- **Scalable:** Runs n8n in `queue` mode with separate workers, ready for high loads. +- **Handles Large Files:** Configured to save binary data to the filesystem, allowing workflows to process large files without memory issues. +- **Ready-to-use Scripts:** Includes simple scripts to start, restart, and update the application. +- **Long-term History:** Execution history is stored for 2 months. +- **Secure:** Uses the latest recommended security environment variables for n8n. +- **Private:** n8n telemetry is disabled by default. +- **Full-featured:** SMTP is pre-configured for password recovery and other email functions. +- **Automatic HTTPS:** Uses Caddy as a reverse proxy for automatic SSL certificates. +- **Persistent Data:** Data is stored in a PostgreSQL database. +- **Caching:** Uses Redis for caching. + +## Prerequisites + +- Docker and Docker Compose installed. +- A domain name pointed to your server's IP address. +- A `.env` file with the required environment variables. + +## Usage + +1. **Clone the repository:** + ```bash + git clone https://github.com/AiratTop/n8n-self-hosted + cd n8n-self-hosted + ``` + +2. **Configure the environment:** + Open the `.env` file and update the variables with your own settings. + +3. **Start the application:** + ```bash + ./start-docker.sh + ``` + This will start all the services in detached mode and scale the `n8n-worker` service to 2 replicas. + +4. **Restart the application:** + ```bash + ./restart-docker.sh + ``` + +5. **Update the application:** + ```bash + ./update-docker.sh + ``` + This will automatically create a database backup, pull the latest Docker images, and restart the application. + +## Backup + +This project includes a script to back up the n8n database. + +To create a backup, run: +```bash +./backup.sh +``` +This will create a compressed SQL dump of your PostgreSQL database in the `local_files/backups/` directory. It is recommended to run this script regularly (e.g., using a cron job). + +## Services + +- `caddy`: Reverse proxy +- `n8n-master`: Main n8n instance +- `n8n-worker`: n8n worker instances +- `n8n-psql`: PostgreSQL database +- `n8n-redis`: Redis cache + +## License + +This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details. + +--- + +## Author + +**Airat Halitov** + +- Website: [airat.top](https://airat.top) +- GitHub: [@AiratTop](https://github.com/AiratTop) +- Repository: [n8n-self-hosted](https://github.com/AiratTop/n8n-self-hosted) \ No newline at end of file diff --git a/backup.sh b/backup.sh new file mode 100755 index 0000000..279bb71 --- /dev/null +++ b/backup.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +date + +# Exit immediately if a command exits with a non-zero status. +set -e + +BACKUP_DIR="local_files/backups" +mkdir -p "${BACKUP_DIR}" + +# Dump the PostgreSQL database and compress it. +docker-compose exec -T n8n-psql pg_dump -U n8n -d n8n_db | gzip > "${BACKUP_DIR}/psql_n8ndb_$(date +%F_%H-%M-%S).sql.gz" diff --git a/caddy_config/Caddyfile b/caddy_config/Caddyfile new file mode 100644 index 0000000..ae9008a --- /dev/null +++ b/caddy_config/Caddyfile @@ -0,0 +1,13 @@ + +# Main n8n service. + +${SUBDOMAIN}.${DOMAIN_NAME} { + # Prevents search engines from indexing this site. + header X-Robots-Tag "noindex, nofollow" + + # Forwards all traffic to the n8n backend service running on port 5678. + reverse_proxy n8n-master:5678 { + # Disables response buffering, which is crucial for Server-Sent Events (SSE) used by n8n. + flush_interval -1 + } +} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..88591f1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,151 @@ +x-n8n-common-env: &n8n-common-env + - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME} + - N8N_PORT=5678 + - N8N_PROTOCOL=https + - NODE_ENV=production + - WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/ + # Sets the timezone for cron nodes, etc. + - GENERIC_TIMEZONE=${GENERIC_TIMEZONE} + # Secret key to encrypt credentials. + - N8N_ENCRYPTION_KEY=${N8N_KEY} + # --- Database --- + - DB_TYPE=postgresdb + - DB_POSTGRESDB_DATABASE=n8n_db + - DB_POSTGRESDB_HOST=n8n-psql + - DB_POSTGRESDB_PORT=5432 + - DB_POSTGRESDB_USER=n8n + - DB_POSTGRESDB_SCHEMA=public + - DB_POSTGRESDB_PASSWORD=${PSQL_PWD} + # --- Workers & Execution Pruning --- + # 'queue' mode is required for scaling with separate workers. + - EXECUTIONS_MODE=queue + - QUEUE_BULL_REDIS_HOST=n8n-redis + - QUEUE_BULL_REDIS_PASSWORD=${REDIS_PWD} + # Max age of executions to keep, in hours: 24h * 30d = 720, 14d = 336, 21d = 504, 60d = 1440 + - EXECUTIONS_DATA_MAX_AGE=1440 + - EXECUTIONS_DATA_PRUNE_MAX_COUNT=1000000 + - EXECUTIONS_DATA_PRUNE=true + # --- Security & Customization --- + - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true + - N8N_GIT_NODE_DISABLE_BARE_REPOS=true + - N8N_BLOCK_ENV_ACCESS_IN_NODE=true + # Performance tuning. + - N8N_RUNNERS_ENABLED=true + - N8N_RUNNERS_MAX_CONCURRENCY=10 + - OFFLOAD_MANUAL_EXECUTIONS_TO_WORKERS=true + # --- SMTP --- + - N8N_EMAIL_MODE=smtp + - N8N_SMTP_HOST=${SMTP_HOST} + - N8N_SMTP_PORT=${SMTP_PORT} + - N8N_SMTP_USER=${SMTP_USER} + - N8N_SMTP_PASS=${SMTP_PWD} + - N8N_SMTP_SENDER=${SMTP_USER} + - N8N_SMTP_SSL=true + # Save binary files to filesystem instead of memory + - N8N_DEFAULT_BINARY_DATA_MODE=filesystem + # Disable modules and spam + - N8N_DISABLED_MODULES=insights + # - N8N_TEMPLATES_ENABLED=false + - N8N_HIRING_BANNER_ENABLED=false + # - N8N_VERSION_NOTIFICATIONS_ENABLED=false + # Disable n8n telemetry events (comment to enable) + - N8N_DIAGNOSTICS_ENABLED=false + - EXTERNAL_FRONTEND_HOOKS_URLS= + - N8N_DIAGNOSTICS_CONFIG_FRONTEND= + - N8N_DIAGNOSTICS_CONFIG_BACKEND= + # Enable n8n /metrics, /healthz and /healthz/readiness endpoints + - N8N_METRICS=true + - QUEUE_HEALTH_CHECK_ACTIVE=true + +services: + caddy: + container_name: n8n-caddy + hostname: n8n-caddy + image: caddy:latest + environment: + - SUBDOMAIN=${SUBDOMAIN} + - DOMAIN_NAME=${DOMAIN_NAME} + networks: ['shared_network'] + restart: unless-stopped + ports: + - '80:80' + - '443:443' + volumes: + - ./data/caddy:/data + - ./caddy_config:/config + - ./caddy_config/Caddyfile:/etc/caddy/Caddyfile + depends_on: + n8n-master: + condition: service_started + n8n-redis: + container_name: n8n-redis + hostname: n8n-redis + image: redis:alpine + networks: ['shared_network'] + restart: always + healthcheck: + test: [ "CMD", "redis-cli", "--raw", "incr", "ping" ] + interval: 5s + timeout: 5s + retries: 5 + command: > + --requirepass ${REDIS_PWD} + --bind 0.0.0.0 + ports: + - '6379:6379' + volumes: + - ./data/n8n-redis:/data + n8n-psql: + container_name: n8n-psql + hostname: n8n-psql + image: postgres:17 + networks: ['shared_network'] + ports: + - '5432:5432' + restart: always + environment: + - POSTGRES_USER=n8n + - POSTGRES_PASSWORD=${PSQL_PWD} + - POSTGRES_DB=n8n_db + volumes: + - ./data/n8n-psql:/var/lib/postgresql/data + healthcheck: + test: ['CMD-SHELL', 'pg_isready -h localhost -U n8n -d n8n_db'] + interval: 5s + timeout: 5s + retries: 10 + n8n-master: + container_name: n8n-master + hostname: n8n-master + image: docker.n8n.io/n8nio/n8n:${N8N_VERSION:-latest} + networks: ['shared_network'] + restart: always + ports: + - '5678:5678' + environment: *n8n-common-env + volumes: + - ./data/n8n:/home/node/.n8n + - ./local_files:/files + depends_on: + n8n-psql: + condition: service_healthy + n8n-redis: + condition: service_healthy + n8n-worker: + image: docker.n8n.io/n8nio/n8n:${N8N_VERSION:-latest} + networks: ['shared_network'] + restart: always + environment: *n8n-common-env + command: worker --concurrency=10 + volumes: + - ./data/n8n:/home/node/.n8n + - ./local_files:/files + depends_on: + n8n-psql: + condition: service_healthy + n8n-redis: + condition: service_healthy +networks: + shared_network: + name: shared_network + driver: bridge \ No newline at end of file diff --git a/local_files/.gitignore b/local_files/.gitignore new file mode 100644 index 0000000..8fe74b3 --- /dev/null +++ b/local_files/.gitignore @@ -0,0 +1,4 @@ +* + +!.gitignore +/backups/ diff --git a/restart-docker.sh b/restart-docker.sh new file mode 100755 index 0000000..66460a3 --- /dev/null +++ b/restart-docker.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +# This script restarts the n8n application. +# It first stops the running containers and then starts them again using the start-docker.sh script. + +date + +docker compose stop +./start-docker.sh diff --git a/start-docker.sh b/start-docker.sh new file mode 100755 index 0000000..cec2ec1 --- /dev/null +++ b/start-docker.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +# This script starts the n8n application using Docker Compose. +# It runs the services in detached mode (-d) and scales the n8n-worker service. + +date + +# Start the container +# with 2 n8n workers +docker compose up -d --scale n8n-worker=2 \ No newline at end of file diff --git a/update-docker.sh b/update-docker.sh new file mode 100755 index 0000000..4bc2eef --- /dev/null +++ b/update-docker.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# This script updates the n8n application by creating a database backup, +# pulling the latest Docker images, and restarting the services. + +date + +# Create a database backup before updating. +echo "Creating database backup..." +./backup.sh + +# Pull latest version +echo "Pulling latest Docker images..." +docker compose pull + +# Stop and remove older version +echo "Stopping and removing old containers..." +docker compose down + +# Start the container +echo "Starting new containers..." +./start-docker.sh + +echo "Update complete!" \ No newline at end of file