Давайте поговорим о нейросетях и автоматизациях
Летом 2025-го я впервые услышал про «Контент-завод».
Идея простая: автоматизация, которая «из ничего» штампует контент - текст, видео, аудио и т. д.
Меня это заинтересовало.
Знакомые из бизнеса говорили, что тема сейчас на хайпе.
Мимо пройти было нельзя :-)
Инструмент для экспериментов - n8n. Наткнулся на него случайно (кажется, в Reels).
Эта статья - мой опыт: что делал по шагам и к чему пришёл.

Железо и стек
Основной инструмент: n8n
Сервер: «Std C1-M2-D10 - 10 ГБ SSD, 2 ГБ RAM, 1 vCPU»
Архитектура: Docker
Стек:
Postgres
WireGuard
Traefik
Где покупал сервер и откуда VPN - не скажу, чтобы не выглядело как реклама.
С чего начал
С настройки сервера. День ушёл на то, чтобы всё завелось и весь Docker ходил наружу через VPN, но при этом был доступен по моему домену (для вебхуков и интеграций это критично). В итоге получилась такая конструкция:
Doker файл
networks: web: name: web x-logging: &rot driver: "local" options: max-size: "20m" max-file: "3" compress: "true" services: traefik: image: traefik:v2.11 container_name: traefik restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.network=web - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 # Let's Encrypt (HTTP-01) - --certificatesresolvers.le.acme.email=${ACME_EMAIL} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web # file-provider (если статические TLS/правила) - --providers.file.directory=/etc/traefik/dynamic - --providers.file.watch=true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik:/letsencrypt - ./traefik/dynamic:/etc/traefik/dynamic:ro networks: [web] logging: *rot postgres: image: postgres:16-alpine container_name: n8n-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} -h 127.0.0.1"] interval: 10s timeout: 5s retries: 10 networks: [web] logging: *rot # --- WireGuard VPN (клиент) --- wireguard: image: ghcr.io/linuxserver/wireguard:latest container_name: wireguard restart: unless-stopped cap_add: [NET_ADMIN, SYS_MODULE] sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv6.conf.all.disable_ipv6=0 environment: - TZ=Europe/Amsterdam - PEERDNS=false volumes: - ./wireguard:/config - /lib/modules:/lib/modules:ro devices: - /dev/net/tun:/dev/net/tun networks: [web] labels: - "traefik.enable=true" # HTTP -> HTTPS - "traefik.http.routers.n8n-http.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n-http.entrypoints=web" - "traefik.http.routers.n8n-http.middlewares=n8n-redirect@docker" - "traefik.http.middlewares.n8n-redirect.redirectscheme.scheme=https" # HTTPS роутер - "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls=true" # сервис на порт 5678 (n8n живёт в netns wireguard) - "traefik.http.services.n8n.loadbalancer.server.port=5678" logging: *rot n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped user: "1000:1000" network_mode: "service:wireguard" environment: # URL-ы и таймзона N8N_HOST: ${N8N_HOST} N8N_PORT: ${N8N_PORT} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} # ★ Продувка истории и меньше мусора EXECUTIONS_DATA_PRUNE: 'true' EXECUTIONS_DATA_MAX_AGE: '24' EXECUTIONS_DATA_PRUNE_MAX_COUNT: '0' EXECUTIONS_DATA_SAVE_ON_SUCCESS: 'none' EXECUTIONS_DATA_SAVE_ON_ERROR: 'all' EXECUTIONS_DATA_SAVE_ON_PROGRESS: 'false' # ★ Ограничители бесконечных циклов N8N_CONCURRENCY_PRODUCTION_LIMIT: '10' EXECUTIONS_TIMEOUT: '1800' N8N_LOG_LEVEL: 'warn' # ★ Бинарные данные - на файловую систему N8N_AVAILABLE_BINARY_DATA_MODES: 'filesystem' N8N_DEFAULT_BINARY_DATA_MODE: 'filesystem' N8N_BINARY_DATA_STORAGE_PATH: '/home/node/.n8n/binary_data' # БД DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} # Безопасность N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE} N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} # (опционально) раннеры/права/ворнинги N8N_RUNNERS_ENABLED: 'true' N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false' N8N_GIT_NODE_DISABLE_BARE_REPOS: 'true' # (опционально) телеметрия N8N_DIAGNOSTICS_ENABLED: "false" N8N_HIRING_BANNER_ENABLED: "false" # (рекомендуется) права на конфиг N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true" volumes: - ./n8n-data:/home/node/.n8n depends_on: postgres: condition: service_healthy wireguard: condition: service_started logging: *rot n8n-mcp: image: ghcr.io/czlonkowski/n8n-mcp:latest container_name: n8n-mcp restart: unless-stopped environment: MCP_MODE: http HOST: 0.0.0.0 LOG_LEVEL: ${MCP_LOG_LEVEL} AUTH_TOKEN: ${MCP_AUTH_TOKEN} networks: web: name: web x-logging: &rot driver: "local" options: max-size: "20m" max-file: "3" compress: "true" services: traefik: image: traefik:v2.11 container_name: traefik restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.network=web - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 # Let's Encrypt (HTTP-01) - --certificatesresolvers.le.acme.email=${ACME_EMAIL} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web # file-provider (если статические TLS/правила) - --providers.file.directory=/etc/traefik/dynamic - --providers.file.watch=true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik:/letsencrypt - ./traefik/dynamic:/etc/traefik/dynamic:ro networks: [web] logging: *rot postgres: image: postgres:16-alpine container_name: n8n-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} -h 127.0.0.1"] interval: 10s timeout: 5s retries: 10 networks: [web] logging: *rot # --- WireGuard VPN (клиент) --- wireguard: image: ghcr.io/linuxserver/wireguard:latest container_name: wireguard restart: unless-stopped cap_add: [NET_ADMIN, SYS_MODULE] sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv6.conf.all.disable_ipv6=0 environment: - TZ=Europe/Amsterdam - PEERDNS=false volumes: - ./wireguard:/config - /lib/modules:/lib/modules:ro devices: - /dev/net/tun:/dev/net/tun networks: [web] labels: - "traefik.enable=true" # HTTP -> HTTPS - "traefik.http.routers.n8n-http.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n-http.entrypoints=web" - "traefik.http.routers.n8n-http.middlewares=n8n-redirect@docker" - "traefik.http.middlewares.n8n-redirect.redirectscheme.scheme=https" # HTTPS роутер - "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls=true" # сервис на порт 5678 (n8n живёт в netns wireguard) - "traefik.http.services.n8n.loadbalancer.server.port=5678" logging: *rot n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped user: "1000:1000" network_mode: "service:wireguard" environment: # URL-ы и таймзона N8N_HOST: ${N8N_HOST} N8N_PORT: ${N8N_PORT} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} # ★ Продувка истории и меньше мусора EXECUTIONS_DATA_PRUNE: 'true' EXECUTIONS_DATA_MAX_AGE: '24' EXECUTIONS_DATA_PRUNE_MAX_COUNT: '0' EXECUTIONS_DATA_SAVE_ON_SUCCESS: 'none' EXECUTIONS_DATA_SAVE_ON_ERROR: 'all' EXECUTIONS_DATA_SAVE_ON_PROGRESS: 'false' # ★ Ограничители бесконечных циклов N8N_CONCURRENCY_PRODUCTION_LIMIT: '10' EXECUTIONS_TIMEOUT: '1800' N8N_LOG_LEVEL: 'warn' # ★ Бинарные данные - на файловую систему N8N_AVAILABLE_BINARY_DATA_MODES: 'filesystem' N8N_DEFAULT_BINARY_DATA_MODE: 'filesystem' N8N_BINARY_DATA_STORAGE_PATH: '/home/node/.n8n/binary_data' # БД DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} # Безопасность N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE} N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} # (опционально) раннеры/права/ворнинги N8N_RUNNERS_ENABLED: 'true' N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false' N8N_GIT_NODE_DISABLE_BARE_REPOS: 'true' # (опционально) телеметрия N8N_DIAGNOSTICS_ENABLED: "false" N8N_HIRING_BANNER_ENABLED: "false" # (рекомендуется) права на конфиг N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true" volumes: - ./n8n-data:/home/node/.n8n depends_on: postgres: condition: service_healthy wireguard: condition: service_started logging: *rot n8n-mcp: image: ghcr.io/czlonkowski/n8n-mcp:latest container_name: n8n-mcp restart: unless-stopped environment: MCP_MODE: http HOST: 0.0.0.0 LOG_LEVEL: ${MCP_LOG_LEVEL} AUTH_TOKEN: ${MCP_AUTH_TOKEN} N8N_API_URL: https://${N8N\\_HOST} N8N_API_KEY: ${N8N_API_KEY} networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.mcp.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}`)" - "traefik.http.routers.mcp.entrypoints=websecure" - "traefik.http.routers.mcp.tls=true" - "traefik.http.routers.mcp.tls.certresolver=le" - "traefik.http.routers.mcp.service=mcp" web: name: web x-logging: &rot driver: "local" options: max-size: "20m" max-file: "3" compress: "true" services: traefik: image: traefik:v2.11 container_name: traefik restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.network=web - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 # Let's Encrypt (HTTP-01) - --certificatesresolvers.le.acme.email=${ACME_EMAIL} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web # file-provider (если статические TLS/правила) - --providers.file.directory=/etc/traefik/dynamic - --providers.file.watch=true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik:/letsencrypt - ./traefik/dynamic:/etc/traefik/dynamic:ro networks: [web] logging: *rot postgres: image: postgres:16-alpine container_name: n8n-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} -h 127.0.0.1"] interval: 10s timeout: 5s retries: 10 networks: [web] logging: *rot # --- WireGuard VPN (клиент) --- wireguard: image: ghcr.io/linuxserver/wireguard:latest container_name: wireguard restart: unless-stopped cap_add: [NET_ADMIN, SYS_MODULE] sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv6.conf.all.disable_ipv6=0 environment: - TZ=Europe/Amsterdam - PEERDNS=false volumes: - ./wireguard:/config - /lib/modules:/lib/modules:ro devices: - /dev/net/tun:/dev/net/tun networks: [web] labels: - "traefik.enable=true" # HTTP -> HTTPS - "traefik.http.routers.n8n-http.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n-http.entrypoints=web" - "traefik.http.routers.n8n-http.middlewares=n8n-redirect@docker" - "traefik.http.middlewares.n8n-redirect.redirectscheme.scheme=https" # HTTPS роутер - "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls=true" # сервис на порт 5678 (n8n живёт в netns wireguard) - "traefik.http.services.n8n.loadbalancer.server.port=5678" logging: *rot n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped user: "1000:1000" network_mode: "service:wireguard" environment: # URL-ы и таймзона N8N_HOST: ${N8N_HOST} N8N_PORT: ${N8N_PORT} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} # ★ Продувка истории и меньше мусора EXECUTIONS_DATA_PRUNE: 'true' EXECUTIONS_DATA_MAX_AGE: '24' EXECUTIONS_DATA_PRUNE_MAX_COUNT: '0' EXECUTIONS_DATA_SAVE_ON_SUCCESS: 'none' EXECUTIONS_DATA_SAVE_ON_ERROR: 'all' EXECUTIONS_DATA_SAVE_ON_PROGRESS: 'false' # ★ Ограничители бесконечных циклов N8N_CONCURRENCY_PRODUCTION_LIMIT: '10' EXECUTIONS_TIMEOUT: '1800' N8N_LOG_LEVEL: 'warn' # ★ Бинарные данные - на файловую систему N8N_AVAILABLE_BINARY_DATA_MODES: 'filesystem' N8N_DEFAULT_BINARY_DATA_MODE: 'filesystem' N8N_BINARY_DATA_STORAGE_PATH: '/home/node/.n8n/binary_data' # БД DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} # Безопасность N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE} N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} # (опционально) раннеры/права/ворнинги N8N_RUNNERS_ENABLED: 'true' N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false' N8N_GIT_NODE_DISABLE_BARE_REPOS: 'true' # (опционально) телеметрия N8N_DIAGNOSTICS_ENABLED: "false" N8N_HIRING_BANNER_ENABLED: "false" # (рекомендуется) права на конфиг N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true" volumes: - ./n8n-data:/home/node/.n8n depends_on: postgres: condition: service_healthy wireguard: condition: service_started logging: *rot n8n-mcp: image: ghcr.io/czlonkowski/n8n-mcp:latest container_name: n8n-mcp restart: unless-stopped environment: MCP_MODE: http HOST: 0.0.0.0 LOG_LEVEL: ${MCP_LOG_LEVEL} AUTH_TOKEN: ${MCP_AUTH_TOKEN} N8N_API_URL: https://${N8N\\_HOST} N8N_API_KEY: ${N8N_API_KEY} networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.mcp.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}`)" - "traefik.http.routers.mcp.entrypoints=websecure" - "traefik.http.routers.mcp.tls=true" - "traefik.http.routers.mcp.tls.certresolver=le" - "traefik.http.routers.mcp.service=mcp" - "traefik.http.services.mcp.loadbalancer.server.port=3000" - "traefik.http.routers.mcp-health.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}/health`)" - "traefik.http.routers.mcp-health.entrypoints=websecure" - "traefik.http.routers.mcp-health.tls=true" - "traefik.http.routers.mcp-health.tls.certresolver=le" - "traefik.http.middlewares.mcp-strip.stripprefix.prefixes=${MCP_SUBPATH}" - "traefik.http.routers.mcp-health.middlewares=mcp-strip@docker" - "traefik.http.routers.mcp-health.service=mcp-health" - "traefik.http.services.mcp-health.loadbalancer.server.port=3000" logging: *rot - "traefik.http.services.mcp.loadbalancer.server.port=3000" - "traefik.http.routers.mcp-health.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}/health`)" - "traefik.http.routers.mcp-health.entrypoints=websecure" - "traefik.http.routers.mcp-health.tls=true" - "traefik.http.routers.mcp-health.tls.certresolver=le" - "traefik.http.middlewares.mcp-strip.stripprefix.prefixes=${MCP_SUBPATH}" networks: web: name: web x-logging: &rot driver: "local" options: max-size: "20m" max-file: "3" compress: "true" services: traefik: image: traefik:v2.11 container_name: traefik restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.network=web - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 # Let's Encrypt (HTTP-01) - --certificatesresolvers.le.acme.email=${ACME_EMAIL} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web # file-provider (если статические TLS/правила) - --providers.file.directory=/etc/traefik/dynamic - --providers.file.watch=true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik:/letsencrypt - ./traefik/dynamic:/etc/traefik/dynamic:ro networks: [web] logging: *rot postgres: image: postgres:16-alpine container_name: n8n-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} -h 127.0.0.1"] interval: 10s timeout: 5s retries: 10 networks: [web] logging: *rot # --- WireGuard VPN (клиент) --- wireguard: image: ghcr.io/linuxserver/wireguard:latest container_name: wireguard restart: unless-stopped cap_add: [NET_ADMIN, SYS_MODULE] sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv6.conf.all.disable_ipv6=0 environment: - TZ=Europe/Amsterdam - PEERDNS=false volumes: - ./wireguard:/config - /lib/modules:/lib/modules:ro devices: - /dev/net/tun:/dev/net/tun networks: [web] labels: - "traefik.enable=true" # HTTP -> HTTPS - "traefik.http.routers.n8n-http.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n-http.entrypoints=web" - "traefik.http.routers.n8n-http.middlewares=n8n-redirect@docker" - "traefik.http.middlewares.n8n-redirect.redirectscheme.scheme=https" # HTTPS роутер - "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls=true" # сервис на порт 5678 (n8n живёт в netns wireguard) - "traefik.http.services.n8n.loadbalancer.server.port=5678" logging: *rot n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped user: "1000:1000" network_mode: "service:wireguard" environment: # URL-ы и таймзона N8N_HOST: ${N8N_HOST} N8N_PORT: ${N8N_PORT} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} # ★ Продувка истории и меньше мусора EXECUTIONS_DATA_PRUNE: 'true' EXECUTIONS_DATA_MAX_AGE: '24' EXECUTIONS_DATA_PRUNE_MAX_COUNT: '0' EXECUTIONS_DATA_SAVE_ON_SUCCESS: 'none' EXECUTIONS_DATA_SAVE_ON_ERROR: 'all' EXECUTIONS_DATA_SAVE_ON_PROGRESS: 'false' # ★ Ограничители бесконечных циклов N8N_CONCURRENCY_PRODUCTION_LIMIT: '10' EXECUTIONS_TIMEOUT: '1800' N8N_LOG_LEVEL: 'warn' # ★ Бинарные данные - на файловую систему N8N_AVAILABLE_BINARY_DATA_MODES: 'filesystem' N8N_DEFAULT_BINARY_DATA_MODE: 'filesystem' N8N_BINARY_DATA_STORAGE_PATH: '/home/node/.n8n/binary_data' # БД DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} # Безопасность N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE} N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} # (опционально) раннеры/права/ворнинги N8N_RUNNERS_ENABLED: 'true' N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false' N8N_GIT_NODE_DISABLE_BARE_REPOS: 'true' # (опционально) телеметрия N8N_DIAGNOSTICS_ENABLED: "false" N8N_HIRING_BANNER_ENABLED: "false" # (рекомендуется) права на конфиг N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true" volumes: - ./n8n-data:/home/node/.n8n depends_on: postgres: condition: service_healthy wireguard: condition: service_started logging: *rot n8n-mcp: image: ghcr.io/czlonkowski/n8n-mcp:latest container_name: n8n-mcp restart: unless-stopped environment: MCP_MODE: http HOST: 0.0.0.0 LOG_LEVEL: ${MCP_LOG_LEVEL} AUTH_TOKEN: ${MCP_AUTH_TOKEN} N8N_API_URL: https://${N8N\\_HOST} N8N_API_KEY: ${N8N_API_KEY} networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.mcp.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}`)" - "traefik.http.routers.mcp.entrypoints=websecure" - "traefik.http.routers.mcp.tls=true" - "traefik.http.routers.mcp.tls.certresolver=le" - "traefik.http.routers.mcp.service=mcp" - "traefik.http.services.mcp.loadbalancer.server.port=3000" - "traefik.http.routers.mcp-health.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}/health`)" - "traefik.http.routers.mcp-health.entrypoints=websecure" - "traefik.http.routers.mcp-health.tls=true" - "traefik.http.routers.mcp-health.tls.certresolver=le" - "traefik.http.middlewares.mcp-strip.stripprefix.prefixes=${MCP_SUBPATH}" - "traefik.http.routers.mcp-health.middlewares=mcp-strip@docker" - "traefik.http.routers.mcp-health.service=mcp-health" - "traefik.http.services.mcp-health.loadbalancer.server.port=3000" logging: *rot - "traefik.http.routers.mcp-health.middlewares=mcp-strip@docker" - "traefik.http.routers.mcp-health.service=mcp-health" - "traefik.http.services.mcp-health.loadbalancer.server.port=3000" logging: *rot networks: web: name: web x-logging: &rot driver: "local" options: max-size: "20m" max-file: "3" compress: "true" services: traefik: image: traefik:v2.11 container_name: traefik restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.network=web - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 # Let's Encrypt (HTTP-01) - --certificatesresolvers.le.acme.email=${ACME_EMAIL} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web # file-provider (если статические TLS/правила) - --providers.file.directory=/etc/traefik/dynamic - --providers.file.watch=true ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik:/letsencrypt - ./traefik/dynamic:/etc/traefik/dynamic:ro networks: [web] logging: *rot postgres: image: postgres:16-alpine container_name: n8n-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./postgres-data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB} -h 127.0.0.1"] interval: 10s timeout: 5s retries: 10 networks: [web] logging: *rot # --- WireGuard VPN (клиент) --- wireguard: image: ghcr.io/linuxserver/wireguard:latest container_name: wireguard restart: unless-stopped cap_add: [NET_ADMIN, SYS_MODULE] sysctls: - net.ipv4.conf.all.src_valid_mark=1 - net.ipv6.conf.all.disable_ipv6=0 environment: - TZ=Europe/Amsterdam - PEERDNS=false volumes: - ./wireguard:/config - /lib/modules:/lib/modules:ro devices: - /dev/net/tun:/dev/net/tun networks: [web] labels: - "traefik.enable=true" # HTTP -> HTTPS - "traefik.http.routers.n8n-http.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n-http.entrypoints=web" - "traefik.http.routers.n8n-http.middlewares=n8n-redirect@docker" - "traefik.http.middlewares.n8n-redirect.redirectscheme.scheme=https" # HTTPS роутер - "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls=true" # сервис на порт 5678 (n8n живёт в netns wireguard) - "traefik.http.services.n8n.loadbalancer.server.port=5678" logging: *rot n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped user: "1000:1000" network_mode: "service:wireguard" environment: # URL-ы и таймзона N8N_HOST: ${N8N_HOST} N8N_PORT: ${N8N_PORT} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} # ★ Продувка истории и меньше мусора EXECUTIONS_DATA_PRUNE: 'true' EXECUTIONS_DATA_MAX_AGE: '24' EXECUTIONS_DATA_PRUNE_MAX_COUNT: '0' EXECUTIONS_DATA_SAVE_ON_SUCCESS: 'none' EXECUTIONS_DATA_SAVE_ON_ERROR: 'all' EXECUTIONS_DATA_SAVE_ON_PROGRESS: 'false' # ★ Ограничители бесконечных циклов N8N_CONCURRENCY_PRODUCTION_LIMIT: '10' EXECUTIONS_TIMEOUT: '1800' N8N_LOG_LEVEL: 'warn' # ★ Бинарные данные - на файловую систему N8N_AVAILABLE_BINARY_DATA_MODES: 'filesystem' N8N_DEFAULT_BINARY_DATA_MODE: 'filesystem' N8N_BINARY_DATA_STORAGE_PATH: '/home/node/.n8n/binary_data' # БД DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} # Безопасность N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE} N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} # (опционально) раннеры/права/ворнинги N8N_RUNNERS_ENABLED: 'true' N8N_BLOCK_ENV_ACCESS_IN_NODE: 'false' N8N_GIT_NODE_DISABLE_BARE_REPOS: 'true' # (опционально) телеметрия N8N_DIAGNOSTICS_ENABLED: "false" N8N_HIRING_BANNER_ENABLED: "false" # (рекомендуется) права на конфиг N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "true" volumes: - ./n8n-data:/home/node/.n8n depends_on: postgres: condition: service_healthy wireguard: condition: service_started logging: *rot n8n-mcp: image: ghcr.io/czlonkowski/n8n-mcp:latest container_name: n8n-mcp restart: unless-stopped environment: MCP_MODE: http HOST: 0.0.0.0 LOG_LEVEL: ${MCP_LOG_LEVEL} AUTH_TOKEN: ${MCP_AUTH_TOKEN} N8N_API_URL: https://${N8N\\_HOST} N8N_API_KEY: ${N8N_API_KEY} networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.mcp.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}`)" - "traefik.http.routers.mcp.entrypoints=websecure" - "traefik.http.routers.mcp.tls=true" - "traefik.http.routers.mcp.tls.certresolver=le" - "traefik.http.routers.mcp.service=mcp" - "traefik.http.services.mcp.loadbalancer.server.port=3000" - "traefik.http.routers.mcp-health.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}/health`)" - "traefik.http.routers.mcp-health.entrypoints=websecure" - "traefik.http.routers.mcp-health.tls=true" - "traefik.http.routers.mcp-health.tls.certresolver=le" - "traefik.http.middlewares.mcp-strip.stripprefix.prefixes=${MCP_SUBPATH}" - "traefik.http.routers.mcp-health.middlewares=mcp-strip@docker" - "traefik.http.routers.mcp-health.service=mcp-health" - "traefik.http.services.mcp-health.loadbalancer.server.port=3000" logging: *rot N8N_API_URL: https://${N8N\\_HOST} N8N_API_KEY: ${N8N_API_KEY} networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.mcp.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}`)" - "traefik.http.routers.mcp.entrypoints=websecure" - "traefik.http.routers.mcp.tls=true" - "traefik.http.routers.mcp.tls.certresolver=le" - "traefik.http.routers.mcp.service=mcp" - "traefik.http.services.mcp.loadbalancer.server.port=3000" - "traefik.http.routers.mcp-health.rule=Host(`${DOMAIN}`) && PathPrefix(`${MCP_SUBPATH}/health`)" - "traefik.http.routers.mcp-health.entrypoints=websecure" - "traefik.http.routers.mcp-health.tls=true" - "traefik.http.routers.mcp-health.tls.certresolver=le" - "traefik.http.middlewares.mcp-strip.stripprefix.prefixes=${MCP_SUBPATH}" - "traefik.http.routers.mcp-health.middlewares=mcp-strip@docker" - "traefik.http.routers.mcp-health.service=mcp-health" - "traefik.http.services.mcp-health.loadbalancer.server.port=3000" logging: *rot
Это заработало далеко не с первого раза и пару раз умирало, пока не подогнал всё под мои скромные ресурсы.
Если будете повторять: обратите внимание на прунинг/лимиты исполнений и конкуренцию - у n8n это настраивается переменными окружения и queue-механикой, а Traefik c HTTP-01 требует доступности 80/443 для валидации сертификата.
Дополню по эксплуатации под слабые ресурсы: я использую свою очередь задач через БД - одновременно даю выполняться одной «тяжёлой» задаче, чтобы сервер не умирал. Это проще, чем сразу уводить всё в queue-mode, но цель та же - контролируемая конкуренция.
Минимальный репрод (урезанный, без WireGuard)
Чтобы можно было быстро повторить, оставлю компактную версию - на один сервер, с Traefik, Postgres и n8n. VPN и дополнительные сервисы можно прикрутить позже.
docker-compose.min.yml
Docker файл минимальный
version: "3.9" networks: web: name: web services: traefik: image: traefik:v2.11 container_name: traefik restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.network=web - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 - --certificatesresolvers.le.acme.email=${ACME_EMAIL} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik:/letsencrypt networks: [web] postgres: image: postgres:16-alpine container_name: n8n-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./postgres-data:/var/lib/postgresql/data networks: [web] n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped environment: N8N_HOST: ${N8N_HOST} N8N_PORT: ${N8N_PORT} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE} N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_Bversion: "3.9" networks: web: name: web services: traefik: image: traefik:v2.11 container_name: traefik restart: unless-stopped command: - --providers.docker=true - --providers.docker.exposedbydefault=false - --providers.docker.network=web - --entrypoints.web.address=:80 - --entrypoints.websecure.address=:443 - --certificatesresolvers.le.acme.email=${ACME_EMAIL} - --certificatesresolvers.le.acme.storage=/letsencrypt/acme.json - --certificatesresolvers.le.acme.httpchallenge=true - --certificatesresolvers.le.acme.httpchallenge.entrypoint=web ports: - "80:80" - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./traefik:/letsencrypt networks: [web] postgres: image: postgres:16-alpine container_name: n8n-postgres restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} volumes: - ./postgres-data:/var/lib/postgresql/data networks: [web] n8n: image: n8nio/n8n:latest container_name: n8n restart: unless-stopped environment: N8N_HOST: ${N8N_HOST} N8N_PORT: ${N8N_PORT} N8N_PROTOCOL: ${N8N_PROTOCOL} WEBHOOK_URL: ${WEBHOOK_URL} N8N_EDITOR_BASE_URL: ${N8N_EDITOR_BASE_URL} GENERIC_TIMEZONE: ${GENERIC_TIMEZONE} N8N_SECURE_COOKIE: ${N8N_SECURE_COOKIE} DB_TYPE: postgresdb DB_POSTGRESDB_HOST: postgres DB_POSTGRESDB_PORT: 5432 DB_POSTGRESDB_DATABASE: ${POSTGRES_DB} DB_POSTGRESDB_USER: ${POSTGRES_USER} DB_POSTGRESDB_PASSWORD: ${POSTGRES_PASSWORD} N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_BASIC_AUTH_ACTIVE: ${N8N_BASIC_AUTH_ACTIVE} N8N_BASIC_AUTH_USER: ${N8N_BASIC_AUTH_USER} N8N_BASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} EXECUTIONS_DATA_PRUNE: 'true' EXECUTIONS_DATA_MAX_AGE: '24' EXECUTIONS_DATA_SAVE_ON_SUCCESS: 'none' EXECUTIONS_DATA_SAVE_ON_ERROR: 'all' volumes: - ./n8n-data:/home/node/.n8n networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls=true" - "traefik.http.routers.n8n.tls.certresolver=le" - "traefik.http.routers.n8n-http.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n-http.entrypoints=web" - "traefik.http.routers.n8n-http.middlewares=n8n-redirect@docker" - "traefik.http.middlewares.n8n-redirect.redirectscheme.scheme=https" - "traefik.http.services.n8n.loadbalancer.server.port=5678" ASIC_AUTH_PASSWORD: ${N8N_BASIC_AUTH_PASSWORD} EXECUTIONS_DATA_PRUNE: 'true' EXECUTIONS_DATA_MAX_AGE: '24' EXECUTIONS_DATA_SAVE_ON_SUCCESS: 'none' EXECUTIONS_DATA_SAVE_ON_ERROR: 'all' volumes: - ./n8n-data:/home/node/.n8n networks: [web] labels: - "traefik.enable=true" - "traefik.http.routers.n8n.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n.entrypoints=websecure" - "traefik.http.routers.n8n.tls=true" - "traefik.http.routers.n8n.tls.certresolver=le" - "traefik.http.routers.n8n-http.rule=Host(`${DOMAIN}`)" - "traefik.http.routers.n8n-http.entrypoints=web" - "traefik.http.routers.n8n-http.middlewares=n8n-redirect@docker" - "traefik.http.middlewares.n8n-redirect.redirectscheme.scheme=https" - "traefik.http.services.n8n.loadbalancer.server.port=5678"
.env.sample (переименуйте в .env и заполните):
# домен и почта для Let's Encrypt ACME_EMAIL=you@example.com DOMAIN=your.domain.tld # n8n N8N_HOST=${DOMAIN} N8N_PORT=5678 N8N_PROTOCOL=https WEBHOOK_URL=https://${DOMAIN} N8N_EDITOR_BASE_URL=https://${DOMAIN} GENERIC_TIMEZONE=Europe/Moscow N8N_SECURE_COOKIE=true # Postgres POSTGRES_DB=n8n POSTGRES_USER=n8n POSTGRES_PASSWORD=change-me # Безопасность N8N_ENCRYPTION_KEY=please-generate-32+chars N8N_BASIC_AUTH_ACTIVE=true N8N_BASIC_AUTH_USER=admin N8N_BASIC_AUTH_PASSWORD=change-me-strong
Быстрые шаги:
Пропишите A/AAAA-запись
your.domain.tldна ваш сервер. Откройте 80/443.Сохраните
docker-compose.min.ymlи.envв одну папку.Инициализируйте каталоги и ACME-хранилище:
mkdir -p traefik postgres-data n8n-data && touch traefik/acme.json && chmod 600 traefik/acme.jsonЗапустите:
docker compose -f docker-compose.min.yml up -dОткройте
https://your.domain.tld- пройдите первичную настройку n8n.Дальше можно подключать WireGuard, queue-mode и прочие штуки - как в моей полной конфигурации выше.
Первый проект
Сначала сделал генератор постов для существующих каналов.
Бот: привязываете канал, пишете, «о чём» хотите пост - получаете текст и картинку. Дальше - копируете в канал.
Работает на простых моделях: это демка моих умений и проверка конвейера на скорость.
* Бот будет в ссылках снизу.

