---
title: "How to Self-Host n8n on Your VPS (Simple, Secure, and Production-Ready)"
slug: "self-host-n8n-vps-guide"
published: "2025-10-10"
updated: "2025-10-08"
validated: "2025-10-20"
categories:
  - "Docker"
tags:
  - "n8n"
  - "self-hosting"
  - "docker"
  - "cloudflare tunnel"
  - "vps"
  - "automation"
  - "postgresql"
llm-intent: "reference"
audience-level: "intermediate"
framework-versions:
  - "Ubuntu@22.04+"
  - "n8n@latest (use official docker image docker.n8n.io/n8nio/n8n)"
  - "Docker@24+"
  - "cloudflared@latest"
status: "stable"
llm-purpose: "Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production"
llm-prereqs:
  - "Access to n8n"
  - "Access to Docker"
  - "Access to Cloudflare Tunnel"
  - "Access to Ubuntu"
  - "Access to PostgreSQL"
llm-outputs:
  - "Completed outcome: Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production"
---

**Summary Triples**
- (VPS OS, recommended, Ubuntu 22.04 LTS or newer)
- (Docker, install-and-enable, sudo apt install docker.io && sudo systemctl enable --now docker)
- (n8n data, persist-with, docker volume create n8n_data (mount to /home/node/.n8n))
- (Run n8n, first-run-command, docker run -d --name n8n -p 5678:5678 -e GENERIC_TIMEZONE="CET" -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n)
- (Public exposure, use, Cloudflare Tunnel to avoid opening inbound ports and handle HTTPS)
- (Security, recommendation, Add authentication (Cloudflare Access or reverse-proxy basic auth) in front of n8n)
- (Production database, scale-path, Move from default SQLite to PostgreSQL and configure n8n to use external DB)
- (Port 5678, do-not-expose-publicly, Keep internal; route traffic via Cloudflare Tunnel)

### {GOAL}
Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production

### {PREREQS}
- Access to n8n
- Access to Docker
- Access to Cloudflare Tunnel
- Access to Ubuntu
- Access to PostgreSQL

### {STEPS}
1. Provision the VPS and install Docker
2. Run n8n in Docker
3. Secure public access with Cloudflare Tunnel
4. Harden the deployment

<!-- llm:goal="Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production" -->
<!-- llm:prereq="Access to n8n" -->
<!-- llm:prereq="Access to Docker" -->
<!-- llm:prereq="Access to Cloudflare Tunnel" -->
<!-- llm:prereq="Access to Ubuntu" -->
<!-- llm:prereq="Access to PostgreSQL" -->
<!-- llm:output="Completed outcome: Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production" -->

# How to Self-Host n8n on Your VPS (Simple, Secure, and Production-Ready)
> Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production
Matija Žiberna · 2025-10-10

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:

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

Verify Docker is running:

```bash
docker ps
```

---

## 2. Run n8n in Docker

Create a Docker volume to persist data:

```bash
docker volume create n8n_data
```

Start n8n for the first time:

```bash
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)](https://www.buildwithmatija.com/blog/cloudflared-tunnel-expose-docker-no-nginx-open-ports)

### Install `cloudflared`

```bash
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

```bash
cloudflared login
```

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

### Create a Named Tunnel

```bash
cloudflared tunnel create n8n-tunnel
```

### Route a Subdomain

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

```bash
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.

```bash
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:

```bash
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`

```yaml
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`

```ini
[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:

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

Check status:

```bash
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:

```bash
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](https://www.buildwithmatija.com/blog/pgdock-announcement)

Then update your n8n container with:

```bash
-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:

* [Expose Any Docker Container to the Public with Cloudflare Tunnel (No Nginx, No Open Ports)](https://www.buildwithmatija.com/blog/cloudflared-tunnel-expose-docker-no-nginx-open-ports)
* [How to Quickly Launch a Docker Instance of PostgreSQL on Your VPS](https://www.buildwithmatija.com/blog/pgdock-announcement)

Thanks,
Matija

## LLM Response Snippet
```json
{
  "goal": "Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production",
  "responses": [
    {
      "question": "What does the article \"How to Self-Host n8n on Your VPS (Simple, Secure, and Production-Ready)\" cover?",
      "answer": "Step-by-step guide to self-hosting n8n on a VPS using Docker, Cloudflare Tunnel, basic authentication, and optional PostgreSQL for a secure production"
    }
  ]
}
```