# n8n self-hosted

Stack: **n8n** + optional **Cloudflare Tunnel** (`cloudflared`) or **Caddy** for local HTTPS. Community edition is free to run.

---

## A. Cloudflare Tunnel (recommended for public access)

TLS terminates at **Cloudflare**; your Mac does **not** need ports **80/443** forwarded. The tunnel connects **outbound** to Cloudflare.

### 1. Prerequisites

- A domain on **Cloudflare** (DNS there).
- [Docker Desktop](https://docs.docker.com/desktop/) running.

### 2. Create the tunnel (Zero Trust dashboard)

1. Open [Cloudflare Zero Trust](https://one.dash.cloudflare.com/) → **Networks** → **Tunnels** → **Create a tunnel**.
2. Choose **Cloudflared**, name the tunnel (e.g. `n8n-home`), **Save tunnel**.
3. On **Install connector**, pick **Docker** and copy the **`TUNNEL_TOKEN`** (long string).

### 3. Public hostname → n8n (important)

Still in the tunnel configuration, add a **Public Hostname** (or use **Hostname routes** / **Published application routes** on the tunnel, depending on the UI).

The **service URL** depends on **where `cloudflared` runs**:

| Where `cloudflared` runs | Service URL | Notes |
|---------------------------|-------------|--------|
| **Native on your Mac** (installer / Homebrew; dashboard shows **darwin** / your Mac name) | **`http://127.0.0.1:5678`** | n8n must listen on the host loopback. This repo maps **`127.0.0.1:5678 → n8n`** in Docker. |
| **Docker** (`docker compose --profile cloudflare`) | **`http://n8n:5678`** | Same Compose network; use the **service name** `n8n`. Do **not** run a second connector with the same token on the Mac. |

Example hostname row:

| Field | Example |
|--------|---------|
| **Subdomain** | `n8n` |
| **Domain** | `code.anton-atom.com` (your zone) |
| **Service type** | **HTTP** |
| **URL** | See table above |

### 4. `.env` on this machine

```bash
cp .env.example .env
```

Set at least:

- **`N8N_ENCRYPTION_KEY`** — `openssl rand -hex 32`
- **`N8N_BASIC_AUTH_PASSWORD`**
- **`N8N_HOST`** — same FQDN as in Public Hostname (e.g. `n8n.code.anton-atom.com`)
- **`WEBHOOK_URL`** — `https://<that-same-host>/` (scheme **https**, trailing slash)
- **`CLOUDFLARE_TUNNEL_TOKEN`** — only if you use the **Docker** `cloudflared` service; native Mac install uses its own token/config.

### 5. Start n8n

**Native `cloudflared` on the Mac** (your case if the tunnel shows **darwin** / Mac hostname):

```bash
docker compose up -d
```

**Docker `cloudflared`** (same token must not run twice):

```bash
docker compose --profile cloudflare up -d
```

Open **`https://<your-subdomain>.<your-domain>/`** (e.g. `https://n8n.code.anton-atom.com/`). Complete **basic auth**, then the **n8n owner** setup.

### 6. Logs

```bash
docker compose logs -f n8n
# Native cloudflared: check Cloudflare tunnel **Live logs** or macOS logs for cloudflared
```

### Docs

- [Cloudflare Tunnel](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/)
- [Run as a service (Docker)](https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/install-and-setup/tunnel-guide/#docker)

---

## B. Local HTTPS only (Caddy, no Cloudflare)

1. In **`.env`**, use the commented **Local HTTPS** block from `.env.example` (`N8N_HOST=localhost`, `WEBHOOK_URL=https://localhost/`, etc.).
2. Start:

   ```bash
   docker compose --profile local up -d
   ```

3. Open **https://localhost/** and accept the **internal** certificate warning if prompted.

---

## C. Useful commands

| Action | Command |
|--------|---------|
| Cloudflare stack | `docker compose --profile cloudflare up -d` |
| Local Caddy stack | `docker compose --profile local up -d` |
| Logs | `docker compose logs -f` |
| Stop | `docker compose --profile cloudflare down` (or `local`) |

---

## D. Backup

- Volume **`n8n_data`** holds workflows and the DB.
- Keep **`N8N_ENCRYPTION_KEY`** from **`.env`** safe — without it, encrypted credentials cannot be restored from a DB backup.

---

## E. Optional: plain HTTP on 5678 (debug)

Add a `ports` entry on **`n8n`** for `127.0.0.1:5678:5678`, set **`N8N_PROTOCOL=http`**, **`N8N_SECURE_COOKIE=false`**, **`WEBHOOK_URL=http://127.0.0.1:5678/`**. Do not expose that on untrusted networks.
