Skip to content

Updating and Troubleshooting

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

Docker

Terminal window
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

Linux

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

Windows

An update replaces only the app files in the Node deployment folder (C:\apps\my-app\). Two things stay put:

  • Your .env. It lives on the server, not in the build, so don’t delete or overwrite it. If you’ve added a variable to web/.env.example since the last deploy, add it to the server’s .env now.
  • The IIS site and its web.config. They live in the separate site folder (C:\apps\my-app-site\), not the app folder, so you don’t re-run any of the IIS reverse-proxy steps when shipping a new build.
Terminal window
# 1. On your build machine, rebuild: cd web ; npm ci ; npm run build
# 2. Stop the service so users aren't served a half-updated bundle:
nssm stop MyApp
# 3. Delete the old build output so stale, hashed JS/CSS chunks don't linger.
# This removes only build output — it leaves .env and the logs folder in place:
Remove-Item -Recurse -Force "C:\apps\my-app\.next", "C:\apps\my-app\node_modules"
# 4. Copy the new build in, as in the first deploy:
# - contents of web\.next\standalone\ -> C:\apps\my-app\ (server.js at the root)
# - web\.next\static\ -> C:\apps\my-app\.next\static\
# - web\public\ -> C:\apps\my-app\public\
# 5. Start it again:
nssm start MyApp

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.


SymptomLikely cause
Login works, then immediately bounces back to the login pageCookie domain mismatch. The frontend and auth endpoint aren’t on the same registrable domain. See Before You Deploy.
500 error on first page loadA 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 consoleA 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 rebootLinux: sudo systemctl enable my-app wasn’t run. Windows: the NSSM service start type isn’t Automatic.
Windows: site loads but every request returns HTTP 502.3 (or a blank IIS error)ARR’s proxy engine is off, so IIS has nothing to forward requests to. Enable it once at the server level: Application Request Routing Cache → Server Proxy Settings → Enable proxy. See Set up IIS as the reverse proxy.
Windows: HTTP 500.19 — “configuration section … is locked at a parent level”Your site web.config contains a <proxy> element, which is a server-level setting and can’t live in a site file. Remove it and enable the proxy at the server level instead. See Set up IIS as the reverse proxy.
Windows: HTTP 500 — “The specified server variable … is not allowed here”The rewrite rule sets X-Forwarded-* headers that aren’t on URL Rewrite’s allowed list. Add HTTP_X_FORWARDED_PROTO and HTTP_X_FORWARDED_FOR under your site’s URL Rewrite → View Server Variables. See Set up IIS as the reverse proxy.
node: command not found when the service startsNode 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 errorsThe 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 unreachableAuto-renewal stopped silently. Linux: run sudo certbot renew --dry-run to test. Windows: check the win-acme scheduled task in Task Scheduler.