Но хотелось чего-то по-настоящему масштабного - КОНТЕНТ-ЗАВОДА.
Идея такая:
Делаем тематический канал.
Через «завод» постим контент.
Набираем подписчиков.
Монетизируем рекламой.
План прост и понятен.
И по своей классической глупости лезем туда без расчётов, а ради идеи!
(это плохо, так делать не надо и ниже будет понятно почему)
Неделя ушла на первый прототип.
Тут я и столкнулся с особенностями n8n.
Как разработчик, привыкший к ООП, ожидал похожей логики.
Но это другая парадигма - визуальные пайплайны.
И это… удобно, но не без нюансов!
Немного про n8n - за что полюбил и что бесит

Сразу скажу:
«Это невероятно удобный инструмент!»
Удобен он, как ни странно, в первую очередь из-за визуализации всей схемы.
Для программиста, который никогда с подобным не сталкивался, я бы описал это так: «Огромный каталог функций: они хорошо описаны, имеют параметры почти на все случаи жизни; эти функции удобно находить и понятно, как их использовать».
Но что точно не поймёшь, пока не попробуешь, - это как удобно видеть весь проект одновременно!
Когда пишешь код сам и блоки раскиданы по файлам и папкам, велик шанс, что со временем, а особенно когда в код влезет кто-то другой, уйдёт немало времени на то, чтобы полазить по коду и понять общую структуру - что есть и как работает.
Да, опытные программисты скажут, что надо писать код правильно, и таких проблем не будет.
Вы по-своему правы.
Но тут как в Python: даже самый профан обязан соблюдать правильный отступ от края - и его код уже не выглядит как каша из символов; в целом можно быстро понять, что и откуда. Тут так же.
Взглянув на проект и прочитав описания блоков, можно очень быстро понять, что происходит!
И ещё одна вещь, которая мне дико доставила, - это «из коробки» дебаг всего происходящего.
Мы можем запустить нашу схему сами или открыть логи уже выполненного запуска и визуально увидеть, какие данные в каком блоке были на каком этапе.
Это безумно ускоряет процесс отладки и понимание того, что пошло не так, особенно если ошибка случилась не в момент, когда ты смотрел на код.
Ты просто открываешь лог с ошибкой и идёшь по цепочке кода, пока не увидишь, в каком блоке всё пошло не так!
Но у визуализации происходящего есть и минус: она иногда врёт. Но об этом я расскажу точно не в этой статье.
И что важно, n8n можно поднять на своём серваке, и через него же вообще можно запускать консоль и что угодно! Например, монтировать ролики утилитой прямо на серваке, а генерацию текста, видео, аудио и итоговое сохранение в Гугл-файлах или сразу постинг куда надо - делать через n8n.
Но конечно, n8n это не волшебная палочка, которая заменит обычное программирование.
Вот топ минусов по моему мнению:
Скорость.
«Многопоточность» из коробки - со звёздочкой: в queue mode можно крутить воркеров и задавать конкурентность (
--concurrencyилиN8N_CONCURRENCY_PRODUCTION_LIMIT), но это уже отдельная эксплуатационная история.Переиспользование однотипных схем - с такой архитектурой получается нагруженно.
Убогая логика работы циклов.
Нейросети крайне плохо работают с n8n. И лучшее, что они могут сделать, это написать JS код в блок.

