Skip to content
bitzorcas
中EN

Guide

1Panel Linux Development Preview

Deploy a BitzOrcas Development preview on Linux with 1Panel, OpenResty, SQL Server, Redis, RabbitMQ, MinIO, and AgileConfig—including pack, deploy, seed flags, and login troubleshooting.

Last updated

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):

ModeBrowser entryBrowser-reachable MinIO
Domain + HTTPShttps://app.example.comhttps://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.com or http://10.10.10.10:8088
  • <FILES_BASE> — https://files.example.com or http://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 :8082

Rules:

  • Bind API / JobHost / Gateway to 127.0.0.1 only—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.

Terminal window
sudo docker ps --format 'table {{.Names}}\t{{.Status}}\t{{.Ports}}'
sudo ss -lntp | grep -E ':(1433|6379|5672|9000|15000|80|8088)\b' || true

MinIO region must match the backend (example cn-east-1); CORS must allow PUT from <PUBLIC_BASE>.

2. One-time server setup

Terminal window
dotnet --list-runtimes
sudo mkdir -p /www/apps /www/releases /www/backups /www/scripts \
/www/sites/bitzorcas-preview /etc/bitzorcas /var/lib/bitzorcas/preview

Upload scripts from the workstation:

Terminal window
# ① 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-lib

Install on the server:

Terminal window
# ① Install executables, secrets, and proxy fragments with deliberately different modes.
sudo install -m 0755 /tmp/deploy.sh /www/scripts/deploy.sh
sudo install -m 0755 /tmp/deploy-frontend.sh /www/scripts/deploy-frontend.sh
sudo install -m 0755 /tmp/deploy-blue-green.sh /www/scripts/deploy-blue-green.sh
sudo install -m 0755 /tmp/deploy-rollback.sh /www/scripts/deploy-rollback.sh
sudo install -m 0755 /tmp/setup-1panel-openresty.sh /www/scripts/setup-1panel-openresty.sh
sudo 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/lib
sudo install -m 0644 /tmp/deploy-lib/common.sh /www/scripts/lib/common.sh
sudo 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

Terminal window
sudo nano /etc/bitzorcas/preview.env
sudo chmod 600 /etc/bitzorcas/preview.env

Skeleton (targets are 127.0.0.1 + 1Panel host ports):

ASPNETCORE_ENVIRONMENT=Development
DOTNET_ENVIRONMENT=Development
BITZORCAS_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.1
RabbitMq__Port=<MQ_PORT>
RabbitMq__User=<USER>
RabbitMq__Password=<PWD>
RabbitMq__VirtualHost=/
Redis__Enabled=true
Redis__ConnectionString="127.0.0.1:<REDIS_PORT>,password=<PWD>"
Jwt__Secret=<openssl rand -base64 48>
PiiEncryption__SearchHashKey=<openssl rand -base64 32>
DataProtection__ApplicationName=bitzorcas-preview
DataProtection__RedisKey=bitzorcas:dataprotection:preview
FileStorage__DefaultProvider=Minio
FileStorage__S3__Endpoint=<FILES_BASE>
FileStorage__S3__AccessKey=<AK>
FileStorage__S3__SecretKey=<SK>
FileStorage__S3__BucketName=bitzorcas-files
FileStorage__S3__Region=cn-east-1
FileStorage__S3__UseSsl=true
FileStorage__S3__ForcePathStyle=true
FileStorage__S3__CreateBucketIfNotExists=true
# IP + http only: UseSsl=false
# Must match the browser address bar exactly (scheme + host + port), or login returns Cookie Origin 403
Frontend__BaseUrl=<PUBLIC_BASE>
Cors__AllowAnyOrigin=false
Cors__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 depth
OpenApi__Enabled=true
OpenApi__RequireAuthentication=true
OpenApi__PersistAuthentication=false
OpenApi__Servers__0__Url=<PUBLIC_BASE>
OpenApi__Servers__0__Description=1Panel-preview-entry
ForwardedHeaders__ForwardLimit=2
ForwardedHeaders__KnownProxies__0=127.0.0.1
Gateway__KnownProxies__0=127.0.0.1/8
Gateway__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.json
ReverseProxy__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=true
Licensing__Runtime__PolicyId=community.small.v1
Licensing__Runtime__Environment=development
Licensing__Runtime__ProductId=bitzorcas-modern
Licensing__Runtime__ProductVersion=1.0.0
Licensing__Runtime__TenancyMode=multi-tenant
Licensing__Runtime__DeploymentIdentityPath=/www/apps/preview/.bitzorcas/license/deployment-id
Licensing__Runtime__OfflineLicensePath=/www/apps/preview/.bitzorcas/license/runtime-license.json
BITZORCAS_DISABLE_AGILECONFIG=0
AgileConfig__Enabled=true
AgileConfig__AppId=modern-api-01
AgileConfig__Secret=<SECRET>
AgileConfig__Nodes=http://127.0.0.1:<AGILECONFIG_PORT>
AgileConfig__Env=DEV
AgileConfig__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=1

