Давайте поговорим о нейросетях и автоматизациях
Летом 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 генераций.
Если интересно, найдите меня в боте и я вам выдам пару генераций.