Files
chocoadmin/docs/deployment.md
T

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://chocoadmin-stage.jisangs.com"
NEXTAUTH_URL="https://chocoadmin-stage.jisangs.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/chocoadmin-stage
git pull --ff-only

Stage uses docker-compose.stage.yml, the chocoadmin-stage 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: chocoadmin-stage
  • Forward Port: 3000
  • Websockets Support: enabled
  • SSL: enabled
  • Force SSL: enabled

Stage URL:

https://chocoadmin-stage.jisangs.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:

  1. Create or use a DB account with read-only permissions.
  2. Set DATABASE_URL in .env.production to that read-only account.
  3. Start the container.
  4. Verify login, maestro list, extension request list, and upgrade request list.
  5. 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:

  1. Check AUTH_URL / NEXTAUTH_URL — must exactly match the public HTTPS URL including scheme.
  2. Check NPM scheme — NPM must forward with scheme http (not https) to the container. The app detects HTTPS from AUTH_URL, not from the incoming request.
  3. Check cookie name consistencyauth.ts (cookies.sessionToken.name) and proxy.ts (getToken({ cookieName })) must both use the same ${APP_ENV}-chocoadmin.session-token value. If proxy.ts uses the default name while auth.ts uses a custom one, middleware never finds the session.
  4. 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.
  5. Clear browser cookies for the domain, then test in a private window.
  6. 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 chocoadmin-stage appear with different IPs. If chocoadmin appears twice, a stale container is using that alias.

Fix: ensure docker-compose.stage.yml has services: chocoadmin-stage: (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_URL or malformed connection string.
  • AUTH_SECRET not set — NextAuth throws on startup.
  • Port already allocated — check if another service uses host port 3000. Both compose files use expose (not ports) for port 3000, so this should not happen unless the compose file was modified.