CluPilotCloud/docs/deployment.md

80 lines
3.6 KiB
Markdown

# Deploying CluPilot on a fresh server
Developed locally, deployed by pulling. Two scripts do the work:
```bash
# once, on a fresh Debian/Ubuntu server — as root, because of apt
bash deploy/install.sh
# afterwards, as the service account — never as root
sudo -u clupilot bash -c 'cd /opt/clupilot && bash deploy/update.sh'
```
The installer needs root only to install packages and open the firewall. It
creates a `clupilot` account that owns the checkout and runs Docker; the
application never runs as root, and `update.sh` refuses to start if you try.
That account has no password login — membership in the docker group is
root-equivalent on the host, which is inherent to Docker and the reason it is
reachable only by `sudo -u` or an SSH key.
## What the installer does
Docker, git and `wireguard-tools`; clone into `/opt/clupilot`; generate every
secret (`APP_KEY`, `VPN_CONFIG_KEY`, database password, Reverb keys); start the
stack; migrate; create the WireGuard hub and print its public key; create your
Owner account; close the firewall down to 22, 80, 443 and 51820/udp.
It is safe to re-run: an existing `.env` is kept and the hub key is never
regenerated — doing so would silently disconnect every onboarded host.
### Unattended, from a bare server
The only thing to install by hand is git:
```bash
apt-get update && apt-get install -y git
git clone https://git.bave.dev/boban/CluPilotCloud.git /tmp/clupilot
bash /tmp/clupilot/deploy/install.sh --env-file /root/clupilot.env
```
`deploy/clupilot.env.example` is the template. Put the real file on the server
as `/root/clupilot.env` with mode 600 **before** starting — the installer warns
if it is readable by anyone else. Keep the master copy in a password manager,
never in the repository (`clupilot.env` is gitignored for that reason).
After a successful run the working values live in `/opt/clupilot/.env`, so the
bootstrap copy can go: `shred -u /root/clupilot.env`.
The file also carries the operational secrets — Hetzner DNS, Stripe, SMTP, the
Proxmox SSH key path. Those are optional: the install succeeds without them and
the corresponding features simply stay dark until they are filled in.
## Order of the update
Migrations run **before** the new containers take traffic: code that expects a
column the database does not have yet answers with 500s. The image is rebuilt
only when `docker/`, composer or npm manifests actually changed. Queue workers
are restarted at the end — they are long-running PHP processes and keep the old
classes in memory otherwise. That one bit us during development: a fixed job
kept failing until the worker was restarted.
## What the installer deliberately leaves to you
- **DNS and TLS.** Put a reverse proxy in front (Zoraxy, Caddy, nginx). The
console hostname must not be publicly resolvable: `/admin` answers only on
the hostnames in `ADMIN_HOSTS` and 404s everywhere else, but keeping it out
of public DNS is the layer above that.
- **`STRIPE_WEBHOOK_SECRET`.** Stripe dashboard → Developers → Webhooks →
`https://<app-domain>/webhooks/stripe`, events `checkout.session.completed`
and `checkout.session.async_payment_succeeded`. The secret starts with
`whsec_` and differs between test and live mode.
- **Backups.** At minimum the MariaDB volume and `/etc/wireguard` in the
`wireguard` volume — losing the hub key means re-peering every host.
## Hiding the site while you build
Settings → Website visibility. Outsiders then get a placeholder (503,
`noindex`), while anyone on the management VPN and any signed-in operator keeps
seeing the real thing. The console stays reachable either way, so the switch can
always be flipped back.