Installation and configuration – running nexxcite¶
This chapter describes what nexxcite needs to run, how the first start proceeds and which settings operators make in the configuration of the application. Running several servers side by side is described separately in Running several servers.
It is intended for operators who install and run nexxcite.
1. Installation at a glance¶
nexxcite is a web application. It is delivered as a container image and runs as one process per server. All data – definitions, records, history – is kept in a SQL Server database.
| Component | Required | Remark |
|---|---|---|
| Container runtime (Docker, Kubernetes, …) | yes | The image contains the application and the .NET runtime. |
| SQL Server database | yes | An empty database and a login that may create tables in it. nexxcite creates and updates the tables itself. |
| License key | yes | Without a valid license key, nexxcite is locked. See Licensing. |
| Reverse proxy or load balancer with TLS | recommended | Terminates HTTPS. It must pass WebSocket connections through. |
| Directory for the key storage | recommended, required for several servers | Keeps users signed in across restarts. See section 5. |
| Azure Blob Storage or S3-compatible storage | for images and files | Configured in the global settings, see Global settings. |
| Redis | for several servers | Shared definition cache and message bus. See Running several servers. |
| Mail account (SMTP or Exchange Online) | for password reset | Configured in the global settings, see Global settings. |
Where settings are made¶
| Where | What | Who |
|---|---|---|
Configuration of the application (appsettings.json or environment variables) |
Database, servers, key storage, proxies, monitoring endpoints, timings | Operators |
| Admin Studio → System → Settings | License key, sign-in rules, mail account, file storage, styles | Administrators, see Global settings |
2. The container¶
| Property | Value |
|---|---|
| Base | The official ASP.NET Core runtime image of Microsoft (.NET 10) |
| User | Runs as the non-root user app of the base image |
| Port | 8080 (HTTP) |
| Working directory | /app |
| Logs | Written to the console (standard output) |
Configuration through environment variables¶
Every setting of appsettings.json can be set as an environment variable. The sections are separated
by two underscores: DataAccess:ConnectionString becomes DataAccess__ConnectionString. An
environment variable overrides the value in the file, so no configuration file has to be mounted
into the container.
Example – a single server with Docker:
docker run -d --name nexxcite -p 8080:8080 \
-e DataAccess__ConnectionString="Server=sqlserver;Database=nexxcite;User Id=nexxcite;Password=…;TrustServerCertificate=True" \
-e DataProtection__KeyRingPath=/keys \
-v nexxcite-keys:/keys \
-e Logging__File__Path= \
-e Logging__LogLevel__Default=Information \
<nexxcite image>
The user app must be allowed to write to the directory of the key storage.
Logging¶
nexxcite writes its log to the console, where container platforms collect it. In addition, the
default configuration writes a file app.log next to the application.
| Setting | Default | Meaning |
|---|---|---|
Logging:LogLevel:Default |
Debug |
Lowest level that is logged. For production we recommend Information. |
Logging:File:Path |
app.log |
File the log is also written to. Empty switches the file off – recommended in containers, where the file disappears with the container. |
Logging:File:FileSizeLimitBytes |
5242880 | Size at which a new file is started (5 MB) |
Logging:File:MaxRollingFiles |
5 | Number of files kept |
HTTPS and the environment¶
nexxcite expects to be reached through HTTPS. In a typical setup, a reverse proxy, load balancer or ingress terminates TLS and forwards the requests to port 8080 over HTTP. Configure the forwarded headers (section 5) so that nexxcite knows the original scheme and the address of the client.
Run production installations with ASPNETCORE_ENVIRONMENT set to Production (the default when the
variable is not set). It activates the error page for users and HSTS. Do not use Development in
production: users would see technical error details.
Requirements for the proxy¶
- WebSockets must be passed through. Every open browser tab keeps a permanent connection to the server.
- Timeouts for idle connections must be long enough for permanent connections. Browser and server exchange a sign of life about every 15 seconds.
- Request size: do not limit it below the size of the files your users upload.
- With several servers, sticky sessions are required in addition. See Running several servers.
3. First start¶
On the first start against an empty database, nexxcite proceeds as follows:
- It creates all tables it needs (see section 4).
- It creates the basic definitions: the roles "Administrator" and "Default User", the user
profile entity with a default form, and the user
nxadminwith the role "Administrator". These changes are published as a sketch named "Scaffold System" followed by the time. -
It writes the password of
nxadminto its log:INFO: Default user created with password: "nxAdm!n2026" -
It starts to accept requests. Since there is no license key yet, every request leads to the page "License required".
Then:
- Open nexxcite in the browser and enter the license key on the page "License required".
- Sign in as
nxadminwith the password from the log. - Change the password of
nxadminimmediately. The default password is the same for every installation. - In the Admin Studio under System → Settings, set at least the company name, the mail account and the file storage. See Global settings.
The role "Administrator" has the permission "System Developer" and with it every permission of nexxcite. See Roles and permissions.
Do not delete the user nxadmin. nexxcite checks at every start whether it exists. If it does
not, it creates it again – with the default password. Deactivate the user instead if you do not want
to use it.
4. Schema migration at startup¶
Every time a server starts, it brings the database schema up to date before it accepts requests: it creates missing tables, columns and indexes. There is no separate migration tool and no manual step after an update.
- Several servers migrate one after the other. A server that starts while another one migrates waits up to 10 minutes. If the migration is not finished by then, the server stops with an error rather than working on a half-migrated database. Started again, it migrates whatever is still missing.
- While the migration runs, the server does not answer requests. The startup endpoint
/health/startuptherefore only answers once the migration is complete (section 5). - Back up the database before every update. A migration cannot be undone by nexxcite.
Tables of entities are changed when an entity is published, not at startup. Such a change can take a
while on large tables; its time limit is set with DataAccess:SchemaMigrationTimeout.
5. Settings for operation¶
The following settings are made in the configuration of the application. The defaults are those of
the delivered appsettings.json.
Database¶
| Setting | Default | Meaning |
|---|---|---|
DataAccess:ConnectionString |
– | Required. Connection string of the SQL Server database. Without it, nexxcite does not start ("DataAccess:ConnectionString must be set"). |
DataAccess:SchemaMigrationTimeout |
00:10:00 |
Time limit (hh:mm:ss) for each statement when the table of an entity is changed. Empty uses 10 minutes. |
Keep the connection string out of files that are shared – it contains the password of the database. On container platforms, set it from a secret.
Application server¶
| Setting | Default | Meaning |
|---|---|---|
ApplicationServer:MultiServerMode |
false |
true when more than one server works with the same database. See Running several servers. |
ApplicationServer:InstanceId |
a new random id at every start | Name of the server in monitoring and in the history. Must be unique per server. |
ApplicationServer:ProcessScheduledActions |
true |
Runs the scheduled actions on this server |
ApplicationServer:ProcessMailboxListeners |
true |
Polls the mailboxes of the mailbox listeners on this server |
ApplicationServer:ManagementPort |
empty | Port on which the health and metrics endpoints answer. Empty: on every port. |
With a single server, keep the defaults. With several servers, exactly one server may process scheduled actions and mailbox listeners.
Data protection keys¶
nexxcite encrypts the sign-in cookie, form tokens and stored tokens of connected services (OAuth 2.0) with a set of keys.
| Setting | Default | Meaning |
|---|---|---|
DataProtection:KeyRingPath |
empty | Directory in which the keys are kept. Empty keeps them in memory only. |
DataProtection:ApplicationName |
nexxcite |
Name the keys are bound to. Must be the same on every server. Only change it to separate two installations that share a directory. |
Without a directory, every restart signs all users out, and stored tokens of connected services
can no longer be read – such connections have to be authorised again. With MultiServerMode, the
directory is mandatory and must be shared by all servers; without it, nexxcite does not start.
Back up the directory. Whoever can read it can decrypt sign-in cookies, so protect it like a password.
Secrets in definitions¶
Passwords, API keys and other secrets of credentials are encrypted with a key of their own, separate from the data protection keys above. The encryption is switched off by default.
| Setting | Default | Meaning |
|---|---|---|
DefinitionSecrets:Enabled |
false |
Encrypts secrets when a definition is stored. |
DefinitionSecrets:Key |
empty | 32 random bytes, Base64 encoded. Must be the same on every server. |
DefinitionSecrets:PreviousKeys |
empty | Keys used before the current one, to read what they encrypted. |
Keep a copy of the key. Without it, every encrypted secret has to be entered again. See Secrets for switching the encryption on and replacing the key.
Forwarded headers¶
When a proxy forwards requests to nexxcite, the proxy is the sender as far as nexxcite can see. The
headers X-Forwarded-For and X-Forwarded-Proto tell nexxcite the address of the client and whether
the original request used HTTPS. nexxcite only trusts them when configured to.
| Setting | Default | Meaning |
|---|---|---|
ForwardedHeaders:Enabled |
false |
Trusts the forwarded headers. Leave it off only if nothing runs in front of nexxcite. |
ForwardedHeaders:Headers |
XForwardedFor,XForwardedProto |
Which headers are trusted, separated by commas: XForwardedFor, XForwardedProto, XForwardedHost, XForwardedPrefix or All |
ForwardedHeaders:ForwardedForHeaderName |
empty (X-Forwarded-For) |
The header the client address is read from. Set it when your proxy or CDN passes the client on in a header of its own, for example X-Real-IP, CF-Connecting-IP or True-Client-IP. Only used while XForwardedFor is among the trusted headers. |
ForwardedHeaders:ForwardLimit |
empty (1) | Number of proxies in a row whose entries are processed. Count every hop (a firewall, a load balancer and an ingress are three). 0 means unlimited. |
ForwardedHeaders:KnownProxies |
empty | IP addresses of trusted proxies, separated by commas |
ForwardedHeaders:KnownNetworks |
empty | Networks of trusted proxies in CIDR notation, for example 10.0.0.0/8 |
ForwardedHeaders:TrustAllProxies |
false |
Trusts every sender instead of the two lists above |
Example – one reverse proxy in the network 10.0.4.0/24: set Enabled to true and
KnownNetworks to 10.0.4.0/24.
Use TrustAllProxies only when nexxcite cannot be reached other than through the proxy (for
example secured by a firewall or network policy). Otherwise a caller could fake their address.
Example – a CDN that passes the client in CF-Connecting-IP: set Enabled to true,
Headers to XForwardedFor,XForwardedProto, ForwardedForHeaderName to CF-Connecting-IP, and
list the addresses of the CDN in KnownNetworks.
Symptoms of a missing configuration: every user appears with the address of the proxy – also in
the authentication log – all
users share one limit of sign-in attempts (see LoginProtection below), and nexxcite may consider
requests as plain HTTP although the browser uses HTTPS – which can lead to redirect loops.
Health and metrics endpoints¶
nexxcite offers endpoints for the probes of a container platform and for metrics. They answer
without sign-in and do not reveal any details – the health endpoints answer only with the status word
(Healthy, Degraded or Unhealthy).
| Endpoint | Checks | Use |
|---|---|---|
/health/startup |
nothing – answers once the start (migration included) is complete | Startup probe: wait until it answers |
/health/live |
whether the process answers | Liveness probe: restart the server if it fails |
/health/ready |
whether the database can be reached | Readiness probe: send no requests while it fails |
/metrics |
– | Metrics in Prometheus format |
/health/live deliberately does not check the database: an outage of the database would otherwise
restart every server in turn without fixing anything.
| Setting | Default | Meaning |
|---|---|---|
ApplicationServer:ManagementPort |
empty | When set, the four endpoints answer only on this port. nexxcite must also listen on it, for example with ASPNETCORE_HTTP_PORTS=8080;8081. Do not publish this port to users. |
Telemetry:MetricsEnabled |
true |
Collects metrics and serves them under the metrics route |
Telemetry:MetricsRoute |
/metrics |
Address of the metrics |
Telemetry:OtlpEndpoint |
empty | Additionally sends metrics (and traces, if switched on) to an OpenTelemetry collector, for example http://collector:4317 |
Telemetry:TracesEnabled |
false |
Records traces of requests, outgoing HTTP calls and database statements. Only useful with a collector. |
Telemetry:ServiceName |
nexxcite |
Name the servers report under. The InstanceId is added to tell the servers apart. |
The metrics include requests and their duration, open connections, active user connections (Blazor
circuits), memory and thread pool figures. The names of the metrics follow the .NET runtime and may
change with new .NET versions; check them against /metrics of your installation before you build
alerts or scaling rules on them.
Without ManagementPort, the endpoints are reachable for everybody who can reach nexxcite. Set the
port or block the paths at your proxy.
Definition cache and messaging¶
| Setting | Default | Meaning |
|---|---|---|
DefinitionCache:Provider |
InMemory |
InMemory or Redis |
DefinitionCache:RedisConnectionString |
empty | Address of the Redis, for example redis:6379,password=… |
DefinitionCache:RedisKeyPrefix |
nexxcite:defs: |
Prefix of all keys nexxcite writes to Redis |
DefinitionCache:TimeToLiveSeconds |
1800 | How long an unused cache entry is kept |
DefinitionCache:TimeToLiveJitterSeconds |
10 | Random offset added to the lifetime |
DefinitionCache:Disabled |
false |
Switches the cache off. For diagnosis only – nexxcite is not usable like this. |
Messaging:Provider |
None |
None or Redis. (Nats is not available; nexxcite does not start with it.) |
Messaging:ConnectionString |
empty | Address of the Redis for the message bus |
Messaging:SubjectPrefix |
nexxcite |
Servers with the same prefix see each other's messages. Use a different prefix per installation that shares a Redis. |
A single server needs none of this. For several servers, see Running several servers.
Execution history¶
nexxcite records every run of background processing (background actions, scheduled actions, mailbox listener rounds that handled mails) and shows it under Monitoring → Background Processes. See Monitoring.
| Setting | Default | Meaning |
|---|---|---|
ExecutionHistory:Enabled |
true |
Records the history. Off, nothing new is recorded; the health of the servers is still shown. |
ExecutionHistory:LogCaptureMode |
OnFailure |
Whether the log of a run is stored: Never, OnFailure (failed runs and runs with warnings) or Always. Scheduled actions, rules, mailbox listeners and actions can override it. |
ExecutionHistory:MaxLogEntries |
2000 | Lines of the log kept per run |
ExecutionHistory:LogCaptureMinLevel |
Debug |
Lowest level of the captured log |
ExecutionHistory:MaxProtocolNodes |
2000 | Size limit of the stored protocol of a run |
ExecutionHistory:HeartbeatIntervalSeconds |
15 | Interval in which every server reports its state |
ExecutionHistory:HousekeepingIntervalMinutes |
5 | Interval of the clean-up (failed servers, retention) |
ExecutionHistory:JobQueueProbeIntervalMinutes |
5 | Interval of a small test job that checks whether the job queue of a server executes jobs. 0 switches it off. |
ExecutionHistory:ServerLostAfterMinutes |
10 | After this time without a sign of life, a server is considered lost |
ExecutionHistory:ServerDisplayHours |
6 | Hours a stopped or lost server is still shown |
ExecutionHistory:ServerRetentionDays |
2 | Days after which a stopped or lost server is removed |
ExecutionHistory:MetricsSnapshotIntervalMinutes |
5 | Interval in which every server stores its figures for the history charts. 0 switches it off. |
ExecutionHistory:MetricsRetentionDays |
90 | Days the figures for the history charts are kept |
Always makes the history grow quickly. Use it for a limited time to analyse a problem.
How long runs and their logs are kept is not set here but in the global settings: one value for the installation, because every server cleans the same history.
Processes of event based workflow rules¶
The processes of event based workflow rules wait in the
database and are continued by the servers with ApplicationServer:ProcessScheduledActions = true.
| Setting | Default | Meaning |
|---|---|---|
RuleProcesses:PollIntervalSeconds |
10 | How often a server looks for processes whose wait time has passed |
RuleProcesses:PollBatchSize |
100 | How many due processes one look continues before the next one takes over |
RuleProcesses:StaleClaimMinutes |
10 | After this time, a process a stopped server was continuing is taken over by another server |
RuleProcesses:MaxAttempts |
5 | Attempts to continue a process whose step fails, before it ends with "Failed" |
RuleProcesses:RetryDelayMinutes |
[1, 5, 15, 60] |
Minutes to wait before each attempt; the last value repeats |
RuleProcesses:LoopLimit |
100 | Items a process may pass without a wait before it is stopped as a loop |
How long finished processes are kept is set in the global settings, see also Event based workflow rules.
Licensing¶
The license itself is entered in the global settings. The timings around it are described in Licensing.
Further settings¶
| Setting | Default | Meaning |
|---|---|---|
LoginProtection:MaxAttemptsPerWindow |
20 | Sign-in attempts one caller (one address) may make within the window, regardless of the account. See Sign-in and sessions. |
LoginProtection:WindowInSeconds |
60 | Length of that window |
ApiAuthentication:SigningKey |
empty | Key the access tokens of the REST API are signed with, at least 32 characters. Empty switches the REST API sign-in off. Must be the same on every server. See API access and credentials. |
ApiAuthentication:Issuer |
nexxcite |
Issuer written into the access tokens |
ApiAuthentication:Audience |
nexxcite-api |
Audience written into the access tokens |
ApiAuthentication:AccessTokenLifetimeInSeconds |
3600 | Lifetime of an access token |
ApiDocumentation:Enabled |
true |
Serves the description (OpenAPI) and the interactive documentation of the REST API. Both are reachable without sign-in. See REST API. |
ApiDocumentation:UiRoute |
api/v1/docs |
Address of the interactive documentation |
ApiDocumentation:DocumentRoute |
api/{documentName}/openapi.json |
Address of the OpenAPI document |
ActionRuntime:UIInteractionTimeoutSeconds |
120 | How long a step of an action waits for the user interface to report that, for example, a form was opened. Steps that wait for the user (prompts, popups with buttons) are not affected; a popup without buttons is – see Show Popup. |
ActionRuntime:MaxRecursionDepth |
5 | How often the same action may be nested within its own run before it ends with an error |
ActionRuntime:MaxActionCallDepth |
25 | How many actions one chain of calls may contain at all |
BusinessRuleRuntime:MaxFieldChangeDepth |
50 | How many field changes may be nested within one another – a value that starts a rule, whose value starts the next rule and so on. 0 switches the limit off. |
BusinessRuleRuntime:MaxRepeatedFieldChanges |
5 | How often the same rule may set the same field of the same record within the changes it started itself. 0 switches the limit off. |
The limits of ActionRuntime protect against endless loops – for example an action that saves a
record, which triggers a rule that starts the same action again. A single action can raise them in
its own settings. The limits of BusinessRuleRuntime do the same for business rules that keep
setting each other's fields; a change beyond them is not made and is written to the application log
(see Business rules, section 7).
6. Limitations¶
- The database must be Microsoft SQL Server. Other databases are not supported.
- nexxcite stores images and files only in Azure Blob Storage or an S3-compatible storage, not on the file system of the server.
- Changes to the configuration of the application are read when the server starts. Apart from the log levels, they take effect only after a restart. Settings made in the Admin Studio take effect without a restart.
- There is no separate tool to roll back a schema migration. Restore the database backup if you have to return to a previous version.
7. Frequently asked questions¶
Where do I find the password of the default administrator?
In the log of the first start: INFO: Default user created with password: "…". The line appears
only when the user is created.
Do I have to create the tables myself? No. Only the empty database and a login that may create tables. nexxcite creates and updates the tables at every start.
All users are signed out after every restart. Why?
DataProtection:KeyRingPath is not set, or the directory is not kept across restarts (for example a
directory inside the container). Point it at a persistent volume.
The browser reports "too many redirects" behind our proxy.
Most likely the proxy terminates HTTPS, but nexxcite does not trust its forwarded headers. Configure
the section ForwardedHeaders.
Do changes to appsettings.json take effect while nexxcite runs?
No, apart from the log levels. Restart the server after changing the configuration.