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