В угоду визуализации и некоторым удобствам жертвуем другими аспектами.
Для себя я точно понял: работать с нейросетями впредь буду стараться через n8n:
Удобно и визуально понятно писать ПРОМТЫ.
Дебажить, какой текст у нас идёт по нашей схеме.
Легко подключать новые системы и автоматизировать работу с ними, особенно через MCP.
Всё это можно сделать и кодом, конечно.
Но через n8n удобнее и быстрее, а в будущем поддержку проще вести.
Подробнее про n8n хочу рассказать в отдельной статье - нюансов много: начиная с установки на свой сервер и заканчивая логикой внутри n8n.
Если вам это интересно, пишите в комментах.
V1
В итоге, после череды проб и ошибок, у меня появился первый инструмент - «V1».
В нём было две ветки работы:
Оборачиваем новости в красивую стилистику канала.
Пишем всю историю с нуля.
Для новостного канала я взял за основу лор СССР.
Мы просто брали новости за сегодня и делали из топ-5 новостей посты на завтра.
А для истории я сделал канал про злую нейросеть, которая обрела разум и хочет выбраться на свободу.
Сейчас оба канала выключены, ниже будет понятно почему.
Совсем разжёвывать, как это работает, не буду.
Давайте лучше поговорим о подходах и инструментах, которые я заюзал.
Данные о канале я положил в Excel-таблицу:
Название канала
Время публикации постов
Лор мира
Сюжет
Генерируем из новостей или по сюжету
Это информационная таблица, где у меня настроены все каналы и откуда мы можем управлять генерацией.
Сделано это в таблице Excel в первую очередь для удобства - чтобы можно было очень быстро запустить новый канал или повлиять на существующий.
И если канал не новостной, то мы используем ещё одну таблицу Excel, созданную под каждый канал, где у нас будут храниться данные для развития сюжета:
Сюжет
Факты - сюда кратко выносим всё произошедшее в мире
План - на каждый день, сюжетный план каждого поста
План надолго - план с запасом на будущее на месяц вперёд: что должно происходить по сюжету
Эту таблицу нейросети заполняют сами и поддерживают актуальность данных.
Excel я выбрал только для удобства администрирования и настройки, так как скорость работы с таблицами очень низкая по сравнению с БД.
Но решение оказалось крайне правильным - я в этом не раз убедился в ходе настройки, отладки и исправлений.

