# dokploy-cluster.sh

Single-file bootstrapper for a six-node Dokploy Docker Swarm (3 managers, 3 workers) on Hetzner Cloud running Fedora 44: swarm over the Hetzner private network, Hetzner Load Balancer with `/ping`-gated TCP passthrough, global Traefik on the managers with Cloudflare DNS-01 certificates and Cloudflare-only origin enforcement, Tailscale SSH as the admin plane. Every design decision, verification status, and caveat lives in `GUIDE.md` — this script executes that tutorial's sections.

**Install** — downloads the toolkit into a fresh `dokploy-cluster.*` folder under /tmp and launches the interactive setup right away (pass a directory — `… | sh -s -- <dir>` — to keep the toolkit somewhere permanent):

```bash
curl -fsSL https://provision.lvq.app/dokploy/install.sh | sh
```

**Requirements:** a Hetzner Cloud project (API token) and a tailnet prepared per tutorial §2-B (tag, SSH ACL rule, tagged reusable auth key). Workstation dependencies (`hcloud`, `jq`, `rsync`, `ssh`, `curl`) are checked on start and the script offers to install missing ones — or answer no if you know better. Secrets enter via interactive prompt or environment, never argv.

**Usage** — the interactive mode drives everything:

```bash
./provision.sh up      # checks deps, prompts for secrets, datacenter, VPS types
                       # and SSH key, then runs the pipeline with guided pauses
                       # at the manual interludes (Dokploy admin UI, Cloudflare)
```

Or run the pipeline step by step (run bare to print it; env-driven, useful for re-runs and repairs):

```bash
TS_AUTHKEY=tskey-auth-… ./provision.sh provision
./provision.sh wait
./provision.sh swarm-init
./provision.sh dokploy-install
# Dokploy UI via Tailscale :3000 — create the admin account
./provision.sh join
ACME_EMAIL=… CF_DNS_API_TOKEN=… ./provision.sh traefik
./provision.sh lb
# Cloudflare: SSL mode Full (strict); records → LB IP, Proxied
./provision.sh cf-allowlist
./provision.sh verify
```

**Properties:** guarded re-runs, not convergence — existing resources are skipped, drift is not reconciled. Node IPs come from the hcloud API. All scratch files live in a `mktemp -d` workspace that is removed on exit. `traefik.yml` edits are structural (PyYAML, `.orig` backup, fails loud on layout drift). The Cloudflare token becomes a Docker secret via stdin, never argv. Re-run `cf-allowlist` when Cloudflare's published ranges change; confirm the LB health-check flag names against your `hcloud` version before the first `lb` run.
