logicspike/docs

Blog Engine

Blog System — Deployment Guide

Last Updated: 2026-05-06 Status: Active

How to run the blog system locally and deploy it to production.


1. Prerequisites

Tool Version Purpose
Node.js 18+ Runtime for local dev
pnpm 8+ Package manager (Turborepo workspace)
Wrangler CLI 4.x Cloudflare Workers local dev + deploy
A Neon account — PostgreSQL database

2. Local Development

The blog system spans three services. In local dev they run on these ports:

Service Port Start command
seller-dashboard 3000 pnpm dev (from repo root, or turbo run dev)
gateway 8788 pnpm dev in apps/gateway
blog-service 8791 pnpm dev in apps/blog-service

Step-by-Step Setup

1. Install dependencies

pnpm install

2. Set up blog-service env

Create apps/blog-service/.env:

BLOG_DATABASE_URL=postgresql://user:password@hostname.neon.tech/blog?sslmode=require
CORE_DATABASE_URL=postgresql://user:password@hostname.neon.tech/main?sslmode=require

Create apps/blog-service/.dev.vars:

GATEWAY_SECRET=your-local-gateway-secret
DEBUG=true

BLOG_DATABASE_URL and CORE_DATABASE_URL need separate Neon databases (or branches). CORE_DATABASE_URL must point to the same project as manager-service's DATABASE_URL.

3. Run database migrations

cd apps/blog-service
npm run migrate

This applies all migrations in drizzle/ to your BLOG_DATABASE_URL database.

4. Set up gateway env

Create apps/gateway/.dev.vars:

JWT_PUBLIC_KEY=<RSA public key PEM — must match manager-service's JWT_PRIVATE_KEY>
GATEWAY_SECRET=your-local-gateway-secret
DATABASE_URL=postgresql://user:password@hostname.neon.tech/main?sslmode=require
DEBUG=true

GATEWAY_SECRET must be the same string in both gateway .dev.vars and blog-service .dev.vars.

5. Set up seller-dashboard env

Create apps/seller-dashboard/.env.local:

NEXT_PUBLIC_GATEWAY_URL=http://127.0.0.1:8788
NEXTAUTH_SECRET=any-stable-string
NEXTAUTH_URL=http://localhost:3000
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

6. Start all services

Either start everything together from the repo root:

turbo run dev

Or start each service in a separate terminal:

# Terminal 1
cd apps/gateway && pnpm dev
 
# Terminal 2
cd apps/blog-service && pnpm dev
 
# Terminal 3
cd apps/seller-dashboard && pnpm dev

7. Verify the setup

# blog-service health
curl http://localhost:8791/health
# → BLOG SERVICE OK
 
# blog-service env status (DEBUG mode only)
curl -H "x-gateway-key: your-local-gateway-secret" http://localhost:8791/debug
# → { service: "blog-service", status: "alive", env: { BLOG_DATABASE_URL_host: "..." } }
 
# Gateway → blog-service connectivity (DEBUG mode only)
curl -H "Authorization: Bearer <valid-jwt>" http://localhost:8788/blog/debug
# → { service: "blog-service", status: "alive" }

3. Environment Variable Reference

blog-service

Set in .env (loaded by wrangler) and .dev.vars (local secrets, gitignored):

Variable Required Where Description
BLOG_DATABASE_URL ✅ .env Neon connection string for the blog database
CORE_DATABASE_URL ✅ .env Neon connection string for the core/manager database
GATEWAY_SECRET ✅ .dev.vars Shared secret validated on every request from gateway
DEBUG ❌ .dev.vars Set to "true" for verbose request logging

Gateway

Set in .dev.vars (local) or Cloudflare Dashboard / wrangler secret put (production):

Variable Required Description
JWT_PUBLIC_KEY ✅ RSA public key (PEM) matching manager-service's signing key
GATEWAY_SECRET ✅ Shared secret injected into every service request
DATABASE_URL ✅ Core database for logging and API key validation
DEBUG ❌ Set to "true" for verbose proxy logging

Never commit .dev.vars to git. It is gitignored by wrangler by default. For production secrets use wrangler secret put <NAME>.

seller-dashboard

Set in .env.local (gitignored):

Variable Required Description
NEXT_PUBLIC_GATEWAY_URL ✅ Gateway URL — http://127.0.0.1:8788 for local, production URL for prod
NEXTAUTH_SECRET ✅ Signs NextAuth session cookies — must be stable across restarts
NEXTAUTH_URL ✅ NextAuth callback base URL
GOOGLE_CLIENT_ID ✅ OAuth — from Google Cloud Console
GOOGLE_CLIENT_SECRET ✅ OAuth — from Google Cloud Console

4. Production Deploy

blog-service

cd apps/blog-service
 
# Set production secrets (one-time per env)
wrangler secret put BLOG_DATABASE_URL
wrangler secret put CORE_DATABASE_URL
wrangler secret put GATEWAY_SECRET
 
# Deploy
npm run deploy
# → wrangler deploy --minify

The Worker name is logicspike-blog-service (from wrangler.toml). The gateway's service binding BLOG_SERVICE references this exact name — they must match.

Gateway

cd apps/gateway
 
# Set production secrets
wrangler secret put JWT_PUBLIC_KEY
wrangler secret put GATEWAY_SECRET
wrangler secret put DATABASE_URL
 
# Deploy
pnpm deploy

Production domain: api.vlozi.app (configured as custom_domain in wrangler.toml).

seller-dashboard

Deployed via Vercel. Set env vars in Vercel Dashboard:

  • NEXT_PUBLIC_GATEWAY_URL=https://api.vlozi.app
  • NEXTAUTH_SECRET, NEXTAUTH_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET

5. Service Binding

The gateway communicates with blog-service via a Cloudflare Service Binding — not HTTP. This means:

  • Zero network latency (in-process call)
  • No internet exposure for blog-service
  • In local dev: wrangler starts both services and connects them via the binding

The binding is declared in apps/gateway/wrangler.toml:

[[services]]
binding = "BLOG_SERVICE"
service = "logicspike-blog-service"

The service value must exactly match the name field in apps/blog-service/wrangler.toml.


6. Common Setup Issues

Symptom Cause Fix
Direct access forbidden. Use the gateway. blog-service received request without x-gateway-key or secret mismatch Verify GATEWAY_SECRET is identical in both .dev.vars files
Configuration error: GATEWAY_SECRET missing blog-service started without .dev.vars Create .dev.vars in apps/blog-service/
BLOG_DATABASE_URL not configured Missing database env Add BLOG_DATABASE_URL to apps/blog-service/.env
Gateway returns 503 / binding error blog-service not running Start blog-service first, then restart gateway
401 on all admin requests JWT_PUBLIC_KEY mismatch Gateway's public key must match manager-service's private signing key
Env changes not taking effect Next.js caches server-side env on startup Fully stop and restart the dev server (hot reload doesn't re-read env)
Blog works in prod but not locally NEXT_PUBLIC_GATEWAY_URL points to prod in .env.local Set it to http://127.0.0.1:8788 for local dev
Blog Engine