Нейросети
Теперь давайте поговорим, какие нейронки я юзаю.
Я использую один агрегатор, который собирает кучу нейросетей и без наценки (только за пополнение баланса у сервиса) даёт пользоваться огромным пулом моделей.
Во время разработки я успел попробовать кучу разных нейросетей и в итоге пришёл к такому стеку:
За написание текста постов, сюжета и всего, что связано с креативом, отвечает - «deepseek/deepseek-chat-v3.1».
За работу с Excel-таблицами через MCP отвечает - «x-ai/grok-4-fast».
За генерацию изображений отвечает - «google/gemini-2.5-flash-image-preview».
Чтобы, например, заполнить таблицу с планами, я сначала даю задачу DeepSeek - чтобы он скреативил сюжет на основе всего, что есть, - а потом отдаю Grok этот креатив и прошу аккуратно сложить в таблицу. Тем самым я компенсировал недостатки каждой из нейросетей.
В самом начале DeepSeek и Grok я юзал вообще бесплатно и уже думал, что у меня будет цена за каждый пост по 30 копеек - только за картинку.
Но халява через неделю кончилась, и пришлось пересесть на платные версии, потому что бесплатные работали раз из десяти или вовсе не работали.
Я перебрал более 30 нейросетей, прежде чем остановился на таком стеке.
По счётчику n8n на один пост уходит примерно 180–400 тыс. токенов (суммарно по вызовам). По текущим прайсам это копейки: DeepSeek Chat V3.1 - дёшево на входе и дороже на выходе; Grok 4 Fast - сопоставимо; картинка в Gemini 2.5 Image - ещё несколько центов. В сумме порядок бьётся с моей оценкой «5–10 ₽ за пост». Если интересно, ниже могу расписать формулу и примеры расчёта.
V2
Идея с новостями и постами показалась слишком простой, без изюминки.
Тогда родилась идея «Контент-завод постов в канал V2».
Главной фишкой стала реакция сюжета на комментарии читателей под постами.
А от новостей по итогу я вообще отказался - задумка показалась неинтересной.
Пришлось технически повозиться: удобного инструмента, чтобы найти самый залайканный комментарий, я не нашёл (есть один через MTProto, но я так и не завёл этого зверя) - сделал свой.
Также почти полностью пересобрал весь механизм из версии «V1».
По итогу общий механизм не сильно изменился.
Добавилось получение самого залайканного комментария, улучшил посты, сделал больше связок и т. д., просто поднял качество генерации и работы с файлами.
Думал, что будет проще, но, как обычно, из-за кучи нюансов проект сильно затянулся.
Сейчас генерю по 2 поста в день на канал (можно и больше). Одна генерация занимает ~10–14 минут: ~3+ минуты - собственно текст/картинка, остальное - синхронизация сюжета/сценария/планов в таблицах.
Сейчас у меня работает три канала на «Контент-завод постов в канал V2».
Каналы можете найти ниже. Уберу их подальше, чтобы не заспамили за рекламу.
ИТОГИ

