kitdocs · github

Skill: ship-a-site

raw markdown skills/ship-a-site/SKILL.md

Two jobs, very different sizes:

Situation Path Time
New site/app on a server that already exists Fast path below ~10–30 min
Brand-new server (fresh box, a client) references/new-server.md ~2 hours

If the user has a filled-in references/server-profile.md (copied from server-profile.template.md), read it first — real hostnames, paths and ports instead of guesses. Never write the user's real IPs, tokens or secrets into files that get committed or published.

Verify rather than assume: dig, ss -tlnp, systemctl, nginx -t before acting.


Before anything: know which machine you are on

Cloudflare's proxy carries HTTP/HTTPS only, so ssh user@domain stops working once a domain is proxied. Use the IP through an SSH alias (ssh <server>).

Label every command you give the user with where it runs — laptop or server.


Fast path: a new site on an existing server

1. DNS

A <sub> → <server-ip>. Proxied is correct immediately when a wildcard origin certificate covers *.<base-domain>. Grey cloud only when the origin has no cert yet (proxying then gives 5xx everywhere). Check: dig +short <domain> @1.1.1.1.

2. What is behind it

3. Nginx

sudo scripts/add-site.sh <domain> --proxy <app-port> or --static <web-root> — writes the block, runs nginx -t, reloads only on success. Then include the shared snippets and caching rules from references/nginx-blocks.md. Always sudo nginx -t && sudo systemctl reload nginx.

4. Service (apps)

scripts/app.service.template → /etc/systemd/system/<app>.service: node --max-old-space-size=128 dist/index.js, MemoryMax=256M, Restart=always. Never tsx, ts-node, nodemon, a dev server or npm start in production. Prove it: pkill -9 -f "[d]ist/index.js" → new PID; reboot → comes back alone. Details: references/systemd-units.md.

5. Deploy script

scripts/deploy.sh.template → the app repo's scripts/deploy.sh: tests → build on the laptop → back up DB → rsync → npm ci --omit=dev → migrate → publish web via a staging dir → restart → health check → check from outside → print memory. Backup comes before migrating; no auto-rollback (print the rollback command instead); fail closed if any build fails.

6. Verify from outside

curl -s -o /dev/null -L -w "%{http_code} tls=%{ssl_verify_result} http/%{http_version}\n" https://<domain>/
curl -s -o /dev/null -w "%{http_code} -> %{redirect_url}\n" http://<domain>/
curl -sI https://<domain>/index.html | grep -ciE 'x-content-type|x-frame|referrer|strict-transport'

Want 200 tls=0 http/2, 301, and 4.

7. Backups (apps with a DB)

scripts/backup-db.sh <app> nightly from cron, scripts/restore-db.sh <app> drill once now and monthly, scripts/pull-backups.sh on the laptop via launchd (not macOS cron). Measure memory after the deploy and again a day later.


When something is stale or wrong

Work outwards: service worker → browser → Cloudflare edge → origin. curl -sI https://<domain>/file | grep -i cf-cache-status rules out two layers. Compare with the origin directly: curl -sI -k --resolve <domain>:443:<server-ip> https://<domain>/file.

DNS looks wrong but dig is right → OS and browser caches, not the server.

For any error text, search references/gotchas.md first.


Costs

State recurring costs as per month, until removed. Snapshots bill monthly per snapshot forever — take one only right before a risky change and delete it once proven. Prefer the provider's flat-price automated backups. Before upgrading a server, measure (free -m, per-service MemoryCurrent); if resizing, CPU/RAM only — disk resizes are permanent.


Files

File Use it when
references/server-profile.template.md copy to server-profile.md and fill in once per server (keep it private)
references/new-server.md building a server from scratch
references/nginx-blocks.md writing server blocks, headers, caching, PWA, real-IP
references/systemd-units.md turning an app into a capped service
references/gotchas.md something is broken, or before touching SSH/Postgres/TLS/caching
scripts/add-site.sh server: create + test + reload an Nginx site
scripts/app.service.template the systemd unit
scripts/deploy.sh.template the app repo's one-command deploy
scripts/backup-db.sh / restore-db.sh server: nightly dump / restore drill / real recovery
scripts/pull-backups.sh + .plist.template laptop: off-machine copy via launchd