SKILL.md
Ghost Publisher
Ghost is the migration target when a publication outgrows hosted platforms: full SEO control, 0% platform fee (payment processing only), your domain, your audience data. Do not migrate before the numbers justify the ops burden.
Migrate or stay
Recommend Ghost when at least one holds:
- Paid subscriber revenue makes a ~10% platform fee materially painful (typically past ~50 paid subscribers).
- You need SEO control a hosted platform will not give you: canonicals, meta, structured data, sitemap.
- You need to consolidate content on your own domain, or you need audience data portability with no platform lock-in.
None hold: stay on the hosted platform, revisit quarterly. Setup cost is real and recurring.
Deploy
- Run Ghost as a long-lived HTTP service on port 2368 behind your reverse proxy, on whatever host you operate ($WORKDIR, VPS, or container platform).
- Point your custom domain at it, force HTTPS.
- Configure SMTP for transactional mail and newsletter delivery.
- For paid memberships, connect Stripe in Ghost admin. You pay processing (~2.9%), no platform cut.
Publish via Admin API
- Create an Admin API integration in Ghost admin, store the key as
$GHOST_ADMIN_API_KEY, never in the repo. - Post content:
curl -X POST "https://<YOUR_DOMAIN>/ghost/api/admin/posts/?source=html" \
-H "Authorization: Ghost $GHOST_JWT" \
-H "Content-Type: application/json" \
-d '{"posts":[{"title":"<TITLE>","html":"<BODY_HTML>","status":"draft"}]}'Note: the Admin API takes a short-lived JWT signed with the API key, not the raw key. Generate it per request.
- Always create as
draft, review in Ghost admin, then publish. Newsletter send happens at publish time when the post is set to email, so a bad autopublish is an unrecallable send.
Good vs bad
Bad: 30 free subscribers, zero paid, and you spend a week self-hosting Ghost for "control", then another week a month patching it. The publication gains nothing.
Good: 80 paid subscribers on a hosted platform taking 10%. You deploy Ghost, migrate content with redirects mapped, connect Stripe, and the fee saving pays the hosting bill several times over.
Verification
After deploy or publish, run curl -s -o /dev/null -w "%{http_code}" https://<YOUR_DOMAIN>/ghost/api/admin/site/ with a valid JWT. Expect 200 and, for a publish, the post visible at its public URL with correct canonical and meta tags in view-source. If not, check the reverse proxy and the JWT generation before touching Ghost config.
Completion checklist
- [ ] Migration decision backed by the three criteria, not preference
- [ ] HTTPS on the custom domain, admin reachable
- [ ] SMTP configured and a test mail delivered
- [ ] Stripe connected if paid tiers exist
- [ ] API posts created as drafts, published only after review
- [ ] Redirects mapped for every migrated URL
Any box unchecked: not done. Fix or say so.
Footguns
- Publishing straight to `published` via API. If the post is set to email members, the send fires immediately and cannot be recalled. Fix:
status: "draft"always, publish from admin. - Migrating without redirects. Old platform URLs 404 and rankings evaporate. Fix: map every old URL to its new path in Ghost's redirects file before switching DNS.
- Raw API key in Authorization. The Admin API rejects it or, worse, you leak the key in logs. Fix: sign a short-lived JWT from the key per request.