Deployment
HeadlessCore splits into two deployable units: WordPress (backend) and Next.js (frontend).
Architecture overview
┌─────────────────┐
Editors ────────▶│ WordPress │
│ (PHP + MySQL) │
└────────┬────────┘
│ WPGraphQL (HTTPS)
▼
┌─────────────────┐
Visitors ───────▶│ Next.js │
│ (Vercel/Node) │
└─────────────────┘
▲
│ POST /api/revalidate
WordPress webhooks
WordPress hosting
Any PHP 8+ host with MySQL:
- Shared hosting (cPanel, Plesk)
- VPS (DigitalOcean, Linode, Hetzner)
- Managed WP (optional — you only need admin + GraphQL, not public theme)
Checklist
- HTTPS enabled
- HeadlessCore theme active
- WPGraphQL + JWT plugins active
- Permalinks set to "Post name"
- Next.js URL set in theme settings
- Revalidation + preview secrets configured
- Contact webhook URL set (optional)
Hardening
See Security Guide and Production — aaru.live.
- Restrict
/wp-adminby IP or Cloudflare Access - Disable XML-RPC if unused
- Keep WordPress core, plugins, and PHP updated
- Use strong secrets (
openssl rand -base64 32)
Next.js hosting
Recommended: Vercel (zero-config for Next.js App Router).
Alternatives: Netlify, Railway, DigitalOcean App Platform, self-hosted Node.
Environment variables (production)
NEXT_PUBLIC_WORDPRESS_API_URL=https://cms.yourdomain.com/graphql
NEXT_PUBLIC_WORDPRESS_URL=https://cms.yourdomain.com
NEXT_PUBLIC_SITE_URL=https://yourdomain.com
REVALIDATION_SECRET=<same as WordPress>
PREVIEW_SECRET=<same as WordPress>
WORDPRESS_AUTH_TOKEN=<JWT for draft preview>
Build command
cd nextjs-starter-kit && npm ci && npm run build
Custom domain
Point yourdomain.com to Vercel. WordPress can live on cms.yourdomain.com or a subdomain.
Update next.config.ts images.remotePatterns if WordPress is on a different domain.
Revalidation in production
WordPress must reach your Next.js /api/revalidate endpoint. On Vercel this is automatic.
headlesscore_nextjs_url=https://yourdomain.com- Firewall allows outbound HTTPS from WordPress host to Next.js
REVALIDATION_SECRETmatches on both sides
Test after deploy:
curl -X POST https://yourdomain.com/api/revalidate \
-H "Content-Type: application/json" \
-d '{"secret":"YOUR_SECRET","path":"/blog","tag":"posts"}'
See API Routes and Troubleshooting.
Preview mode
Preview links require:
PREVIEW_SECRETset in Next.js (matches WordPress)WORDPRESS_AUTH_TOKEN(JWT) for authenticated draft GraphQL queries
Generate JWT via WPGraphQL login mutation with the JWT Authentication plugin.
CI/CD
HeadlessCore includes a GitHub Actions workflow at .github/workflows/ci.yml.
What runs on push/PR to main:
npm ciinnextjs-starter-kit/npm run lintnpm test(Vitest — 9 unit tests)npm run build
Suggested production pipeline
# .github/workflows/deploy.yml (example)
name: Deploy Next.js
on:
push:
branches: [main]
jobs:
test-and-build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: nextjs-starter-kit/package-lock.json
- run: cd nextjs-starter-kit && npm ci
- run: cd nextjs-starter-kit && npm run lint
- run: cd nextjs-starter-kit && npm test
- run: cd nextjs-starter-kit && npm run build
env:
NEXT_PUBLIC_WORDPRESS_API_URL: ${{ secrets.WP_GRAPHQL_URL }}
NEXT_PUBLIC_WORDPRESS_URL: ${{ secrets.WP_URL }}
NEXT_PUBLIC_SITE_URL: ${{ secrets.SITE_URL }}
deploy:
needs: test-and-build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Deploy to Vercel, Netlify, or your host
WordPress deploys via git pull, rsync, or host-specific pipeline.
See Testing for test details.
Docker (dev/staging)
docker compose up -d
- WordPress:
http://localhost:8080 - Theme volume-mounted from repo
For production, use separate managed services instead of Docker Compose.
Docker Compose in production (Coolify or any Docker host)
docker-compose.prod.yml at the repo root deploys WordPress as a
production-ready container. Point a Coolify "Docker Compose" resource at
it (or docker compose -f docker-compose.prod.yml up -d on any Docker
host), with env vars from .env.production.example.
Git auto-deploy on Coolify: connect GitHub, enable Auto Deploy on both the Docker Compose (CMS) and Nixpacks (Next.js) resources. See Coolify Deploy for the full aaru.live setup (repo root build, env vars, two-resource layout).
Remote MySQL
On a small VPS (e.g. a 2GB Linode), running MySQL as a container next to
WordPress competes with it for RAM. If you'd rather offload the database —
to a managed MySQL service (Linode Managed Database, RDS, etc.) or a
separate small VPS running just MySQL — use
docker-compose.prod.remote-db.yml instead. It's identical to
docker-compose.prod.yml minus the local db service; WordPress connects
out to REMOTE_DB_HOST (accepts host:port) instead.
docker compose -f docker-compose.prod.remote-db.yml up -d
Fill in the REMOTE_DB_* vars in .env.production.example (host, user,
password, database name) instead of the local MYSQL_* ones. Two things
to check with your provider before switching:
- Firewall / allowed IPs — the remote MySQL must accept connections from your WordPress host's IP.
- TLS — most managed MySQL requires SSL. The official WordPress Docker
image supports this via
mysqli_ssl_set()in aWORDPRESS_CONFIG_EXTRAsnippet (see thewordpress:phpimage docs) if your provider mandates it; many allow non-SSL connections from an allow-listed IP as a simpler alternative for a single-server setup.
In Coolify: create the resource the same way, just set the Compose file
path to docker-compose.prod.remote-db.yml and use the REMOTE_DB_* env
vars instead of MYSQL_*.
Monitoring
| Layer | What to monitor |
|---|---|
| Next.js | Vercel Analytics, error tracking (Sentry) |
| WordPress | Uptime on /graphql endpoint |
| Revalidation | Test by publishing a post after deploy |
| Contact form | Check Contact Logs CPT + webhook delivery |
vs WP Engine Atlas
| HeadlessCore | WP Engine Atlas | |
|---|---|---|
| Hosting | Self-hosted | Managed |
| Cost | Server only | Platform fees |
| WP admin | Standard | Standard |
| Frontend | Next.js (you own) | Faust.js / Next.js |
| Vendor lock-in | None | Platform-dependent |
| Site modes | 8 built-in | Custom per project |
| CI/tests | Vitest + GitHub Actions | Platform-dependent |
You get the same decoupled architecture with full control over code and infrastructure.