Generate deployment-id once:

Terminal window
# Create the stable seat-counting anchor once; never rotate it during an ordinary release.
sudo mkdir -p /www/apps/preview/.bitzorcas/license
if [[ ! -s /www/apps/preview/.bitzorcas/license/deployment-id ]]; then
openssl rand -hex 16 | sudo tee /www/apps/preview/.bitzorcas/license/deployment-id >/dev/null
fi
sudo chmod 640 /www/apps/preview/.bitzorcas/license/deployment-id

4. Pack and upload

4.1 First build: full package

Terminal window
# 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):

Terminal window
.\scripts\deploy\pack.ps1 -Env preview -Version $Version
.\scripts\deploy\pack-frontend.ps1 -Environment preview -Version $Version

pack-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.

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

Success looks like:

[INFO] server baseline selected: manifest-server-preview-20260923.txt
[INFO] incremental result: 14 changed, 102 unchanged, 0 deleted

The 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:

Terminal window
sudo /www/scripts/deploy.sh /www/releases/bitzorcas-preview-<VERSION>.zip preview

Flow: 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.

Terminal window
sudo BITZORCAS_DEPLOY_DB_MODE=schema-only \
/www/scripts/deploy.sh /www/releases/bitzorcas-preview-<VERSION>.zip preview

Compat: 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)

  1. Set BITZORCAS_DEPLOY_DB_MODE=full-seed (or SKIP_DEMO_SEED=0 without INIT_NO_SEED=1) and all USER__*__PASSWORD values.
  2. Redeploy.
  3. Existing users are not password-reset by seed; only newly created accounts use the initial secrets.

5.4 Frontend

Terminal window
# 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 \
preview

6. 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;

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:

<site-alias>.conf (include style)
# 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:

app.example.com.conf
# 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):

<public-ip>.conf
# 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.

openresty-app.locations.conf
# 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

Terminal window
# Verify all resident processes first, then probe internal and public readiness routes.
systemctl is-active bitzorcas-jobhost-preview bitzorcas-api-preview bitzorcas-gateway-preview
curl -fsS http://127.0.0.1:8080/health/ready
curl -fsS http://127.0.0.1:8080/health/license
curl -fsS http://127.0.0.1:8082/health/ready
curl -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.json
curl -sS -o /dev/null -D - -H 'Accept: text/html' <PUBLIC_BASE>/scalar/v1

Then 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

SymptomFix
Every pack.sh is fullMissing manifest-server-preview-*.txt; download §4.2 baseline
Login 403 Authentication.Cookie.OriginRejectedCors__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.InvalidCredentialsDemo seed not run or wrong password; see §5.3
admin requires PASSWORD_HASHKeep platform-seed / SKIP_DEMO_SEED=1, or use full-seed with passwords; day-to-day deploys use schema-only
Gateway ignores api-cluster env keysUse cluster ids api / signalr / files; newer deploy writes appsettings.Deploy.json
Gateway crash on CookieStickySession / RequestHeaderDeploy a package with the fixed Gateway appsettings.json
/openapi or /scalar returns 404Confirm OpenApi__Enabled=true and forwarding for both paths in OpenResty and Gateway
API startup says RequireAuthentication is mandatoryKeep BITZORCAS_BIND_ADDRESS and add OpenApi__RequireAuthentication=true
Scalar redirects to a 404 login page or loopsForward /api/docs/login and /docs/login; verify the public scheme, trusted proxies, and bitzorcas.docs cookie
Scalar calls port 80/443 or the wrong hostUse $http_host, add X-Forwarded-Port $server_port, and verify OpenApi__Servers__0__Url
DocsLogin reports MFA, CAPTCHA, or forced password changeUse formal login and paste its accessToken into Bearer; do not disable the login policy
First empty-DB seed is slowExpected; later use BITZORCAS_DEPLOY_DB_MODE=schema-only (or INIT_NO_SEED=1)
Default deploy still slow after tables existDefault is platform-seed (dictionaries still run); set schema-only to skip all seeds
Terminal window
journalctl -u 'bitzorcas-*-preview' -f

Updates 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 with deploy-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.

100%

Scroll or use controls to zoom · drag when enlarged · double-click for 100% / 200%