How to Self-Host n8n on Your VPS (Simple, Secure, and Production-Ready)

Docker deployment secured with Cloudflare Tunnel, hardened access, and production-ready tweaks

·Matija Žiberna·
How to Self-Host n8n on Your VPS (Simple, Secure, and Production-Ready)

🐳 Docker & DevOps Implementation Guides

Complete Docker guides with optimization techniques, deployment strategies, and automation prompts to streamline your containerization workflow.

No spam. Unsubscribe anytime.

If you’ve been using n8n Cloud but want full control of your automations (and to stop paying monthly fees), self-hosting n8n on your own VPS is a great move.

In this guide, I’ll show you how to install n8n on any VPS using Docker, expose it securely to the public internet using Cloudflare Tunnel (no Nginx, no Let’s Encrypt, no open ports), and configure it for production with your real domain.


Why Self-Host n8n?

Running n8n yourself gives you:

  • Full data ownership — credentials and workflows stay on your server
  • Unlimited workflows for a fixed VPS cost
  • Better integration with private systems and APIs
  • More control over scaling, logging, and automation triggers

Even a small 2 GB VPS can handle dozens of n8n workflows comfortably.


1. Set Up Your VPS

You can use any provider — Hetzner, DigitalOcean, AWS Lightsail, etc. Make sure it’s running Ubuntu 22.04 LTS or newer.

Update and install Docker:

sudo apt update && sudo apt upgrade -y
sudo apt install docker.io curl -y
sudo systemctl enable --now docker

Verify Docker is running:

docker ps

2. Run n8n in Docker

Create a Docker volume to persist data:

docker volume create n8n_data

Start n8n for the first time:

docker run -d \
  --name n8n \
  -p 5678:5678 \
  -e GENERIC_TIMEZONE="CET" \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n

At this point, n8n is running locally on port 5678. Instead of exposing that port to the public, we’ll secure it through Cloudflare.


3. Expose n8n Securely with Cloudflare Tunnel

You could use Nginx and Let’s Encrypt manually, but Cloudflare Tunnel makes it simpler and safer — no ports, no certificates, and automatic HTTPS.

If you want a detailed explanation of how tunnels work and why they replace reverse proxies, check out: Expose Any Docker Container to the Public with Cloudflare Tunnel (No Nginx, No Open Ports)

Install cloudflared

sudo mkdir -p --mode=0755 /usr/share/keyrings
curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null
echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared bullseye main' | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared -y

Authenticate with Cloudflare

cloudflared login

Choose your domain and authorize through the browser. Credentials are saved at ~/.cloudflared/cert.pem.

Create a Named Tunnel

cloudflared tunnel create n8n-tunnel

Route a Subdomain

For example, route automate.we-hate-copy-pasting.com:

cloudflared tunnel route dns n8n-tunnel automate.we-hate-copy-pasting.com

4. Start n8n Again with Your Correct Domain

Now that your tunnel and domain are ready, restart n8n with the proper production configuration.

docker stop n8n && docker rm n8n

docker run -d \
  --name n8n \
  -p 5678:5678 \
  -e GENERIC_TIMEZONE="CET" \
  -e TZ="CET" \
  -e N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS=true \
  -e N8N_RUNNERS_ENABLED=true \
  -e WEBHOOK_URL="https://automate.we-hate-copy-pasting.com/" \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n

The WEBHOOK_URL variable ensures that external triggers, webhooks, and integrations use your real HTTPS domain — essential for production deployments.


5. Connect the Tunnel to n8n

Run this to connect the Cloudflare Tunnel to your local n8n instance:

cloudflared tunnel --url http://localhost:5678 run n8n-tunnel

After a few seconds, you can open:

https://automate.we-hate-copy-pasting.com

You’ll see the n8n editor live, served securely through Cloudflare.


6. Keep the Tunnel Running with systemd

Create a persistent configuration file.

File: ~/.cloudflared/config.yml

tunnel: n8n-tunnel
credentials-file: /home/ubuntu/.cloudflared/<tunnel-id>.json
ingress:
  - hostname: automate.we-hate-copy-pasting.com
    service: http://localhost:5678
  - service: http_status:404

Then create a systemd service:

File: /etc/systemd/system/cloudflared-n8n.service

[Unit]
Description=Cloudflared Tunnel for n8n
After=network.target

[Service]
User=ubuntu
ExecStart=/usr/bin/cloudflared --config /home/ubuntu/.cloudflared/config.yml tunnel run n8n-tunnel
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

Enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now cloudflared-n8n.service

Check status:

sudo systemctl status cloudflared-n8n

Now your tunnel reconnects automatically after reboots or connection drops.


7. Add Basic Authentication

Protect your n8n dashboard by enabling Basic Auth:

docker stop n8n && docker rm n8n

docker run -d \
  --name n8n \
  -p 5678:5678 \
  -e GENERIC_TIMEZONE="CET" \
  -e TZ="CET" \
  -e WEBHOOK_URL="https://automate.we-hate-copy-pasting.com/" \
  -e N8N_BASIC_AUTH_ACTIVE=true \
  -e N8N_BASIC_AUTH_USER=admin \
  -e N8N_BASIC_AUTH_PASSWORD=strongpassword \
  -v n8n_data:/home/node/.n8n \
  docker.n8n.io/n8nio/n8n

Now you’ll be prompted for credentials before accessing the UI.


8. (Optional) Use PostgreSQL for Production

SQLite is fine for small setups, but PostgreSQL is better for reliability and multi-user workflows. You can easily launch a Dockerized database right next to n8n — I’ve written a separate article for that: How to Quickly Launch a Docker Instance of PostgreSQL on Your VPS

Then update your n8n container with:

-e DB_TYPE=postgresdb \
-e DB_POSTGRESDB_DATABASE=n8n \
-e DB_POSTGRESDB_HOST=localhost \
-e DB_POSTGRESDB_PORT=5432 \
-e DB_POSTGRESDB_USER=n8nuser \
-e DB_POSTGRESDB_PASSWORD=supersecret \

9. Verify Everything Works

Visit your domain:

https://automate.we-hate-copy-pasting.com

You should see your self-hosted n8n instance — encrypted, authenticated, and backed by Cloudflare’s network. No open ports, no Nginx, no manual certificates.


10. Conclusion

You now have your own production-ready n8n Cloud running on your VPS:

  • Docker handles isolation and persistence
  • Cloudflare Tunnel provides secure HTTPS without opening ports
  • WEBHOOK_URL ensures all workflows use your real domain
  • systemd keeps it online automatically
  • PostgreSQL (optional) prepares it for scale

You fully own your automation stack — and you can reuse this exact setup for any self-hosted service.

If you want to dive deeper into the Cloudflare setup or connect a database next, check out:

Thanks, Matija

2

Frequently Asked Questions

Comments

Leave a Comment

Your email will not be published

10-2000 characters

• Comments are automatically approved and will appear immediately

• Your name and email will be saved for future comments

• Be respectful and constructive in your feedback

• No spam, self-promotion, or off-topic content

Matija Žiberna
Matija Žiberna
Full-stack developer, co-founder

I'm Matija Žiberna, a self-taught full-stack developer and co-founder passionate about building products, writing clean code, and figuring out how to turn ideas into businesses. I write about web development with Next.js, lessons from entrepreneurship, and the journey of learning by doing. My goal is to provide value through code—whether it's through tools, content, or real-world software.

You might be interested in