Getting a Next.js frontend and FastAPI backend running cleanly across Dev, QA, and Production on a shared VPS โ untangling JWT secrets, Nginx routing conflicts, cookie stripping, and Node.js version hell.
RoleDevOps & Backend Engineer
PlatformHostinger VPS ยท Nginx ยท PM2
DomainHealthcare SaaS
TypeMulti-Env Deployment & DevOps
The Problem
Three environments, one VPS, and nothing talking to each other correctly
CareJourney needed Dev, QA, and Production environments running simultaneously on a single Hostinger VPS. The initial deployment had authentication completely broken โ users could log in but were immediately redirected back to the login page on every page load.
Three independent bugs compounding each other: mismatched JWT secrets, Nginx stripping Set-Cookie headers, and Node.js v22 conflicting with the system Node.js v18 used by PM2.
Diagnosis & Fixes
Peeling the onion โ three bugs masking each other
1
Node.js version conflict โ PM2 running wrong binaryPM2 installed under system Node v18 but the app required v22. Fixed by installing PM2 inside nvm-managed v22 and updating ecosystem.config.js to lock the runtime path.
2
JWT secret mismatch between Next-Auth and FastAPINext-Auth signed JWTs with NEXTAUTH_SECRET; FastAPI read a different JWT_SECRET. Fixed by unifying to a single shared secret injected via GitHub Actions secrets on every deploy.
3
Nginx stripping Set-Cookie on proxied auth responsesNext-Auth issues session cookies via Set-Cookie headers that Nginx was dropping on proxy. Fixed with proxy_cookie_domain and proxy_cookie_path directives.
4
Routing conflict โ /api/auth hijacked before Next.js handled itNginx forwarded ALL /api/* to FastAPI, but /api/auth/* belongs to Next-Auth. Fixed by adding a specific location block for /api/auth/ pointing to the Next.js port, placed before the generic /api/ block.