Updating and Troubleshooting

Updating an existing deployment

Rebuild on your build machine and replace the deployed files. Stop the service before copying so users aren't served a half-updated bundle.

Windows

nssm stop MyApp
# Copy the contents of web\.next\standalone\ into C:\apps\my-app\,
# then copy web\.next\static\ into C:\apps\my-app\.next\static\
# and web\public\ into C:\apps\my-app\public\.
nssm start MyApp

Linux

sudo systemctl stop my-app
sudo rsync -a --delete /tmp/new-build/ /opt/my-app/
sudo systemctl start my-app

Docker

docker build \
  --build-arg NEXT_PUBLIC_API_BASE_URL="https://api.example.com" \
  -t my-app:latest ./web
docker stop my-app && docker rm my-app
docker run -d --name my-app -p 3000:3000 --env-file ./web/.env.production --restart unless-stopped my-app:latest

A few seconds of downtime is normal during an update. For zero-downtime deployments, run two instances behind the reverse proxy and update them one at a time.


Troubleshooting

Symptom
Likely cause

Login works, then immediately bounces back to the login page

Cookie domain mismatch. The frontend and auth endpoint aren't on the same registrable domain. See Before You Deploy.

500 error on first page load

A NEXT_PUBLIC_* variable wasn't set at build time. These are baked into the browser bundle during next build. Setting them at run time has no effect — rebuild after changing them.

Mixed-content warnings in the browser console

A NEXT_PUBLIC_* variable points at http:// but the site is running over https://. All public URLs in production must be HTTPS.

Service won't start after a server reboot

Linux: sudo systemctl enable my-app wasn't run. Windows: the NSSM service start type isn't Automatic.

node: command not found when the service starts

Node 22 isn't installed, or the service environment has a different PATH than your interactive shell. Use the full path to node in your service file. See the which node note in the Linux guide.

API calls return CORS errors

The frontend and backend API are on different domains. Either put them under one domain via the reverse proxy, or configure your backend to send the appropriate CORS headers.

Certificate expired and the site is unreachable

Auto-renewal stopped silently. Linux: run sudo certbot renew --dry-run to test. Windows: check the win-acme scheduled task in Task Scheduler.

Last updated

Was this helpful?