Hostinger Deployment Guide¶
🏗️ How Deployment Actually Works¶
Deployment is fully automated CI/CD — there is no manual VPS/PM2/nginx setup. The two services live in different places:
- Frontend + docs → built by GitHub Actions and served by Hostinger
(static site) at https://pcbgenerator.com (docs at
/docs/). - FastAPI backend → runs on Render
(
https://ai-assisted-pcb-stackup-generator.onrender.com); the frontend is built to call it viaVITE_API_URL.
There is no Node.js backend, no Ollama, no PM2, and no nginx in the current architecture. AI chat is served by the FastAPI backend over SSE.
The pipeline is defined in
.github/workflows/deploy-hostinger.yml.
🚀 The Deploy Flow¶
push to main (backend/**, frontend/**, docs/**, mkdocs.yml, deps, or the workflow)
│
▼
┌──────────────────────┐ pytest backend/tests/ must pass
│ Job 1: test-backend │ (installs CPU-only PyTorch + backend deps)
└──────────┬───────────┘
│ success or skipped
▼
┌──────────────────────────────────────────────────────────┐
│ Job 2: build-and-deploy │
│ • npm ci + `npx vitest run` (full suite gates the deploy)│
│ • npm run build (VITE_API_URL → Render backend) │
│ • mkdocs build (docs site) │
│ • assemble deploy_out/ (frontend at /, docs at /docs) │
│ • write deploy-info.json │
│ • force-push orphan commit → `hostinger-deploy` branch │
│ • curl RENDER_DEPLOY_HOOK_URL (triggers backend deploy) │
└──────────┬───────────────────────────────────────────────┘
▼
Hostinger pulls `hostinger-deploy` → public_html (its Git auto-deploy)
Render rebuilds the FastAPI backend (deploy hook)
Why the branch hand-off? GitHub-hosted runners are blocked from Hostinger's
FTP (21) and SSH/SFTP (65002) ports, so the runner never connects to Hostinger
directly — it publishes the built tree to the hostinger-deploy branch and
Hostinger's Git integration pulls it into public_html.
✅ Triggering a Deploy¶
- Automatic: push to
maintouchingbackend/**,frontend/**,docs/**,mkdocs.yml,backend/requirements*.txt,frontend/package*.json, or the workflow file itself. - Manual: run the Deploy Frontend to Hostinger workflow via
workflow_dispatch(Actions tab → Run workflow).
A push that only changes unrelated files (e.g. .claude/, root scripts) does
not trigger a deploy.
🔑 Required GitHub Secrets¶
Set these in the repo's Settings → Secrets and variables → Actions:
| Secret | Used for |
|---|---|
VITE_SUPABASE_URL, VITE_SUPABASE_ANON_KEY |
Frontend auth/DB at build time |
VITE_POSTHOG_KEY, VITE_POSTHOG_HOST, VITE_SENTRY_DSN |
Observability (dormant no-ops when unset) |
RENDER_DEPLOY_HOOK_URL |
Triggers the Render backend deploy |
GITHUB_TOKEN |
Force-push the built site to hostinger-deploy (auto-provided) |
Feature flags are baked into the build by the workflow (AI-native shell,
standalone impedance/link-budget/antenna, planar filter, thermal management,
chat prompts/auto-open all =true). Change them in the Build frontend step
of the workflow, not in a server .env.
🔍 Verifying What's Live¶
The build writes a web-servable marker at the site root:
curl https://pcbgenerator.com/deploy-info.json
# -> {"commit":"<sha>","ref":"main","built":"<UTC>","run_id":"<id>"}
Compare commit against the main HEAD you expect. Then spot-check:
- Frontend: https://pcbgenerator.com
- Docs: https://pcbgenerator.com/docs/
- Backend health:
https://ai-assisted-pcb-stackup-generator.onrender.com/health
🔐 Hostinger Git Integration (one-time)¶
Hostinger pulls the hostinger-deploy branch via its Git auto-deploy feature.
The SSH key/connection setup for that integration is covered separately:
Mailbox/alias setup for the support@pcbgenerator.com address is in
HOSTINGER_EMAIL_SETUP.md.
📥 Feedback Storage¶
User feedback (thumbs up/down, comments) is written by the FastAPI backend as
JSONL, one file per UTC day: backend/data/feedback/feedback_YYYY-MM-DD.jsonl
(each line is one submission: source, trigger, rating, comment,
context, ts, ip). Because the backend runs on Render, this lives on
the Render service's filesystem — attach a Render persistent disk (or export
periodically) if you need it to survive redeploys, since a service's ephemeral
disk is reset on each deploy. The write path is POST /api/feedback; point it
at a DB/Supabase later if durable storage is needed.
🛠️ Troubleshooting¶
- Deploy didn't run — the push didn't touch a watched path, or it landed on
a branch other than
main. Use the manualworkflow_dispatchtrigger. - Deploy ran but site unchanged — check
deploy-info.json'scommit; if it matches, Hostinger may not have pulled yet (its Git integration polls) — give it a minute or trigger a pull from hPanel. - Backend errors after deploy — the frontend deployed but the Render deploy
hook failed or Render is cold-starting; check the Render dashboard and
/health. See ../TROUBLESHOOTING.md. - Tests blocked the deploy —
test-backend(pytest) or the frontendvitest rungate failed; the deploy correctly does not publish. Fix the failing test and re-push.