Run n8n as a durable background service on a Plesk host with local-only Docker networking and Plesk’s Nginx as the public reverse proxy.
Why this guide
n8n is a popular automation/workflow tool. On Plesk servers you can keep your stack tidy by:
- Running n8n in Docker (isolated, easy upgrades)
- Binding it to 127.0.0.1 (not publicly exposed)
- Using Plesk’s Nginx as the SSL/TLS reverse proxy
- Managing lifecycle via systemd so it behaves like a service (starts on boot, simple
systemctlcontrol)
This post shows all the pieces end‑to‑end.
Prerequisites
- Ubuntu 22.04/24.04 with Docker Engine and Docker Compose v2 installed
- Plesk Obsidian with Proxy mode enabled for your domain
- A DNS record for the hostname (e.g.,
n8n.example.com) pointing to the server - Basic shell access as root (or sudo)
Security model: n8n listens on
127.0.0.1:5678only; Plesk terminates HTTPS and reverse‑proxies to the container.
1) Create a project directory and .env
Pick a working directory (example path):
|
1 2 3 |
mkdir -p /var/www/vhosts/n8n.example.com/n8n-compose cd /var/www/vhosts/n8n.example.com/n8n-compose |
Create a .env file for Compose variable expansion:
|
1 2 3 4 5 6 |
SUBDOMAIN=n8n DOMAIN_NAME=example.com # Use IANA tz for DST-awareness (Pacific time) GENERIC_TIMEZONE=America/Los_Angeles # If you want a fixed UTC−8 with no DST, use: Etc/GMT+8 |
2) compose.yaml for n8n
This configuration runs n8n detached, auto‑restarts, and stores state on a named volume. It binds to localhost so only Plesk can reach it.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
version: "3.9" services: n8n: image: docker.n8n.io/n8nio/n8n container_name: n8n restart: unless-stopped init: true ports: - "127.0.0.1:5678:5678" environment: - N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true - N8N_HOST=${SUBDOMAIN}.${DOMAIN_NAME} - N8N_PORT=5678 - N8N_PROTOCOL=https - N8N_RUNNERS_ENABLED=true - NODE_ENV=production - WEBHOOK_URL=https://${SUBDOMAIN}.${DOMAIN_NAME}/ - GENERIC_TIMEZONE=${GENERIC_TIMEZONE} - TZ=${GENERIC_TIMEZONE} volumes: - n8n_data:/home/node/.n8n - ./local-files:/files stop_grace_period: 30s volumes: n8n_data: |
Bring it up:
|
1 2 |
docker compose up -d |
Check it:
|
1 2 3 |
docker compose ps curl -I http://127.0.0.1:5678/ |
3) Manage with systemd (start on boot)
Create /etc/systemd/system/n8n-compose.service:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[Unit] Description=n8n Docker Compose Stack Requires=docker.service After=docker.service ConditionPathExists=/var/www/vhosts/n8n.example.com/n8n-compose [Service] Type=oneshot WorkingDirectory=/var/www/vhosts/n8n.example.com/n8n-compose RemainAfterExit=yes ExecStart=/usr/bin/docker compose up -d ExecStop=/usr/bin/docker compose down TimeoutStartSec=0 [Install] WantedBy=multi-user.target |
Enable and start:
|
1 2 3 4 |
systemctl daemon-reload systemctl enable --now n8n-compose systemctl status n8n-compose |
Troubleshooting: If you see
status=200/CHDIR, theWorkingDirectorydoesn’t exist. Fix the path or use-f /full/path/compose.yamlonExecStart/ExecStop.
4) Plesk Nginx reverse proxy
In Plesk, go to Websites & Domains → Apache & Nginx Settings → Additional Nginx directives and paste the following (adjust payload size/timeouts to taste):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# --- n8n via local Docker on 127.0.0.1:5678 --- client_max_body_size 50m; proxy_buffering off; proxy_read_timeout 600s; proxy_send_timeout 600s; location / { proxy_pass http://127.0.0.1:5678; # WebSocket / SSE support proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; # Preserve origin details proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; add_header X-Accel-Buffering no; } |
Save/apply, then test over HTTPS:
|
1 2 |
curl -I https://n8n.example.com/ |
Plesk’s Let’s Encrypt extension will keep certificates renewed. The Nginx location above proxies the entire site to n8n; if you prefer a sub‑path (e.g.,
/n8n/), you’ll need to set n8n’s base path variables and rewrite rules—ask us for a tailored snippet.
5) Updates & maintenance
- Update n8n:
1234cd /var/www/vhosts/n8n.example.com/n8n-composedocker compose pulldocker compose up -d - Backups: snapshot/backup the
n8n_datavolume (or copy/var/lib/docker/volumes/<project>_n8n_data/_data). - Logs:
docker compose logs -f n8n - Resource usage:
docker stats
6) Common pitfalls
- Port already in use: something else bound to
127.0.0.1:5678. Change the host port or stop the conflicting service. - Plesk returns 502/504: increase
proxy_read_timeout; confirm the container is healthy and reachable viacurl http://127.0.0.1:5678/. - Large uploads fail: raise
client_max_body_sizein the Nginx directives. - Time zone confusion: prefer IANA zones like
America/Los_Angelesfor DST-aware behavior. For fixed offsets with no DST, useEtc/GMT+8(note the reversed sign convention). - systemd fails
CHDIR: verifyWorkingDirectoryand path names; rundocker compose configfor syntax validation.
7) Optional hardening
- Keep n8n private to localhost (as shown). If you must expose directly, enable HTTPS on the container or put a dedicated reverse proxy in front—Plesk already does this safely.
- Add IP allow‑lists or HTTP auth at Nginx for admin paths.
- Consider a WAF/CDN (Cloudflare/Sucuri) in front of Plesk, but be sure to pass WebSocket upgrades.
Conclusion
With Docker Compose, systemd, and Plesk’s Nginx, n8n runs like a first‑class service: isolated, auto‑starting, and secured behind Plesk’s TLS.
If you’d like help tailoring this to your environment (multi‑tenant Plesk, alternative base paths, Cloudflare/Sucuri in front, automated backups, or HA setups), Reliable Penguin can assist.




