7.4 KiB
chocoadmin Deployment
1. Environment Files
Create environment files on the Synology NAS. Do not commit real .env.* files.
Stage file path:
/volume1/docker/service/jinaju/chocoadmin/.env.stage
Stage example:
DATABASE_URL="mysql://USER:PASSWORD@mariadb.jisangs.com:30001/chocomae"
AUTH_SECRET="replace-with-stage-secret"
NEXTAUTH_SECRET="replace-with-stage-secret"
AUTH_URL="https://stage-chocoadmin.jinaju.com"
NEXTAUTH_URL="https://stage-chocoadmin.jinaju.com"
Production file path:
/volume1/docker/service/jinaju/chocoadmin/.env.production
Production example:
DATABASE_URL="mysql://CHCOCO_ADMIN_USER:PASSWORD@chocomae.jinaju.com:3306/chocomae"
AUTH_SECRET="replace-with-production-secret"
NEXTAUTH_SECRET="replace-with-production-secret"
AUTH_URL="https://chocoadmin.jinaju.com"
NEXTAUTH_URL="https://chocoadmin.jinaju.com"
AUTH_SECRET and NEXTAUTH_SECRET must be the same strong random value within each environment for Auth.js compatibility. Use different values between production and stage.
APP_ENV (production / stage) is set by the docker-compose*.yml environment block — do not put it in the env file. It drives the session cookie name (${APP_ENV}-chocoadmin.session-token), which keeps production and stage cookies separate even if they share a browser.
When the public URL uses HTTPS, both AUTH_URL and NEXTAUTH_URL must use the exact external HTTPS URL.
Generate a secret on the NAS:
openssl rand -base64 32
2. Synology Stage Deployment
Both production and stage share the same Git checkout at /volume1/docker/service/jinaju/chocoadmin. Pull from there:
cd /volume1/docker/service/jinaju/stage-chocoadmin
git pull --ff-only
Stage uses docker-compose.stage.yml, the stage-chocoadmin container, and the external Docker network proxy-network.
Verify or create the network:
docker network inspect proxy-network >/dev/null 2>&1 || docker network create proxy-network
Start or update stage:
docker compose -f docker-compose.stage.yml up -d --build
docker compose -f docker-compose.stage.yml ps
docker compose -f docker-compose.stage.yml logs -f
If a service was renamed in the compose file, add --remove-orphans once to remove the stale container.
After redeployment, reload NPM to flush its upstream DNS cache:
docker exec npm nginx -s reload
docker-compose.stage.yml exposes container port 3000 to proxy-network instead of binding host port 3000, because host port 3000 may already be used by another service such as Gitea.
Nginx Proxy Manager settings:
- Scheme:
http - Forward Hostname / IP:
stage-chocoadmin - Forward Port:
3000 - Websockets Support: enabled
- SSL: enabled
- Force SSL: enabled
Stage URL:
https://stage-chocoadmin.jinaju.com
After changing AUTH_URL, NEXTAUTH_URL, or cookie-related settings, clear browser cookies for the stage domain or test in a private window.
3. Production Deployment
Production uses docker-compose.yml and reads .env.production by default:
cd /volume1/docker/service/jinaju/chocoadmin
git pull --ff-only
docker compose up -d --build
docker compose ps
docker compose logs -f chocoadmin
After redeployment, reload NPM:
docker exec npm nginx -s reload
Nginx Proxy Manager settings:
- Scheme:
http - Forward Hostname / IP:
chocoadmin - Forward Port:
3000 - Websockets Support: enabled
- SSL: enabled
- Force SSL: enabled
Production URL:
https://chocoadmin.jinaju.com
The Docker build uses placeholder build-time environment variables only so Next.js can compile without committing secrets. Runtime values are read from the compose env_file.
4. AWS EC2 Security Group
Allow MariaDB only from the Synology NAS public IP.
- Type:
MYSQL/Aurora - Protocol:
TCP - Port:
3306 - Source:
NAS_PUBLIC_IP/32 - Description:
chocoadmin Synology NAS
Do not open 3306 to 0.0.0.0/0.
5. Read-Only Rehearsal
Before enabling approval/rejection operations against production DB:
- Create or use a DB account with read-only permissions.
- Set
DATABASE_URLin.env.productionto that read-only account. - Start the container.
- Verify login, maestro list, extension request list, and upgrade request list.
- Switch to the production write-capable chocoadmin DB account only after read screens work.
6. Smoke Checks
After each deployment:
docker compose -f docker-compose.stage.yml ps # stage
docker compose ps # production
Verify these routes in the browser:
/login/maestros/extension-requests/upgrade-requests
7. Troubleshooting
ERR_TOO_MANY_REDIRECTS after login
Middleware redirects to /login, login page redirects back to / — infinite loop. Work through this checklist in order:
- Check
AUTH_URL/NEXTAUTH_URL— must exactly match the public HTTPS URL including scheme. - Check NPM scheme — NPM must forward with scheme
http(nothttps) to the container. The app detects HTTPS fromAUTH_URL, not from the incoming request. - Check cookie name consistency —
auth.ts(cookies.sessionToken.name) andproxy.ts(getToken({ cookieName })) must both use the same${APP_ENV}-chocoadmin.session-tokenvalue. Ifproxy.tsuses the default name whileauth.tsuses a custom one, middleware never finds the session. - Check for
__Secure-prefix — do not add it. Middleware runs in Node.js runtime and receives HTTP from Nginx.__Secure-cookies are silently rejected over HTTP. - Clear browser cookies for the domain, then test in a private window.
- Recreate the container after changing
.env.*.
Production and stage traffic mixing
Symptom: logging into chocoadmin.jinaju.com shows the stage UI, or requests hit both containers interchangeably.
Cause: Docker registers each services: key as a DNS alias in proxy-network. If stage and production share the same service name, NPM's upstream resolves to both containers via round-robin.
Check the network:
docker network inspect proxy-network --format '{{range .Containers}}{{.Name}} {{.IPv4Address}}{{"\n"}}{{end}}'
Expected: chocoadmin and stage-chocoadmin appear with different IPs. If chocoadmin appears twice, a stale container is using that alias.
Fix: ensure docker-compose.stage.yml has services: stage-chocoadmin: (not chocoadmin), redeploy stage once with --remove-orphans to clean up the stale container, then reload NPM.
Failed to find Server Action after redeployment
Symptom: clicking a button (e.g. logout) returns a 404 or Failed to find Server Action error after deploying a new build.
Cause: the action was defined as an inline "use server" closure. Closures get a new action ID on every build. The browser cached the old ID.
Fix: force-reload the page (Cmd+Shift+R / Ctrl+Shift+R) to discard the cached page with stale action IDs. If the problem recurs after every deployment, the action must be extracted to a module-level named export in a separate actions.ts file.
Container starts but immediately exits
docker compose logs chocoadmin
Common causes:
- Missing
DATABASE_URLor malformed connection string. AUTH_SECRETnot set — NextAuth throws on startup.- Port already allocated — check if another service uses host port 3000. Both compose files use
expose(notports) for port 3000, so this should not happen unless the compose file was modified.