Hosting documentation on a VPS (edox-ops)
GitLab Pages is the default for this repository (see
pages-domain.md).
Use this runbook when you move the same static build (website/build/) to a VPS you
manage with edox-ops — Ubuntu 24.04, Debian 12+, or a Debian derivative (e.g. Raspberry
Pi OS).
Pages vs VPS
| GitLab Pages | VPS + edox-ops | |
|---|---|---|
| Ops | Push master / develop; CI builds and publishes | You run bootstrap, hardening, deploy, TLS on the host |
| Content | CI artifact website/build/ | Same tree — rsync from CI download or local make docs |
| TLS | GitLab Let's Encrypt (custom domain) | Certbot via edox-ops certs / project certs issue |
| Hardening | GitLab-managed edge | host harden, firewall, fail2ban, nginx templates |
The Docusaurus build uses baseUrl: / and a public DOCS_SITE_URL. Rebuild with the
production hostname before deploy if the URL changes:
export DOCS_SITE_URL=https://docs.example.com
export DOCS_SITE_BASE_URL=/
python scripts/assemble_docs.py # or download artifact from CI docs:build
Prerequisites
- Fresh Ubuntu 24.04 or Debian 12+ VPS (SSH as root or sudo)
- DNS A / AAAA (or CNAME) pointing at the VPS for the docs hostname
- Python 3.11+ on the VPS with edox-ops installed (
pip install edox-ops; see Installation), or deploy from a workstation with SSH + rsync source on the server
Phase 1 — Host baseline
edox-ops bootstrap --yes
edox-ops host harden --yes # security-updates + UFW + fail2ban
edox-ops host logrotate --yes # optional: /var/log/edox-ops/*.log
# edox-ops host ssh-harden --yes # optional; test SSH in a second session first
edox-ops doctor
host harden stores ssh_port in /etc/edox-ops/host.json (default 22). Use
--ssh-port if sshd listens elsewhere. Match the port on firewall and fail2ban.
On a dry run first:
edox-ops host harden --yes --dry-run
Phase 2 — TLS tooling
For HTTPS vhosts (project add --tls / project update --tls):
edox-ops certs install --yes
edox-ops certs setup-auto-renew --yes # certbot.timer + nginx reload hook
Issue a certificate after the site is reachable on HTTP (see phase 3–4).
Phase 3 — Register the docs site
Example project slug edox-ops-docs serving https://docs.example.com:
edox-ops project add edox-ops-docs \
--domain docs.example.com \
--root-path /var/www/docs/edox-ops-docs
edox-ops project init edox-ops-docs
Place the static build on the VPS (pick one):
| Source | Command |
|---|---|
| CI artifact | Download website/build/ from job docs:build → /opt/edox-ops-docs-build/ |
| Local build | rsync / scp website/build/ to the VPS |
edox-ops project update edox-ops-docs --source /opt/edox-ops-docs-build
edox-ops project deploy edox-ops-docs --yes
edox-ops project enable edox-ops-docs
edox-ops project validate edox-ops-docs
deploy creates a timestamped backup under /var/www/docs/.edox-ops-backups/<slug>/ before
rsync --delete. Nginx vhosts include rate limiting, security headers, and static-only
methods (GET/HEAD) — no extra CLI step.
Phase 4 — HTTPS
edox-ops project certs issue edox-ops-docs --email you@example.com --yes
edox-ops project update edox-ops-docs --tls
edox-ops project enable edox-ops-docs
edox-ops doctor
curl -fsS https://docs.example.com/docs/intro/
curl -fsS https://docs.example.com/api/index.html
Phase 5 — Prove recovery (once)
edox-ops project backups list edox-ops-docs
# After a test deploy, roll back:
edox-ops project restore edox-ops-docs --yes
edox-ops project validate edox-ops-docs
Ongoing updates
- Build docs (
docs:buildartifact orpython scripts/assemble_docs.pylocally). - Sync the new
website/build/tree to the VPS source directory. edox-ops project deploy edox-ops-docsedox-ops project validate edox-ops-docsand spot-check URLs.
Optional: automate step 1–3 from CI with SSH/rsync (out of scope for edox-ops core; use your own deploy job).
Troubleshooting
| Symptom | Check |
|---|---|
doctor fails host:firewall | edox-ops host firewall --yes; UFW allows SSH/80/443 |
| Certbot HTTP-01 fails | Site enabled on port 80; DNS points to this host; /.well-known reachable |
404 on /api/ | Deploy included full website/build/ (with api/ subtree), not Docusaurus-only |
| Wrong absolute links | Rebuild with correct DOCS_SITE_URL before deploy |
Related
- Product ops: Operations (backups, hardening commands)
- Build pipeline:
documentation-build.md - Pages (default):
pages-domain.md