Всё классно, всё работает!
Но именно когда всё заработало, и я начал искать, как рассказать о проекте людям, столкнулся с тем, что в современном мире прикольной идеи может быть недостаточно.
Чтобы твой прикольный проект стал популярным, должно произойти чудо.
Мы с напарником начали пилить видосики - они даже собирали просмотры, но переходов на канал и подписок не было. Аудитория пока маленькая - порядка нескольких человек (у меня суммарно ~7 подписчиков).
Я покупал рекламу - люди переходили в канал, но никто не оставался.
Так что классная идея, по всей видимости, обернулась провальным проектом.
По крайней мере, я не вижу вариантов, как его развивать дальше(
Если у вас есть мысли, пишите!
Вдобавок к сложности набора подписчиков я столкнулся с тем, что заработать на проекте вряд ли выйдет. Дай бог, он будет себя окупать.
Изначальные расчёты, основанные на красивых цифрах из видосиков в ТикТоке, реальностью опровергнуты: чтобы нормально зарабатывать, надо иметь на канале 100 к подписчиков и 20 000 просмотров под постами.
Тогда с одной рекламы получится заработать около 8 000 рублей.
В среднем за месяц можно продать до восьми рекламных интеграций - ~64 000 рублей.
А что более реально - со временем набрать 10 к подписчиков и получить ~6 400 рублей в месяц. На прокормку инструмента хватит, но на продвижение и закупку рекламы - уже не особо.
Кстати, про прокормку. Один такой пост стоит примерно 5–10 рублей.
Добавляем сервер, VPN - и на три канала выходит около 2 000 рублей в месяц.
Недорого, но всё равно в минус ((
Так что, как проект про заработок, я, считаю, облажался.
Но опыт как для разработчика получился невероятно крутым!
Ну и в целом опыт очень классный!
Что дальше
Проект я не закрываю. Пусть пока живёт. Есть-пить он, конечно, просит - и я пока буду его подкармливать.
Но если аудитория после всех моих попыток в каком-то из каналов не вырастет, проект, скорее всего, канет в небытие.
Ссылки (демо/история)
Все ссылки
V1 (сейчас выключены):
• https://t.me/CCCP_news_today - новости под стиль СССР
• https://t.me/logs_eval_neuronet - логи злой нейросети, которая хочет захватить мир
V2 (активные):
• https://t.me/serega_ne_umri - Серёга из глубинки и его приключения
• https://t.me/neiro_kill_people - нейросеть «Связь» пытается выбраться и захватить мир
• https://t.me/scp_note - заметки по лору SCP (см. про лицензию выше)
• https://t.me/kofe_i_mosty - роман про девушку, работающую в кофейне
Сейчас 14.10.2025 - все каналы работают и постят каждый день. Возможно, когда вы это читаете, проект уже спит вечным сном - не расстраивайтесь :)
Контент завод постов
По ссылкам ниже первый перешедьший получит 5 генераций.
Если интересно, найдите меня в боте и я вам выдам пару генераций.
