Configuration reference
How configuration works
Settings live in docker/.env.example, copied to .env next to the compose file. The compose file reads from .env and passes the values to services as environment variables. Some settings reach services under a different name, for example, STACK_NETWORK_SUBNET arrives at the API as RateLimit__KnownNetworks__0.
Not every setting lives in .env.example. A few rate-limit and order-numbering settings are set directly on the API service in a compose override file (see Reverse proxy and trusted proxies and API and ports for the complete list).
Compose files
Each compose file is for a different deployment target. Settings tables below show which tier each setting belongs to.
How a value reaches the API
The same setting can live in different places depending on whether it is in .env.example or set in a compose override. Here is an example using the trusted proxy and order settings:
# Most settings live here:
RATE_LIMIT_TRUSTED_PROXY_COUNT=1
STACK_NETWORK_SUBNET=172.30.0.0/24# Settings not in .env are set directly on the service:
services:
hostpanel-api:
environment:
Orders__NumberPrefix: "ORD-"
Orders__NumberDigits: 4# .env variable → API configuration key
STACK_NETWORK_SUBNET → RateLimit__KnownNetworks__0
RATE_LIMIT_TRUSTED_PROXY_COUNT → RateLimit__TrustedProxyCount
Orders__NumberPrefix (override) → Orders__NumberPrefix (no rename)| File | Purpose | Tier |
|---|---|---|
docker-compose.hub.yml | Standalone self-host, used by install.sh | hub |
docker-compose.shared.yml + docker-compose.dev.yml | Local development | dev |
docker-compose.prod.yml | Deployment built from source | prod |
docker-compose.demo.yml | Demo stack | demo |
docker-compose.platform.yml | Joining an existing network | n/a |
Required secrets
CHANGE_ME is a placeholder, not a real default. The API refuses to start outside Development if a secret contains a placeholder or is too short. Always generate fresh values for production. openssl: - JWT_SECRET (at least 32 characters):
openssl rand -hex 32 - ENCRYPTION_KEY (exactly 32 bytes, base64):
openssl rand -base64 32 - SESSION_ENCRYPTION_KEY (exactly 32 bytes, base64):
openssl rand -base64 32 - HOSTPANEL_BACKCHANNEL_LOGOUT_KEY (at least 16 characters):
openssl rand -hex 24 - POSTGRES_PASSWORD:
openssl rand -hex 24 - RABBITMQ_PASSWORD:
openssl rand -hex 24
General
Development runs the DevDataSeeder and re-seeds test data on every restart. Always set ASPNETCORE_ENVIRONMENT=Production on a deployed host. | Name | Type | Default | Description |
|---|---|---|---|
ASPNETCORE_ENVIRONMENT | string | Development | Runtime mode: Production or Development. Development also runs DevDataSeeder, which re-seeds test data on every restart. A server must use Production. Required in hub and prod. Tiers: hub, prod, dev. |
AUTH_APP_NAME | string | hostpanel | Application name registered with the auth layer (cookie and client naming). Tiers: hub, prod, dev. |
DISABLE_RELEASE_UPDATE_CHECKS | flag | false | Set to true to disable the daily check for a newer release on GitHub. Disable when the installation has no outbound internet access. Maps to ScheduledJobs__DisableReleaseUpdateChecks. Tiers: hub, prod. |
Sign-in modes
Hostpanel supports two authentication modes: local (users managed by Hostpanel itself) and SSO (users signed in through an external OpenID Connect provider).
| Name | Type | Default | Description |
|---|---|---|---|
AUTH_MODE | string | local | local: Hostpanel authenticates its own users. sso: sign-in goes to an external OpenID Connect provider. Required. Tiers: all. |
SSO settings
SSO These settings are required only when AUTH_MODE=sso.| Name | Type | Default | Description |
|---|---|---|---|
SSO_URL | string | https://sso.example.com | Server-to-server address the API reaches the provider at. Required for SSO. Tiers: all. |
SSO_PUBLIC_URL | string | https://sso.example.com | Where the browser is sent to sign in (provider's public hostname, issuer). Required for SSO. Tiers: all. |
SSO_CLIENT_ID | string | hostpanel | Client identifier registered at the provider. Required for SSO. Tiers: all. |
SSO_CALLBACK_URL | string | https://panel.example.com/api/portal/auth/sso/callback | URL the provider redirects to after sign-in. The admin callback is <admin origin>/api/auth/callback. Required for SSO. Tiers: all. |
SSO_SERVICE_KEY | secret | CHANGE_ME | Machine credential for reading the user directory. Generate with openssl rand -base64 24. Required for SSO. Tiers: all. |
CLIENT_HOSTPANEL_SECRET | secret | CHANGE_ME | Secret the API exchanges the admin SPA's authorization code with, server to server. API refuses to start without it when AUTH_MODE=sso. Required for SSO. Tiers: all. |
HOSTPANEL_BACKCHANNEL_LOGOUT_KEY | secret | CHANGE_ME | Shared with the provider for back-channel logout. Generate with openssl rand -hex 24. Must be at least 16 characters on a deployed host. Leave empty only where no provider is connected. Required for SSO. Tiers: all. |
Updating to a release that moves the sign-on client package to 5.1 resets the admin panel sessions once: the package now keeps its session cache keys per product, so the sessions stored under the old keys are no longer read. Every administrator signs in again after the update, with no action needed beyond that, and the old entries expire on their own. The package also maps the back-channel logout receiver itself at /api/auth/backchannel-logout, and a sign-in callback that has no matching attempt (a second tab, Back after signing in) now redirects to the sign-in page instead of answering an error body. Nothing needs configuring for either.
Local session settings
Local These settings apply only when AUTH_MODE=local.| Name | Type | Default | Description |
|---|---|---|---|
SESSION_ACCESS_TOKEN_MINUTES | string | 15 | Access-token lifetime in minutes. Sliding renewal re-issues the token while the person keeps working. Tiers: hub, prod, dev. |
SESSION_MAX_MINUTES | string | 720 | Absolute session age after which token renewal stops and the person must sign in again. Tiers: hub, prod, dev. |
Addresses and host names
| Name | Type | Default | Description |
|---|---|---|---|
DOMAIN | string | yourdomain.com | Public host name of the client portal (e.g., panel.example.com). Must differ from ADMIN_DOMAIN. Required. Tiers: all. |
ADMIN_DOMAIN | string | admin.yourdomain.com | Public host name of the admin panel. Must differ from DOMAIN. Required. Tiers: all. |
ADMIN_BASE_URL | string | https://admin.yourdomain.com | Absolute URL of the admin panel (scheme, no trailing slash). Base of links the API mails to staff. Required in prod and hub. Tiers: prod, hub. |
CLIENT_BASE_URL | string | https://yourdomain.com | Absolute URL of the client portal (scheme, no trailing slash). Base of links the API mails to clients. Required in prod and hub. Tiers: prod, hub. |
NUXT_PUBLIC_BASE_URL | string | https://panel.example.com | Public URL the client portal JavaScript uses for API calls. Required. Tiers: all. |
ADMIN_BASE_URL_PATH | string | / | Base path the admin SPA is served under (/ or /admin). Read by the admin container at start, not at build. Tiers: hub, prod. |
ALLOWED_HOSTS | string | panel.example.test;hostpanel-api;localhost | Host names the API answers to, semicolon-separated. Include the public host, hostpanel-api and localhost. Required. Tiers: all. |
PANEL_DEV_ORIGIN | string | http://panel.example.test | Dev-proxy origin the API allows alongside localhost (CORS). Tiers: dev. |
SSH_AGENT_SOURCE_ADDRESSES | string | — | Public address(es) this installation connects to SSH key agents from, comma-separated. New in 0.4.0. Tiers: prod, hub. |
PANEL_HOST_ADDRESSES | string | — | Control panels reachable only over a VPN, as hostname=address pairs, comma-separated. New in 0.4.0. Tiers: prod, hub. |
Servers → SSH shows the exact firewall rules once SSH_AGENT_SOURCE_ADDRESSES is set. See SSH key agent.
Keys and encryption
| Name | Type | Default | Description |
|---|---|---|---|
ENCRYPTION_KEY | secret | see .env.example | Must decode to exactly 32 bytes. Protects encrypted database columns. Generate: openssl rand -base64 32. The example value is a placeholder and is refused outside Development. Required in prod, hub, demo. Tiers: prod, hub, demo. |
SESSION_ENCRYPTION_KEY | secret | — | Key for client portal sessions (AES-256-GCM). 32 random bytes, base64. Changing it signs everyone out. Required in prod, hub, demo. Tiers: prod, hub, demo. |
JWT_SECRET | secret | see .env.example | At least 32 characters. Placeholder is refused outside Development. Generate: openssl rand -hex 32. Tiers: all. |
JWT_ISSUER | string | innovayse-api | Issuer claim stamped into every token. Tiers: all. |
JWT_AUDIENCE | string | innovayse-clients | Audience claim verified on every token. Tiers: all. |
Database, message queue and cache
| Name | Type | Default | Description |
|---|---|---|---|
POSTGRES_HOST | string | hostpanel-db | Database host name. Tiers: all. |
POSTGRES_PORT | string | 5432 | Database port. Tiers: all. |
POSTGRES_USER | string | postgres | Database user. Tiers: all. |
POSTGRES_PASSWORD | secret | CHANGE_ME | Database password. Placeholder is refused outside Development. Required. Tiers: all. |
POSTGRES_DB | string | innovayse_dev | Database name. Tiers: all. |
RABBITMQ_HOST | string | hostpanel-rabbitmq | Message queue host name. Tiers: all. |
RABBITMQ_PORT | string | 5672 | Message queue port. Tiers: all. |
RABBITMQ_USER | string | guest | Message queue user. Tiers: all. |
RABBITMQ_PASSWORD | secret | CHANGE_ME | Message queue password. Placeholder is refused outside Development. Required. Tiers: all. |
REDIS_CONNECTION | string | hostpanel-redis:6379 | Redis connection string. Used for admin sessions (SSO), data protection key ring, and client portal sign-in sessions. Tiers: all. |
Mail
| Name | Type | Default | Description |
|---|---|---|---|
SMTP_HOST | string | hostpanel-mailhog | SMTP relay host. Installer leaves this empty; no mail is sent until it is set. Required in prod and hub. Tiers: all. |
SMTP_PORT | string | 1025 | SMTP port. Usually 587 (STARTTLS) or 465 (implicit TLS). Tiers: all. |
SMTP_USERNAME | string | — | Relay username. Tiers: all. |
SMTP_PASSWORD | secret | — | Relay password. Tiers: all. |
SMTP_ENCRYPTION | string | none | tls (STARTTLS), ssl (implicit TLS) or none (local catcher only). Required and validated on startup. Tiers: all. |
SMTP_FROM | string | noreply@example.com | Sender address. Tiers: all. |
SMTP_FROM_NAME | string | Hostpanel | Sender display name. Tiers: all. |
EMAIL_TO | string | contact@yourdomain.com | Contact form delivery address. Unset form answers 503 CONTACT_NOT_CONFIGURED. Tiers: all. |
DEPARTMENT_SUPPORT_EMAIL | string | support@example.com | Support department email on first install. Editable in the admin panel afterwards. Tiers: all. |
DEPARTMENT_BILLING_EMAIL | string | billing@example.com | Billing department email on first install. Tiers: all. |
DEPARTMENT_SALES_EMAIL | string | sales@example.com | Sales department email on first install. Tiers: all. |
DEPARTMENT_GENERAL_EMAIL | string | hello@example.com | General department email on first install. Tiers: all. |
TELEGRAM_BOT_TOKEN | secret | — | Telegram bot token for contact form notifications. Both or neither: half a pair refuses startup. Tiers: all. |
TELEGRAM_CHAT_ID | string | — | Telegram chat ID, optional pair. Set both or neither. Tiers: all. |
Reverse proxy and trusted proxies
When the installation sits behind one or more proxies, the API and client portal must trust headers about the real visitor's address. The diagram shows the path:
visitor → [extra proxy] → edge (Caddy/nginx) → client portal → API
↑
RATE_LIMIT_TRUSTED_PROXY_COUNT
CLIENT_TRUSTED_PROXY_COUNTEach extra proxy in front of the bundled edge adds 1 to both counts. The shipped stacks have one built-in edge layer, so the default is 1. If you place an extra proxy in front (load balancer, WAF, CDN), increase both counts by 1 per extra layer.
| Name | Type | Default | Description |
|---|---|---|---|
STACK_NETWORK_SUBNET | string | 172.30.0.0/24 | CIDR range of the Docker network the stack creates. Passed to the API as RateLimit__KnownNetworks__0. Pick a range no other network on the host uses. Required. Tiers: hub, prod, dev. |
RATE_LIMIT_TRUSTED_PROXY_COUNT | string | 1 | Number of proxies in front of the API (RateLimit__TrustedProxyCount). The shipped stacks need 1 (the bundled edge). Add one per extra proxy. Tiers: hub, prod, dev. |
CLIENT_TRUSTED_PROXY_COUNT | string | 1 | Number of proxies in front of the client portal (NUXT_TRUSTED_PROXY_COUNT). The shipped stacks need 1. Add one per extra proxy. The dev stack fixes this at 0 and does not read this variable. Tiers: hub, prod, demo. |
Rate-limit settings (compose override only)
.env.example. Use them to tune rate limiting per endpoint. | Setting | Default | Description |
|---|---|---|
RateLimit__Global__PermitLimit | 1200 | Requests allowed per window (applies to every request) |
RateLimit__Global__WindowSeconds | 60 | Window length in seconds |
RateLimit__Auth__PermitLimit | 15 | Auth endpoint limit per window |
RateLimit__Auth__WindowSeconds | 60 | Auth window length in seconds |
RateLimit__Upstream__PermitLimit | 60 | Upstream (panel API) limit per window |
RateLimit__Upstream__WindowSeconds | 60 | Upstream window length in seconds |
RateLimit__Strict__PermitLimit | 5 | Strict endpoints limit per window |
RateLimit__Strict__WindowSeconds | 60 | Strict window length in seconds |
RateLimit__SshKeys__PermitLimit | 20 | SSH key endpoints limit per window |
RateLimit__SshKeys__WindowSeconds | 60 | SSH key window length in seconds |
RateLimit__ConcurrentPermitLimit | 5 | Maximum concurrent requests from one address |
RateLimit__PluginDefault__PermitLimit | 120 | Plugin route limit per window, for a route that names no tier. Counted per plugin and per caller (the client account, or the administrator on an admin call) |
RateLimit__PluginDefault__WindowSeconds | 60 | Plugin default window length in seconds |
RateLimit__PluginStrict__PermitLimit | 20 | Limit per window for a plugin route that asks for the strict tier (costly or sensitive routes, such as one that sends messages) |
RateLimit__PluginStrict__WindowSeconds | 60 | Plugin strict window length in seconds |
RateLimit__PluginHeavy__PermitLimit | 6 | Limit per window for a plugin route that asks for the heavy tier (reports, exports) |
RateLimit__PluginHeavy__WindowSeconds | 60 | Plugin heavy window length in seconds |
RateLimit__PluginCallback__PermitLimit | 120 | Limit per window for the public plugin callback door (/api/plugins/{id}/callback/...). Counted per caller address over every plugin and path, declared or not |
RateLimit__PluginCallback__WindowSeconds | 60 | Plugin callback window length in seconds |
RateLimit__PluginStreamPermitLimit | 3 | Streaming plugin routes one caller may hold open at once, per plugin |
SignInLockout__MaxFailedAttempts | 5 | Wrong passwords before lockout |
SignInLockout__LockoutMinutes | 15 | Minutes an account stays locked (locked sign-in → 423) |
MaxFailedAttempts accepts 1–100, LockoutMinutes accepts 1–1440. Values outside these ranges refuse startup.
Plugin routes are limited per plugin and per caller, so one plugin or one account cannot use up the API. A plugin route declares its tier in the plugin manifest (rateLimit: default, strict or heavy); a route that declares none uses the default tier. A refused request answers 429 with Retry-After and X-RateLimit-Remaining. Every plugin tier must be positive and no larger than RateLimit__Global__PermitLimit, the tiers must be ordered heavy <= strict <= default, and RateLimit__PluginStreamPermitLimit must be positive, or startup is refused.
The public plugin callback door (/api/plugins/{id}/callback/...) is rate limited per caller address, and that address is the one the forwarded headers resolve to. It only works as intended when the forwarded headers are configured: set RateLimit__TrustedProxyCount to the number of proxies in front of the API and list them in RateLimit__KnownProxies or RateLimit__KnownNetworks (the bundled stacks do this through STACK_NETWORK_SUBNET). Behind an unconfigured proxy every gateway call shares the proxy's address and one burst refuses all callbacks; with too many trusted hops a sender can write its own X-Forwarded-For and pick its own bucket. The API ignores X-Forwarded-For from any peer that is not a configured proxy.
Edge proxy
The edge layer (Caddy on hub, nginx on prod) terminates TLS and routes to the API and client portal.
| Name | Type | Default | Description |
|---|---|---|---|
EDGE_SCHEME | string | https:// | https:// for automatic Let's Encrypt certificates (default); http:// when your own proxy terminates TLS (then pick free ports). Tiers: hub, prod. |
EDGE_HTTP_PORT | string | 80 | HTTP port the edge listens on. Tiers: hub, prod. |
EDGE_HTTPS_PORT | string | 443 | HTTPS port the edge listens on. Tiers: hub, prod. |
ACME_EMAIL | string | admin@yourdomain.com | Contact address for Let's Encrypt certificates. Set by installer. Required for hub. Tiers: hub. |
EDGE_MAX_BODY_SIZE | string | 50M | Largest request body on the client portal host. Use the M suffix (e.g., 50M). Tiers: hub, prod. |
EDGE_ADMIN_MAX_BODY_SIZE | string | 1M | Largest request body on the admin host. Tiers: hub, prod. |
EDGE_CLIENT_CSP_SOURCES | string | — | Extra origins the client portal's CSP allows (space-separated). Add the script and API origins a payment plugin's checkout needs; the default policy names no gateway. Tiers: hub, prod. |
NGINX_HTTP_PORT | string | 80 | Host HTTP port for the bundled nginx (prod stack, no platform overlay). Tiers: prod. |
NGINX_HTTPS_PORT | string | 443 | Host HTTPS port for the bundled nginx. Tiers: prod. |
Images and versions
| Name | Type | Default | Description |
|---|---|---|---|
IMAGE_TAG | string | local | Release to run, e.g., 0.4.0 or latest. No leading v. Required for hub and prod. Tiers: hub, prod. |
DOCKERHUB_NAMESPACE | string | innovayse | Docker Hub user/organisation for hub stack images. Tiers: hub. |
IMAGE_REGISTRY | string | registry.example.com/hostpanel | Registry path for prod stack images (e.g., registry.example.com/hostpanel). Required for prod. Tiers: prod. |
POSTGRES_IMAGE | string | see .env.example | Full postgres image reference, pinned to a digest. Tiers: all. |
RABBITMQ_IMAGE | string | see .env.example | Full RabbitMQ image reference, pinned to a digest. Tiers: all. |
REDIS_IMAGE | string | see .env.example | Full Redis image reference, pinned to a digest. Tiers: all. |
MAILHOG_IMAGE | string | see .env.example | Full Mailhog image reference (dev and demo stacks). Tiers: dev, demo. |
NGINX_IMAGE | string | see .env.example | Full nginx image reference (must be the unprivileged build). Tiers: prod. |
CADDY_IMAGE | string | see .env.example | Full Caddy image reference. Tiers: hub. |
API and ports
| Name | Type | Default | Description |
|---|---|---|---|
API_PORT | string | 5148 | Port the API listens on inside its container. Templated into the edge config, change here only. Tiers: all. |
CLIENT_PORT | string | 3000 | Port the client portal listens on inside its container. Templated into the edge config, change here only. Tiers: all. |
API_GC_SERVER | string | 0 | .NET GC mode: 0 workstation, 1 server. Tiers: prod, hub. |
API_GC_CONSERVE_MEMORY | string | 5 | Memory conservation level, 0–9. Tiers: prod, hub. |
API_GC_HEAP_HARD_LIMIT_PERCENT | string | 75 | Heap cap as a percentage of container memory. Tiers: prod, hub. |
Order numbering and invoice settings (compose override only)
.env. Set them directly on the API service in a compose override. | Setting | Default | Description |
|---|---|---|
Orders__NumberPrefix | ORD- | Order number prefix |
Orders__NumberDigits | 4 | Zero-padded digits (e.g., 0042) |
Orders__InvoicePaymentTermDays | 7 | Invoice payment term in days (0–365) |
InvoicePdf__MaxLines | 500 | Maximum line items per PDF |
InvoicePdf__MaxConcurrentRenders | 4 | Concurrent PDF renders |
InvoicePdf__MaxRenderWaitSeconds | 10 | Seconds a download waits for a free render slot before answering 503 with Retry-After. |
Client portal look and content
All these settings control the public-facing storefront and have NUXT_PUBLIC_PORTAL_* environment variable fallbacks. The admin panel's Settings section wins over environment variables once set.
| Name | Type | Default | Description |
|---|---|---|---|
NUXT_PUBLIC_PORTAL_TEMPLATE | string | classic | Storefront template: classic, aurora or nova. Admin Settings overrides this. Tiers: all. |
NUXT_PUBLIC_PORTAL_APPS_ENABLED | flag | false | Show the apps launcher in the header. true/1/yes/on. Tiers: all. |
NUXT_PUBLIC_PORTAL_CONTACT_EMAIL | string | — | Footer contact email. Empty hides it. Tiers: all. |
NUXT_PUBLIC_PORTAL_SOCIAL_TWITTER | string | — | Twitter/X profile URL. Tiers: all. |
NUXT_PUBLIC_PORTAL_SOCIAL_LINKEDIN | string | — | LinkedIn profile URL. Tiers: all. |
NUXT_PUBLIC_PORTAL_SOCIAL_FACEBOOK | string | — | Facebook profile URL. Tiers: all. |
NUXT_PUBLIC_PORTAL_HOSTING_DEMO_URL | string | — | Demo site URL linked from the home products section. Tiers: all. |
NUXT_PUBLIC_PORTAL_CONTACT_ADDRESS | string | — | Postal address shown on the contact page. Tiers: all. |
NUXT_PUBLIC_PORTAL_CONTACT_MAP_URL | string | — | Map link on the contact page. Tiers: all. |
NUXT_PUBLIC_PORTAL_STATS_CLIENTS | string | — | Home page stat: client count (free text). Tiers: all. |
NUXT_PUBLIC_PORTAL_STATS_UPTIME | string | — | Home page stat: uptime figure. Tiers: all. |
NUXT_PUBLIC_PORTAL_STATS_REGIONS | string | — | Home page stat: regions count. Tiers: all. |
NUXT_PUBLIC_PORTAL_HERO_BADGE | string | — | Short badge text above the home page headline. Tiers: all. |
NUXT_PUBLIC_PORTAL_SITE_NAME | string | — | Site name for browser title and social previews. Admin Settings wins. Tiers: all. |
NUXT_PUBLIC_PORTAL_SITE_TAGLINE | string | — | Site tagline. Admin Settings wins. Tiers: all. |
NUXT_PUBLIC_PORTAL_THEME_DEFAULT | string | dark | Default colour mode: light, dark or system. Tiers: all. |
NUXT_PUBLIC_PORTAL_THEME_USER_TOGGLE | flag | true | Show the light/dark switch. Only the literal false hides it. Tiers: all. |
NUXT_PUBLIC_PORTAL_LOGO | string | — | Logo URL (light background). Empty uses the built-in mark. Tiers: all. |
NUXT_PUBLIC_PORTAL_LOGO_DARK | string | — | Logo URL (dark background). Falls back to NUXT_PUBLIC_PORTAL_LOGO. Tiers: all. |
NUXT_PUBLIC_PORTAL_LOGO_MARK | string | — | Square mark for compact spaces. Tiers: all. |
NUXT_PUBLIC_PORTAL_FAVICON | string | — | Favicon URL. Tiers: all. |
NUXT_PUBLIC_PORTAL_BRAND_PRIMARY | string | — | Primary brand colour (#rrggbb). Tiers: all. |
NUXT_PUBLIC_PORTAL_BRAND_ACCENT | string | — | Accent brand colour (#rrggbb). Tiers: all. |
NUXT_PUBLIC_PORTAL_BRAND_FONT_HEADING | string | — | Heading font: inter, noto-sans-armenian, noto-serif-armenian or system. Tiers: all. |
NUXT_PUBLIC_PORTAL_BRAND_FONT_BODY | string | — | Body font. Tiers: all. |
NUXT_PUBLIC_PORTAL_GTM_ID | string | — | Google Tag Manager container ID. Waits for cookie consent. Admin Settings wins. Tiers: all. |
NUXT_PUBLIC_PORTAL_CHAT_BASE_URL | string | — | Live chat server URL. Also a build-time ARG (baked into the image). Tiers: all. |
NUXT_PUBLIC_PORTAL_CHAT_WEBSITE_TOKEN | string | — | Chat widget token (default language). Tiers: all. |
NUXT_PUBLIC_PORTAL_CHAT_WEBSITE_TOKEN_RU | string | — | Chat widget token (Russian). Tiers: all. |
NUXT_PUBLIC_PORTAL_CHAT_WEBSITE_TOKEN_HY | string | — | Chat widget token (Armenian). Tiers: all. |
Payments and external links
| Name | Type | Default | Description |
|---|---|---|---|
BILLING_PORTAL_URL | string | — | Billing portal address (no trailing slash). Left empty, handoff links are hidden. Tiers: all. |
Plugins
| Setting | Default | Description |
|---|---|---|
Plugins__InstallPath | (empty, falls back to plugins-installed beside the app) | Directory where the API installs plugin packages. |
Plugins__MaxEntryCount | 1000 | Maximum number of files in a plugin zip archive. |
Plugins__MaxUncompressedBytes | 209715200 | Maximum total uncompressed size of a plugin archive in bytes (200 MiB). |
Plugins__MigrationTimeoutSeconds | 120 | Longest one plugin migration hook may run after an upgrade, in seconds (1 to 3600). Past it the host stops waiting and keeps the plugin switched off until a later attempt succeeds. |
Plugins__MaxViewBytes | 262144 | Maximum size in bytes of one plugin view document (a page or slot view file, 256 KiB); a larger file refuses the plugin at load and at install. 1 to 4194304 (4 MiB). |
Plugins__TrustedPackageSha256__0, __1, ... | n/a | SHA-256 hashes of plugin packages to trust unconditionally. Each hash is a separate indexed key. |
PluginApi__MaxBodyBytes | 1048576 | Largest request body the host reads for a plugin route, in bytes (1 to 67108864). |
PluginApi__CallTimeoutSeconds | 30 | Seconds the host waits for a plugin route to answer (1 to the ceiling below). Past it the plugin is cancelled through its token and the caller gets PLUGIN_TIMEOUT (504). On a streamed reply it covers the wait for the reply and for the first event and the silence between events, never the length of a stream that keeps sending. |
PluginApi__MaxCallTimeoutSeconds | 600 | The ceiling for a per route limit (1 to 3600). Every route gets CallTimeoutSeconds today; a route will be able to ask for a longer limit, up to this, once the plugin SDK has a field for it. Must be at least CallTimeoutSeconds. |
PluginApi__AbandonWaitSeconds | 5 | Seconds, after the call limit, the host waits for a plugin that ignores its cancellation before it abandons the call (1 to 60). The caller gets PLUGIN_TIMEOUT either way. |
PluginApi__MaxAbandonedCallsPerPlugin | 8 | Most abandoned calls of one plugin that may still be running (1 to 1000). At the limit, new calls to that plugin are refused at once with PLUGIN_TIMEOUT until one of them ends. |
PluginApi__StreamHeartbeatSeconds | 15 | Seconds a streamed plugin reply may stay silent before the host writes a comment line to keep the connection alive (1 to 300). |
PluginApi__StreamEventMaxBytes | 65536 | Largest data of one event of a streamed plugin reply, in bytes (1 to 1048576). A plugin that sends a larger event ends the stream with an error event. |
PluginSigning__MaintainerKeys__0__KeyId, __PublicKey, ... | (shipped in the image) | Ed25519 signing key entries for the official plugin maintainers. Shipped in the image; operators do not set these. |
PluginSigning__OperatorKeys__0__KeyId | n/a | Key ID of an operator-trusted plugin signer. Each entry is a pair of __KeyId and __PublicKey under the same index. With the compose files, set PLUGIN_OPERATOR_KEY_ID. Leave it and the public key empty to add none; setting only one of the two stops the API at start. |
PluginSigning__OperatorKeys__0__PublicKey | n/a | Ed25519 public key of the operator-trusted signer at index 0. Add more signers as __1__KeyId / __1__PublicKey, and so on. With the compose files, set PLUGIN_OPERATOR_PUBLIC_KEY. |
Development-only settings
| Name | Type | Default | Description |
|---|---|---|---|
POSTGRES_HOST_PORT | string | 5434 | Host port published by the database container. Tiers: dev. |
RABBITMQ_HOST_PORT | string | 5673 | Host port published by RabbitMQ. Tiers: dev. |
RABBITMQ_MANAGEMENT_HOST_PORT | string | 15673 | Host port for the RabbitMQ management UI. Tiers: dev. |
MAILHOG_SMTP_HOST_PORT | string | 1026 | Host port for the Mailhog SMTP. Tiers: dev. |
MAILHOG_UI_HOST_PORT | string | 8027 | Host port for the Mailhog web UI. Tiers: dev. |
API_HOST_PORT | string | 5149 | Host port published by the API container. Tiers: dev. |
CLIENT_HOST_PORT | string | 3001 | Host port published by the client portal. Tiers: dev. |
ADMIN_HOST_PORT | string | 5174 | Host port published by the admin panel. Tiers: dev. |
CORS_CLIENT_ORIGIN | string | http://localhost:3001 | Browser origin for the client portal (CORS). Tiers: dev. |
CORS_ADMIN_ORIGIN | string | http://localhost:5174 | Browser origin for the admin panel (CORS). Tiers: dev. |
CORS_STAGE_ORIGIN | string | https://stage-panel.example.com | Browser origin for a staging client portal (CORS). Tiers: dev. |
CLIENT_DEV_ALLOWED_HOSTS | string | — | Extra hostnames the client dev server answers to (comma-separated). Tiers: dev. |
ADMIN_DEV_ALLOWED_HOSTS | string | — | Extra hostnames the admin dev server answers to (comma-separated). Tiers: dev. |
DEV_FILE_POLLING | flag | true | Poll bind mounts for changes (true/false). Needed on Docker Desktop mounts. Tiers: dev. |
DEV_NUGET_AUDIT | flag | false | Enable NuGet package audit inside the dev API container. Tiers: dev. |
DEV_NUGET_SIGNATURE_VERIFICATION | flag | false | Enable NuGet signature verification. Tiers: dev. |
DEV_SEED_ADMIN_PASSWORD | string | Admin123! | Admin account password created by the dev data seeder. Published fixture, not a secret. Tiers: dev. |
DEV_SEED_CLIENT_PASSWORD | string | Test@12345 | Client account password created by the dev data seeder. Tiers: dev. |
TESTCONTAINERS_HOST_OVERRIDE | string | host.docker.internal | Docker host for Testcontainers in the dev API container. Tiers: dev. |
ADMIN_API_BASE | string | /api | Base path the admin dev server proxies to the API. Tiers: dev. |
DB_CPUS | string | 1.0 | CPU cap for the database container. Tiers: dev. |
DB_MEMORY | string | 1G | Memory cap for the database container. Tiers: dev. |
RABBITMQ_CPUS | string | 1.0 | CPU cap for RabbitMQ. Tiers: dev. |
RABBITMQ_MEMORY | string | 512M | Memory cap for RabbitMQ. Tiers: dev. |
REDIS_CPUS | string | 0.5 | CPU cap for Redis. Tiers: dev. |
REDIS_MEMORY | string | 256M | Memory cap for Redis. Tiers: dev. |
MAILHOG_CPUS | string | 0.2 | CPU cap for Mailhog. Tiers: dev. |
MAILHOG_MEMORY | string | 256M | Memory cap for Mailhog. Tiers: dev. |
API_CPUS | string | 2.0 | CPU cap for the API. Tiers: dev. |
API_MEMORY | string | 2G | Memory cap for the API. Tiers: dev. |
CLIENT_CPUS | string | 1.5 | CPU cap for the client portal. Tiers: dev. |
CLIENT_MEMORY | string | 2G | Memory cap for the client portal. Tiers: dev. |
ADMIN_CPUS | string | 1.0 | CPU cap for the admin panel. Tiers: dev. |
ADMIN_MEMORY | string | 1536M | Memory cap for the admin panel. Tiers: dev. |
Demo mode
Local Demo mode requires AUTH_MODE=local.| Name | Type | Default | Description |
|---|---|---|---|
DEMO_ENABLED | flag | true | Enable demo mode. Tiers: demo. |
DEMO_RESET_INTERVAL_HOURS | string | 24 | Hours between database wipes and re-seeds. Tiers: demo. |
DEMO_ADMIN_EMAIL | string | admin@demo.local | Demo admin sign-in email (shown on the sign-in page). Tiers: demo. |
DEMO_ADMIN_PASSWORD | secret | — | Demo admin password. At least 8 characters with upper and lower case. No default; compose refuses to start without it. Required for demo. Tiers: demo. |
DEMO_CLIENT_EMAIL | string | client@demo.local | Demo client sign-in email. Tiers: demo. |
DEMO_CLIENT_PASSWORD | secret | — | Demo client password. At least 8 characters with upper and lower case. Required for demo. Tiers: demo. |
DEMO_CONTACT_EMAIL | string | — | Contact email shown in the demo storefront footer. Empty keeps the fictional placeholder. Tiers: demo. |
DEMO_CONTACT_PHONE | string | — | Contact phone shown in the demo storefront. Tiers: demo. |
DEMO_SMTP_PORT | string | 1025 | Port of the demo's Mailhog inside the network. Tiers: demo. |
DEMO_REDIS_PORT | string | 6379 | Port of the demo's Redis inside the network. Tiers: demo. |
DEMO_SESSION_ENCRYPTION_KEY | secret | — | Session encryption key for the demo portal. Use a separate value from the production one. Required for demo. Tiers: demo. |
DEMO_PUBLISH_ADDRESS | string | 127.0.0.1 | Address the demo's published ports bind to. Loopback keeps them off the network. Required for demo. Tiers: demo. |
DEMO_SMTP_ENCRYPTION | string | none | Demo mail catcher encryption. Use none. Required for demo. Tiers: demo. |
DEMO_API_HOST_PORT | string | 5150 | Host port for the demo API. Required for demo. Tiers: demo. |
DEMO_CLIENT_HOST_PORT | string | 3002 | Host port for the demo client portal. Required for demo. Tiers: demo. |
DEMO_ADMIN_HOST_PORT | string | 5175 | Host port for the demo admin panel. Required for demo. Tiers: demo. |
DEMO_ADMIN_BASE_URL | string | http://localhost:5175 | Absolute URL of the demo admin panel. Required for demo. Tiers: demo. |
DEMO_CLIENT_BASE_URL | string | http://localhost:3002 | Absolute URL of the demo client portal. Required for demo. Tiers: demo. |
DEMO_MAILHOG_UI_HOST_PORT | string | 8028 | Host port for the demo Mailhog UI. Tiers: demo. |
DEMO_NETWORK_SUBNET | string | — | Required. CIDR range of the demo stack internal network. Not in .env.example; docker-compose.demo.yml refuses to start without it. Example: 172.28.0.0/16. Tiers: demo. |
DEMO_EDGE_NETWORK_SUBNET | string | — | Required. CIDR range of the demo edge network. Not in .env.example; docker-compose.demo.yml refuses to start without it. Example: 172.29.0.0/16. Tiers: demo. |
Startup refusals
The API validates secrets and key settings before it accepts any request. If a check fails it logs the exact key and exits. Common causes:
| Symptom in the log | Cause | Fix |
|---|---|---|
| Secret still CHANGE_ME or too short | JWT_SECRET, ENCRYPTION_KEY, POSTGRES_PASSWORD, RABBITMQ_PASSWORD or a configured HOSTPANEL_BACKCHANNEL_LOGOUT_KEY holds a placeholder or is too short. | Generate a real value with openssl rand. |
| SMTP_ENCRYPTION invalid | Value is unset, misspelled or not one of tls, ssl, none. | Set it to the correct value. |
| Only one of an optional pair is set | TELEGRAM_BOT_TOKEN set but TELEGRAM_CHAT_ID empty, or vice versa. | Set both or clear both. |
| CLIENT_HOSTPANEL_SECRET missing | AUTH_MODE=sso but CLIENT_HOSTPANEL_SECRET is empty. | Set the secret or switch to AUTH_MODE=local. |
| HOSTPANEL_BACKCHANNEL_LOGOUT_KEY too short | Value is shorter than 16 characters on a deployed host. | Generate with openssl rand -hex 24. |
| Order number setting invalid | Orders__NumberPrefix and Orders__NumberDigits cannot produce a valid order number, or Orders__InvoicePaymentTermDays is out of range (0–365). | Correct the values in the compose override. |
| CORS_* origin invalid | An allowed origin is not a valid absolute URL (e.g. missing scheme). | Fix the origin in .env. |
Error codes
Error codes returned to the client with their HTTP status and message:
| Code | Status | Message |
|---|---|---|
CONTACT_NOT_CONFIGURED | 503 | "The contact form is not available on this deployment. Please use the email address or phone number shown on this page." |
SIGN_IN_ACCOUNT_LOCKED | 423 | "Too many wrong passwords. This account is locked for a while, try again later." |