This guide creates a controlled Development preview environment and reuses SQL Server, Redis, RabbitMQ, MinIO, and AgileConfig already installed in 1Panel. It does not create, upgrade, or remove those infrastructure containers. OpenResty serves the frontend; systemd runs Gateway, API, and JobHost.
Not production. Protect with password access, IP allowlist, or VPN. Production gates: Production configuration.
Access modes (config differs only in Origin / Endpoint / listen):
| Mode | Browser entry | Browser-reachable MinIO |
|---|---|---|
| Domain + HTTPS | https://app.example.com | https://files.example.com |
| IP only (home broadband) | http://<public-ip>:8088 (port 80 often blocked) | http://<public-ip>:9000 or reverse proxy |
Placeholders:
<PUBLIC_BASE>—https://app.example.comorhttp://10.10.10.10:8088<FILES_BASE>—https://files.example.comorhttp://10.10.10.10:9000- Environment name is fixed as
preview
Topology
Browser → <PUBLIC_BASE> └─ 1Panel OpenResty ├─ / → static frontend ├─ /api /hubs /health → 127.0.0.1:8082 └─ Gateway :8082 → API :8080 ├─ SQL / Redis / RabbitMQ (host-mapped ports) ├─ AgileConfig client └─ MinIO presign → <FILES_BASE>
systemd: bitzorcas-jobhost-preview :8081 bitzorcas-api-preview :8080 bitzorcas-gateway-preview :8082Rules:
- Bind API / JobHost / Gateway to
127.0.0.1only—do not expose 8080–8082 publicly. - JobHost owns 8081; do not use 8081 as the public OpenResty port (use 8088 on home broadband).
- Do not expose AgileConfig admin, MinIO Console, or SQL admin ports.
Server needs: .NET 10 Runtime, systemd, curl, unzip (frontend is built on the workstation).
1. Verify existing infrastructure
In 1Panel App Store → Installed, record host-mapped ports and credentials. systemd cannot use Docker-only service names.
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'sudo ss -lntp | grep -E ':(1433|6379|5672|9000|15000|80|8088)\b' || trueMinIO region must match the backend (example cn-east-1); CORS must allow PUT from <PUBLIC_BASE>.
2. One-time server setup
dotnet --list-runtimes
sudo mkdir -p /www/apps /www/releases /www/backups /www/scripts \ /www/sites/bitzorcas-preview /etc/bitzorcas /var/lib/bitzorcas/previewUpload scripts from the workstation:
# ① Copy the reviewed deployment scripts, environment template, and OpenResty snippets together.scp scripts/deploy/deploy.sh scripts/deploy/deploy-frontend.sh \ scripts/deploy/deploy-blue-green.sh scripts/deploy/deploy-rollback.sh \ scripts/deploy/setup-1panel-openresty.sh \ scripts/deploy/env.example \ deploy/1panel/openresty-app.locations.conf \ deploy/1panel/openresty-app.locations-blue-green.conf \ deploy/1panel/openresty-upstream.conf \ deploy/1panel/openresty-minio.locations.conf \ deploy@server:/tmp/# ② lib/common.sh must be uploaded separately; the blue-green scripts source it.scp -r scripts/deploy/lib deploy@server:/tmp/deploy-libInstall on the server:
# ① Install executables, secrets, and proxy fragments with deliberately different modes.sudo install -m 0755 /tmp/deploy.sh /www/scripts/deploy.shsudo install -m 0755 /tmp/deploy-frontend.sh /www/scripts/deploy-frontend.shsudo install -m 0755 /tmp/deploy-blue-green.sh /www/scripts/deploy-blue-green.shsudo install -m 0755 /tmp/deploy-rollback.sh /www/scripts/deploy-rollback.shsudo install -m 0755 /tmp/setup-1panel-openresty.sh /www/scripts/setup-1panel-openresty.shsudo install -m 0600 /tmp/env.example /etc/bitzorcas/preview.env# ② The blue-green trio requires /www/scripts/lib/common.sh; a missing copy fails immediately.sudo mkdir -p /www/scripts/1panel /www/scripts/libsudo install -m 0644 /tmp/deploy-lib/common.sh /www/scripts/lib/common.shsudo install -m 0644 /tmp/openresty-app.locations.conf /www/scripts/1panel/sudo install -m 0644 /tmp/openresty-app.locations-blue-green.conf /www/scripts/1panel/sudo install -m 0644 /tmp/openresty-upstream.conf /www/scripts/1panel/sudo install -m 0644 /tmp/openresty-minio.locations.conf /www/scripts/1panel/3. /etc/bitzorcas/preview.env
sudo nano /etc/bitzorcas/preview.envsudo chmod 600 /etc/bitzorcas/preview.envSkeleton (targets are 127.0.0.1 + 1Panel host ports):
ASPNETCORE_ENVIRONMENT=DevelopmentDOTNET_ENVIRONMENT=DevelopmentBITZORCAS_BIND_ADDRESS=127.0.0.1
ConnectionStrings__Default="Server=127.0.0.1,<SQL_PORT>;Database=bitzorcas;User Id=<USER>;Password=<PWD>;TrustServerCertificate=true;Encrypt=false"RabbitMq__Host=127.0.0.1RabbitMq__Port=<MQ_PORT>RabbitMq__User=<USER>RabbitMq__Password=<PWD>RabbitMq__VirtualHost=/Redis__Enabled=trueRedis__ConnectionString="127.0.0.1:<REDIS_PORT>,password=<PWD>"
Jwt__Secret=<openssl rand -base64 48>PiiEncryption__SearchHashKey=<openssl rand -base64 32>DataProtection__ApplicationName=bitzorcas-previewDataProtection__RedisKey=bitzorcas:dataprotection:preview
FileStorage__DefaultProvider=MinioFileStorage__S3__Endpoint=<FILES_BASE>FileStorage__S3__AccessKey=<AK>FileStorage__S3__SecretKey=<SK>FileStorage__S3__BucketName=bitzorcas-filesFileStorage__S3__Region=cn-east-1FileStorage__S3__UseSsl=trueFileStorage__S3__ForcePathStyle=trueFileStorage__S3__CreateBucketIfNotExists=true# IP + http only: UseSsl=false
# Must match the browser address bar exactly (scheme + host + port), or login returns Cookie Origin 403Frontend__BaseUrl=<PUBLIC_BASE>Cors__AllowAnyOrigin=falseCors__AllowedOrigins__0=<PUBLIC_BASE>Cors__AllowCredentials=true# HTTP preview:# Auth__WebRefreshCookie__Secure=false# Auth__WebRefreshCookie__SameSite=Lax
# Shared-preview documentation: require a product documentation session and retain outer defense in depthOpenApi__Enabled=trueOpenApi__RequireAuthentication=trueOpenApi__PersistAuthentication=falseOpenApi__Servers__0__Url=<PUBLIC_BASE>OpenApi__Servers__0__Description=1Panel-preview-entry
ForwardedHeaders__ForwardLimit=2ForwardedHeaders__KnownProxies__0=127.0.0.1Gateway__KnownProxies__0=127.0.0.1/8Gateway__DownstreamHealthCheckUrl=http://127.0.0.1:8080/health/live# Cluster ids must not contain hyphens (systemd drops api-cluster keys); deploy.sh also writes appsettings.Deploy.jsonReverseProxy__Clusters__api__Destinations__d1__Address=http://127.0.0.1:8080/ReverseProxy__Clusters__signalr__Destinations__d1__Address=http://127.0.0.1:8080/ReverseProxy__Clusters__files__Destinations__d1__Address=http://127.0.0.1:8080/
Licensing__Runtime__Enabled=trueLicensing__Runtime__PolicyId=community.small.v1Licensing__Runtime__Environment=developmentLicensing__Runtime__ProductId=bitzorcas-modernLicensing__Runtime__ProductVersion=1.0.0Licensing__Runtime__TenancyMode=multi-tenantLicensing__Runtime__DeploymentIdentityPath=/www/apps/preview/.bitzorcas/license/deployment-idLicensing__Runtime__OfflineLicensePath=/www/apps/preview/.bitzorcas/license/runtime-license.json
BITZORCAS_DISABLE_AGILECONFIG=0AgileConfig__Enabled=trueAgileConfig__AppId=modern-api-01AgileConfig__Secret=<SECRET>AgileConfig__Nodes=http://127.0.0.1:<AGILECONFIG_PORT>AgileConfig__Env=DEVAgileConfig__Tag=DEV
# ── Database init mode (important) ──# schema-only | platform-seed | full-seed# Day-to-day app deploys: tables/columns only, no seeds (including dictionaries)BITZORCAS_DEPLOY_DB_MODE=schema-only## Compat (used only when DEPLOY_DB_MODE is unset):# BITZORCAS_INIT_NO_SEED=1 # same as schema-only# BITZORCAS_SKIP_DEMO_SEED=1 # derives platform-seed (platform seeds still run)## First-time dictionaries without demo accounts:# BITZORCAS_DEPLOY_DB_MODE=platform-seed## Demo admin login:# BITZORCAS_DEPLOY_DB_MODE=full-seed# USER__ADMIN__PASSWORD=ChangeMe_Admin_1# USER__OPERATOR__PASSWORD=ChangeMe_Operator_1# USER__SUPPORT__PASSWORD=ChangeMe_Support_1# USER__AUDITOR__PASSWORD=ChangeMe_Auditor_1# USER__DEVELOPER__PASSWORD=ChangeMe_Developer_1# USER__HOST_ADMIN__PASSWORD=ChangeMe_HostAdmin_1# (plus HOST_PRODUCT / HOST_DEVELOPER / HOST_MARKETING / HOST_OPS)## Force full CodeFirst (default: catalog-driven partial table/column repair):# BITZORCAS_SCHEMA_FULL_INIT=1Generate deployment-id once:
# Create the stable seat-counting anchor once; never rotate it during an ordinary release.sudo mkdir -p /www/apps/preview/.bitzorcas/licenseif [[ ! -s /www/apps/preview/.bitzorcas/license/deployment-id ]]; then openssl rand -hex 16 | sudo tee /www/apps/preview/.bitzorcas/license/deployment-id >/dev/nullfisudo chmod 640 /www/apps/preview/.bitzorcas/license/deployment-id4. Pack and upload
4.1 First build: full package
# Use one version identifier for the backend and frontend artifacts in this release.VERSION="$(date +%Y%m%d-%H%M)"./scripts/deploy/pack.sh preview "$VERSION" --full./scripts/deploy/pack-frontend.sh preview "$VERSION"
BACKEND_PACKAGE="$(find ../publish -type f -name "bitzorcas-preview-${VERSION}.zip" -print -quit)"FRONTEND_PACKAGE="$(find ../publish -type f -name "bitzorcas-frontend-preview-${VERSION}.tar.gz" -print -quit)"scp "$BACKEND_PACKAGE" "$FRONTEND_PACKAGE" deploy@server:/www/releases/Windows PowerShell (UTF-8 scripts; console forced to UTF-8):
.\scripts\deploy\pack.ps1 -Env preview -Version $Version.\scripts\deploy\pack-frontend.ps1 -Environment preview -Version $Versionpack-frontend.sh on macOS now strips Apple xattrs, so Linux extract should not spam
LIBARCHIVE.xattr.com.apple.provenance. Do not fold the root deploy/ or ci/ trees into scripts/.
4.2 Later builds: incremental (server baseline required)
Incremental packs trust only a server-exported manifest. Building multiple local zips does not enable incremental mode.
# Pull the active server manifest before creating an incremental backend package.BRANCH="$(git -C . branch --show-current | tr '/' '_')"PUBLISH_DIR="../publish/${BRANCH}"
scp deploy@server:/www/apps/preview/manifest.txt \ "${PUBLISH_DIR}/manifest-server-preview-$(date +%m%d%H%M).txt"
VERSION="$(date +%Y%m%d-%H%M)"./scripts/deploy/pack.sh preview "$VERSION" # do not pass --fullSuccess looks like:
[INFO] server baseline selected: manifest-server-preview-20260923.txt[INFO] incremental result: 14 changed, 102 unchanged, 0 deletedThe full-package warning means no matching manifest-server-preview-*.txt was found because the name or environment is wrong, or the file is missing.
5. Deploy commands
5.1 Default platform-seed (platform seeds; demo users skipped)
When BITZORCAS_DEPLOY_DB_MODE is unset and INIT_NO_SEED is not 1:
sudo /www/scripts/deploy.sh /www/releases/bitzorcas-preview-<VERSION>.zip previewFlow: stop → backup → extract → load_env_file → derive platform-seed (legacy default SKIP_DEMO_SEED=1) → --init-schema (platform seeds still run) → --init-quartz-schema → systemd → health.
This is not “tables only”. Dictionaries and other platform seeds still execute. On success, installs /www/apps/preview/manifest.txt.
5.2 schema-only (no seeds — recommended for iterative deploys)
sudo BITZORCAS_DEPLOY_DB_MODE=schema-only \ /www/scripts/deploy.sh /www/releases/bitzorcas-preview-<VERSION>.zip previewCompat: BITZORCAS_INIT_NO_SEED=1. Or pin BITZORCAS_DEPLOY_DB_MODE=schema-only in preview.env.
Effect: --init-schema --no-seed (tables/columns + audit partitions + CAP; zero seeds). Schema defaults to catalog-driven partial repair.
5.3 full-seed (demo admin account)
- Set
BITZORCAS_DEPLOY_DB_MODE=full-seed(orSKIP_DEMO_SEED=0withoutINIT_NO_SEED=1) and allUSER__*__PASSWORDvalues. - Redeploy.
- Existing users are not password-reset by seed; only newly created accounts use the initial secrets.
5.4 Frontend
# Override the site root explicitly so the frontend release lands in the preview vhost.sudo BITZORCAS_SITE_ROOT=/www/sites/bitzorcas-preview \ /www/scripts/deploy-frontend.sh \ /www/releases/bitzorcas-frontend-preview-<VERSION>.tar.gz \ preview6. OpenResty
6.1 Site directory must be visible inside the container
deploy-frontend writes host path /www/sites/bitzorcas-preview. If OpenResty cannot see it, SPA rewrites may loop.
Mount the sites directory and set:
root /srv/sites/bitzorcas-preview/current;6.2 Proxy headers (required for login Cookie Origin)
Proxy /api/, /hubs/, /openapi/, and /scalar/ to Gateway. Every location uses the same public-address headers:
proxy_set_header Host $http_host;proxy_set_header X-Forwarded-Host $http_host;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-Forwarded-Port $server_port;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;$http_host retains a non-default port such as 8088; $host does not. In addition to /openapi/, /scalar/, and /api/docs/oauth/token, forward /api/docs/login, /api/docs/logout, and the compatibility aliases /docs/login and /docs/logout to the API. OpenApi__RequireAuthentication=true supplies the in-app documentation session; 1Panel access control, a VPN, or an IP allowlist remains the outer layer. If BITZORCAS_BIND_ADDRESS is set while this gate is off, the product Host refuses to start.
6.3 IP-only mode
- Prefer
listen 8088;when public port 80 is blocked. - Never bind public entry to 8081 (JobHost).
Frontend__BaseUrl, CORS, and MinIO CORS must include the port.
6.4 Include chain
The official image nginx.conf already includes conf.d inside http . Single-color publish does not need a separate upstream {} file. Do not edit the OpenResty app directory to add an upstreams/ include.
http { # conf.d is the host ${WEBSITE_DIR}/conf.d mount. include /usr/local/openresty/nginx/conf/conf.d/*.conf;}The site main file is ${WEBSITE_DIR}/conf.d/<site-alias>.conf. Keep the 1Panel-generated proxy / rewrite / ssl / waf includes inside server {}; do not delete existing panel lines. Full include-style example:
# Loaded by nginx.conf in http {}. Single-color publish must not include upstream/ here.
server { listen 80; listen 443 ssl http2; server_name app.example.com;
ssl_certificate /www/sites/app.example.com/ssl/fullchain.pem; ssl_certificate_key /www/sites/app.example.com/ssl/privkey.pem;
root /www/sites/bitzorcas-preview/current; index index.html;
# These includes are inside server {} and may only hold location / headers / extra TLS. include /www/sites/app.example.com/proxy/*.conf; include /www/sites/app.example.com/rewrite/*.conf; include /www/sites/app.example.com/ssl/*.conf; include /www/sites/app.example.com/redirect/*.conf; include /www/sites/app.example.com/auth/*.conf; include /www/sites/app.example.com/error_page/*.conf; include /www/sites/app.example.com/waf/*.conf;}When the panel creates a reverse-proxy site, each path lands in sites/<alias>/proxy/*.conf. Those files should use proxy_pass http://127.0.0.1:8082; (single-color) or the named upstream from the blue-green page. If you are not using panel reverse-proxy files, put the next full server {} in the site main file, or paste the location-only block into Custom config.
6.5 Complete server (site main file)
The next two files can replace or be compared with the site main file. Change root, server_name, and certificate paths to values visible inside the container. Do not listen 8081.
Domain + HTTPS:
# Host: ${WEBSITE_DIR}/conf.d/app.example.com.conf# Single-color deploy.sh: proxy_pass pins the blue Gateway.
server { listen 80; listen 443 ssl http2; server_name app.example.com;
ssl_certificate /www/sites/app.example.com/ssl/fullchain.pem; ssl_certificate_key /www/sites/app.example.com/ssl/privkey.pem;
root /www/sites/bitzorcas-preview/current; index index.html;
access_log /www/sites/app.example.com/log/access.log; error_log /www/sites/app.example.com/log/error.log;
if ($scheme = http) { return 301 https://$host$request_uri; }
location ^~ /.well-known/acme-challenge { allow all; root /usr/share/nginx/html; }
location /api/ { proxy_pass http://127.0.0.1:8082; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port; }
location /hubs/ { proxy_pass http://127.0.0.1:8082; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port; proxy_read_timeout 3600s; proxy_buffering off; }
location /health/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port; }
location /openapi/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; }
location /scalar/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; }
location /docs/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; }
location / { try_files $uri $uri/ /index.html =404; }}IP-only (home broadband often uses 8088):
# Host: ${WEBSITE_DIR}/conf.d/<public-ip>.conf# Listen 8088 when public 80 is taken. Do not listen 8081.# CORS / Frontend__BaseUrl must include the port, e.g. http://10.10.10.10:8088.
server { listen 8088; server_name <public-ip>;
root /www/sites/bitzorcas-preview/current; index index.html;
access_log /www/sites/bitzorcas-preview/log/access.log; error_log /www/sites/bitzorcas-preview/log/error.log;
location /api/ { proxy_pass http://127.0.0.1:8082; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port; }
location /hubs/ { proxy_pass http://127.0.0.1:8082; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port; proxy_read_timeout 3600s; proxy_buffering off; }
location /health/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port; }
location /openapi/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; }
location /scalar/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; }
location /docs/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port; }
location / { try_files $uri $uri/ /index.html =404; }}6.6 Server-level locations (custom config)
If you only edit 1Panel Custom config and do not replace the site main file, paste this block. It matches deploy/1panel/openresty-app.locations.conf. Change root to the frontend current path visible inside the container.
# Paste into the 1Panel site server block (single-color deploy.sh).# For blue-green, use openresty-app.locations-blue-green.conf and do not pin 8082.# Change root to BITZORCAS_SITE_ROOT/current used by deploy-frontend.sh.root /www/sites/bitzorcas-preview/current;index index.html;
location /api/ { proxy_pass http://127.0.0.1:8082; proxy_http_version 1.1; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port;}
location /hubs/ { proxy_pass http://127.0.0.1:8082; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port; proxy_read_timeout 3600s; proxy_buffering off;}
location /health/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Port $server_port;}
location /openapi/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port;}
location /scalar/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port;}
location /docs/ { proxy_pass http://127.0.0.1:8082; proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Port $server_port;}
location / { try_files $uri $uri/ /index.html =404;}7. Verify
# Verify all resident processes first, then probe internal and public readiness routes.systemctl is-active bitzorcas-jobhost-preview bitzorcas-api-preview bitzorcas-gateway-previewcurl -fsS http://127.0.0.1:8080/health/readycurl -fsS http://127.0.0.1:8080/health/licensecurl -fsS http://127.0.0.1:8082/health/readycurl -fsS <PUBLIC_BASE>/health/ready
# Anonymous JSON should return 401; a browser-style Scalar request should redirect to documentation login.curl -sS -o /dev/null -w '%{http_code}\n' <PUBLIC_BASE>/openapi/v1.jsoncurl -sS -o /dev/null -D - -H 'Accept: text/html' <PUBLIC_BASE>/scalar/v1Then open <PUBLIC_BASE>/scalar/v1 in a browser and confirm that Server retains the address-bar port. An ordinary account can select DocsLogin in the left Authentication panel. For MFA, CAPTCHA, MFA enrollment, or forced password change, complete formal login and paste its accessToken into Bearer.
If <PUBLIC_BASE> is still HTTP, use only a disposable development account on an isolated network. Configure HTTPS before DocsLogin is used with a real account; the Host’s internal RSA password adaptation is not a replacement for TLS.
8. Troubleshooting
| Symptom | Fix |
|---|---|
Every pack.sh is full | Missing manifest-server-preview-*.txt; download §4.2 baseline |
Login 403 Authentication.Cookie.OriginRejected | Cors__AllowedOrigins__0 / Frontend__BaseUrl must match the address bar; OpenResty must forward Host / X-Forwarded-Host; on HTTP set cookie Secure=false in Development |
Login 401 Identity.Login.InvalidCredentials | Demo seed not run or wrong password; see §5.3 |
admin requires PASSWORD_HASH | Keep platform-seed / SKIP_DEMO_SEED=1, or use full-seed with passwords; day-to-day deploys use schema-only |
Gateway ignores api-cluster env keys | Use cluster ids api / signalr / files; newer deploy writes appsettings.Deploy.json |
Gateway crash on CookieStickySession / RequestHeader | Deploy a package with the fixed Gateway appsettings.json |
/openapi or /scalar returns 404 | Confirm OpenApi__Enabled=true and forwarding for both paths in OpenResty and Gateway |
API startup says RequireAuthentication is mandatory | Keep BITZORCAS_BIND_ADDRESS and add OpenApi__RequireAuthentication=true |
| Scalar redirects to a 404 login page or loops | Forward /api/docs/login and /docs/login; verify the public scheme, trusted proxies, and bitzorcas.docs cookie |
| Scalar calls port 80/443 or the wrong host | Use $http_host, add X-Forwarded-Port $server_port, and verify OpenApi__Servers__0__Url |
| DocsLogin reports MFA, CAPTCHA, or forced password change | Use formal login and paste its accessToken into Bearer; do not disable the login policy |
| First empty-DB seed is slow | Expected; later use BITZORCAS_DEPLOY_DB_MODE=schema-only (or INIT_NO_SEED=1) |
| Default deploy still slow after tables exist | Default is platform-seed (dictionaries still run); set schema-only to skip all seeds |
journalctl -u 'bitzorcas-*-preview' -fUpdates and rollback
- Frontend only:
pack-frontend+deploy-frontend. - In-place backend:
pack(incremental when server baseline exists) +deploy.sh. - Blue-green backend: wire OpenResty first using 1Panel blue-green, then
deploy-blue-green.sh; roll back withdeploy-rollback.sh preview. - In-place backend rollback:
/www/backups/preview/; back up SQL before schema-changing releases.
See OpenAPI and Scalar documentation surface, Local Aspire orchestration, 1Panel OpenResty blue-green, Deployment methods, and Production configuration.