Installation & Deployment
This guide covers building and deploying the OneIoT UI Portals — the Angular Support Portal and Management Portal single-page applications, served by Nginx from a single Docker container.
Everything described here is checked in to this repository: the image definition
(docker/Dockerfile), the Nginx template (docker/ops/nginx.conf), the container entrypoint
(docker/ops/entrypoint.sh), the Compose examples (docker/docker-compose.yml,
docker/ui-only/docker-compose.yml) and the CI pipeline that builds and pushes the image
(.github/workflows/build-callable.yml).
1. Overview
The portals are a presentation layer only. All data operations go to exactly one service: the UI Backend REST API. Everything else (database, Hazelcast, ClickHouse, FTACS) is reached through the backend. This guide therefore deploys the portals container and nothing else — the backend and all infrastructure are treated as pre-existing prerequisites and are deployed by their own guides.
Unlike earlier releases, the browser does not call the backend directly: Nginx inside this
container reverse-proxies /iot-webservice/ (and /ai-agent/, /grafana-ro/) to the upstream
services, and the entrypoint patches the Angular runtime config to the relative path
/iot-webservice/. One consequence: the upstream URLs are container-side settings and only have to
be reachable from the container — not from end-user browsers.
The container image bundles:
-
Support Portal — end-user support portal (Angular project
support-center, built todist/support-center, served at/support-portal/) -
Management Portal — administrator portal (Angular project
management-console, built todist/management-console, served at/management-portal/) -
Nginx — serves both apps, terminates HTTP/HTTPS and proxies the upstream APIs
-
an entrypoint script that renders
nginx.conffrom its template, patches the Angular runtime config, uploads portal permissions to the backend, then starts Nginx
|
The Angular project names ( |
| Item | Value |
|---|---|
Image |
|
Serves |
Support Portal + Management Portal (Angular 19 SPAs) |
Base image |
|
Talks to |
UI Backend REST API (required), AI Agent and Grafana (optional, proxied) |
HTTP port |
|
HTTPS port |
|
Two service URLs are exposed once the container is running:
-
Support Portal —
http://<host>:8880/support-portal/ -
Management Portal —
http://<host>:8880/management-portal/
2. Prerequisites
2.1. Host Requirements
The portals container is lightweight: static assets served by Nginx, no JVM and no local database. Size the host for the UI Backend and the infrastructure separately.
| Component | Minimum | Recommended | Notes |
|---|---|---|---|
Docker Engine |
20.10 |
Latest stable |
Required. The portals ship only as a container image. |
Docker Compose |
2.0 |
Latest stable |
Used by every command in this guide. |
RAM |
256 MB |
512 MB |
Static assets and Nginx only. |
Disk |
1 GB |
5 GB |
Image plus Nginx access/error logs; size for the retained log volume. |
2.2. Required External Dependencies
These services must be reachable from the container. None of them are deployed by this guide.
| Component | Why It Is Needed | Required |
|---|---|---|
UI Backend ( |
The target of every Angular API call, proxied by Nginx at |
Yes |
AI Agent ( |
Backs the in-app AI assistant, proxied at |
No |
Grafana |
Backs the embedded system-overview dashboards, proxied at |
No |
The UI Backend in turn needs its own database (MySQL or Oracle), Hazelcast, FTACS and — for QoE data — ClickHouse. The portals reach none of them directly. Those dependencies, their versions and their ports belong to the UI Backend deployment guide.
2.3. Supported Operating Systems
| Deployment | Operating system |
|---|---|
Docker |
Linux (recommended), macOS, or Windows with WSL2 |
The published image is built for linux/amd64 — the default platforms input of
.github/workflows/build-callable.yml. Another architecture requires a CI run with an explicit
platforms value.
2.4. Registry Access
Network access to the hub.friendly-tech.com Docker registry, or offline image archives — see
Offline Servers.
3. Network Requirements
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
Portals host |
|
HTTP / HTTPS |
End-user browsers load both portals; all API traffic is proxied through the same origin. |
UI Backend |
as configured in |
HTTP / HTTPS |
Nginx proxy for |
AI Agent |
as configured in |
HTTP |
Nginx proxy for |
Grafana |
as configured in |
HTTPS |
Nginx proxy for |
|
|
HTTPS |
Pull the portals image, unless an offline archive is used. |
|
Because the browser talks to the portals origin only, the upstream addresses do not have to be
resolvable from client machines. |
3.1. Docker Networking
All upstream calls now originate in the container, so name resolution follows Docker’s rules:
-
A service name (
http://ui-backend:8880/iot-webservice/, the entrypoint default) resolves only when both containers are on the same Docker network. Compose puts the services of one project on that project’s default network; a backend started by a different Compose project needs an explicit shared external network. -
A routable host address (
http://192.168.1.58:8881/iot-webservice/, as indocker/docker-compose.yml) always works and is the simplest choice for a portals-only host.
The /ai-agent/ and /grafana-ro/ locations resolve their upstreams at request time: the
entrypoint reads the first nameserver from /etc/resolv.conf and injects it as the Nginx
resolver, which works in both Docker (127.0.0.11) and Kubernetes (CoreDNS). A restart is
therefore not needed when those upstream IP addresses change.
4. Registry Authentication
All portals images are pulled from hub.friendly-tech.com. Authenticate once per host before the
first docker compose up.
docker login hub.friendly-tech.com
Enter the read-only pull credentials when prompted:
| Field | Value |
|---|---|
Username |
|
Password |
|
|
The |
Alternatively, log in non-interactively:
echo "fokxuw-fymte1-taSxyc" | docker login hub.friendly-tech.com -u readonly --password-stdin
Verify authentication:
docker info | grep -A 5 Registry
4.1. Offline Servers
When the portals host cannot reach hub.friendly-tech.com, pull the image on a machine that does
have registry access, export it to an archive, transfer it, and load it on the offline host. The
only image for this deployment is hub.friendly-tech.com/ui/portals:<tag>.
|
Pass an explicit |
-
On the offline host, find out which architecture it runs — this is the value you pass as
PLATFORMbelow. Ask Docker itself, since it reports what the daemon will actually accept:docker version --format '{{.Server.Arch}}'If Docker is not installed there yet, use the operating system instead —
uname -mon Linux, orecho $env:PROCESSOR_ARCHITECTUREin PowerShell on Windows. Map the result:docker versionreportsuname -m/ Windows reportsUse as PLATFORMamd64x86_64/AMD64linux/amd64arm64aarch64/ARM64linux/arm64On Windows with Docker Desktop,
{{.Server.Arch}}reports the architecture of the Linux VM that actually runs the containers — which is the value you want, not the Windows host’s own architecture. -
On a machine with registry access, log in. Run this on its own — it prompts for a password, so anything pasted after it on the same go would be swallowed as input:
docker login hub.friendly-tech.com -
Pull and export the image. Paste the whole block as-is; the only lines to change are
PLATFORMandTAGon top.Linux / macOS (bash):
PLATFORM=linux/amd64 # <-- from step 1 TAG=<tag> # <-- your pinned tag docker pull --platform "$PLATFORM" "hub.friendly-tech.com/ui/portals:$TAG" docker save "hub.friendly-tech.com/ui/portals:$TAG" | gzip > "portals-$TAG.tar.gz"Windows (PowerShell):
$PLATFORM = "linux/amd64" # <-- from step 1 $TAG = "<tag>" # <-- your pinned tag docker pull --platform $PLATFORM "hub.friendly-tech.com/ui/portals:$TAG" docker save -o "portals-$TAG.tar" "hub.friendly-tech.com/ui/portals:$TAG"On Windows, always write the archive with
docker save -o <file>. Piping or redirectingdocker savefrom PowerShell (docker save … > file.tar) corrupts the archive, because the PowerShell pipeline re-encodes the stream as text instead of passing raw bytes.docker loadthen fails withunexpected EOForinvalid tar header. To compress for transfer, use the bundledtar.exe(Windows 10 1803+ / Server 2019+):tar.exe -czf portals.tar.gz portals-$TAG.tar. -
Transfer the archive to the offline host via
scpor removable media, together withcompose.yml,.env,portals/.envand the certificate and key inportals/ssl/. -
On the offline host, load the archive and start the service. Set
TAGto the same value you used above:Linux / macOS (bash):
TAG=<tag> gzip -dc "portals-$TAG.tar.gz" | docker load docker compose up -d portalsWindows (PowerShell):
$TAG = "<tag>" docker load -i "portals-$TAG.tar" docker compose up -d portals
Confirm the image is present before starting, so a missing or mis-architected image fails here rather than mid-startup:
docker images hub.friendly-tech.com/ui/portals
Upgrades use the same flow: pull the new tag on the connected machine, transfer and load the
archive, then docker compose up -d portals. See Stack Management.
5. Preparation
5.1. Directory Structure
The portals need a TLS certificate directory and, optionally, a log directory on the host. This
follows the same ft-system base-directory convention as the rest of the platform.
|
|
mkdir -p /usr/local/ft-system/portals/{ssl,nginx/logs}
cd /usr/local/ft-system
Every volume in the Compose example is written as ${DATA_FOLDER:-.}/…, so with DATA_FOLDER
unset the mounts resolve against the current directory — which is why the cd above is what
actually fixes the install root. Setting DATA_FOLDER in the shell is not required, and relying on
it is fragile: the value is lost in the next login shell. To pin the path independently of the
working directory, set DATA_FOLDER in the .env file next to compose.yml — Compose reads that
file for variable interpolation.
5.1.1. Directory Layout
/usr/local/ft-system/
├── compose.yml # portals service definition
├── .env # shared stack environment
└── portals/
├── .env # per-service environment
├── ssl/ # -> /etc/nginx/ssl (read-only, REQUIRED)
│ ├── friendly.crt
│ └── friendly.key
└── nginx/logs/ # -> /var/log/nginx (optional)
| Path | Content | Backup |
|---|---|---|
|
Portals service definition (see Docker Compose). |
Yes |
|
Shared stack environment — the values every service reads (see Environment Configuration). |
Yes |
|
Per-service environment — the portals host ports. |
Yes |
|
PEM certificate and key, mounted read-only at |
Yes |
|
Nginx |
No |
5.2. Environment Configuration
The platform uses a two-layer environment file architecture, and the portals follow it. The portals hold no database configuration at all.
| File | Purpose |
|---|---|
|
Shared stack environment at the install root: values common to every service — upstream addresses, the internal keyword, timezone, base data folder. In a full-stack install this is also where the database connection, ClickHouse, Hazelcast, JWT and |
|
Per-service environment: the portals' own host ports. |
Create both files:
touch /usr/local/ft-system/.env /usr/local/ft-system/portals/.env
Root .env — the variables the container itself reads (docker/ops/entrypoint.sh):
| Variable | Description | Default | Required |
|---|---|---|---|
|
Full base URL of the UI Backend REST API, including the trailing |
Yes in practice — the default only works when a container named |
|
|
Internal service-to-service keyword sent as |
empty (development example |
Yes |
|
Upstream for the |
No |
|
|
Upstream for the |
No |
|
|
Overrides the host extracted from |
derived from |
No |
|
Extra CIDR allowed to read |
|
No |
|
Container timezone. |
none (example |
No |
|
Host base directory used by the volume mounts in |
|
Yes |
FT_UI_BACKEND_URL=http://ui-backend:8880/iot-webservice/
FT_UI_BACKEND_KEYWORD=ft12Internal%^
FT_AI_AGENT_URL=http://ui-ai-agent:8080
METRICS_ALLOW_IP=10.0.0.0/8
TZ=Europe/Kiev
DATA_FOLDER=/usr/local/ft-system
|
Copy |
|
|
portals/.env — this service only:
| Variable | Description | Default | Required |
|---|---|---|---|
|
Host port mapped to the container’s HTTP port ( |
|
No |
|
Host port mapped to the container’s HTTPS port ( |
|
No |
FT_UI_HTTP_PORT=8880
FT_UI_HTTPS_PORT=8843
5.3. TLS Certificate (required)
docker/ops/nginx.conf declares listen 443 ssl unconditionally and reads the certificate from
fixed paths:
/etc/nginx/ssl/friendly.crt
/etc/nginx/ssl/friendly.key
Those files are not baked into the image — they come from the mounted portals/ssl directory.
Nginx refuses to start when either is missing, so the mount is required even when only HTTP is
published. Protocols are restricted to TLS 1.2/1.3 with ECDHE-GCM ciphers.
For a self-signed certificate (development and internal installations), use the bundled generator.
It writes ssl/friendly.crt and ssl/friendly.key relative to the current directory, valid for ten
years, with SANs for friendly-tech.com, *.friendly-tech.com, localhost and 127.0.0.1:
cd /usr/local/ft-system/portals
bash <path-to-repo>/docker/generate-cert.sh
For a CA-issued certificate, copy the PEM files under the expected names:
cp your-cert.crt /usr/local/ft-system/portals/ssl/friendly.crt
cp your-cert.key /usr/local/ft-system/portals/ssl/friendly.key
chmod 600 /usr/local/ft-system/portals/ssl/friendly.key
|
Write the literal install root, not The self-signed certificate and key checked into |
6. Deployment
6.1. Startup Dependencies
The entrypoint uploads permissions to the backend as soon as the container starts, using curl -f
under set -euo pipefail. If the backend is unreachable or rejects the upload, the entrypoint exits
and Nginx never starts. Wait for the backend before bringing the portals up:
cd /usr/local/ft-system
BACKEND_HOST=192.168.1.100 # the host behind FT_UI_BACKEND_URL
until curl -sf "http://${BACKEND_HOST}:8881/iot-webservice/swagger-ui/index.html" >/dev/null; do
echo "Waiting for UI Backend at ${BACKEND_HOST} ..."
sleep 5
done
echo "Backend is up -- starting portals"
docker compose up -d
With restart: unless-stopped the container retries on its own, so a backend that comes up shortly
afterwards resolves the situation without intervention — the wait loop simply avoids the noisy
restart cycle.
6.2. Docker Compose
Create compose.yml at the install root (/usr/local/ft-system). This defines the portals service
only — no database, Hazelcast, ClickHouse, or FTACS.
name: ft-ui-portals
services:
portals:
image: hub.friendly-tech.com/ui/portals:<tag> # <-- pin your tag
env_file:
- .env # shared stack environment
- ./portals/.env # per-service environment
ports:
- "${FT_UI_HTTP_PORT:-8880}:80" # HTTP (host -> container)
- "${FT_UI_HTTPS_PORT:-8843}:443" # HTTPS (host -> container)
volumes:
- ${DATA_FOLDER:-.}/portals/ssl:/etc/nginx/ssl:ro # required
- ${DATA_FOLDER:-.}/portals/nginx/logs:/var/log/nginx # optional
healthcheck:
test: [ "CMD", "curl", "-f", "http://127.0.0.1/support-portal/index.html" ]
interval: 10s
timeout: 5s
retries: 50
restart: unless-stopped
The repository ships two working variants of this file: docker/ui-only/docker-compose.yml (ports,
volumes, healthcheck) and docker/docker-compose.yml (local build against a routable backend
address).
What the entrypoint does. On start, docker/ops/entrypoint.sh performs four steps before Nginx
serves traffic:
-
Render the Nginx configuration.
envsubstexpandsFT_UI_BACKEND_URL,BACKEND_DOMAIN,FT_AI_AGENT_URL,FT_GRAFANA_API_URL,FT_GRAFANA_DOMAIN,METRICS_ALLOW_IPand the DNS resolver (the firstnameserverin/etc/resolv.conf) into/etc/nginx/nginx.conf.template, writing/etc/nginx/nginx.conf. -
Patch the Angular runtime config.
jqsets.server.apito the relative path/iot-webservice/in both apps, so the browser calls the portals origin and Nginx proxies onward:/usr/share/nginx/html/support-center/browser/assets/app-config.json /usr/share/nginx/html/management-console/browser/assets/app-config.json -> .server.api = "/iot-webservice/" -
Upload portal permissions to the backend. One
PUTper app to${FT_UI_BACKEND_URL}iotw/Setting/userGroup/permissions, authenticated with the internal keyword header and tagged by client type (sc= Support Portal,mc= Management Portal):curl -f --request PUT "${FT_UI_BACKEND_URL}iotw/Setting/userGroup/permissions" \ -H "X-Internal-Keyword: ${FT_UI_BACKEND_KEYWORD}" \ -H "X-Internal-Client-Type: sc" \ -F "config=@/usr/share/nginx/html/support-center/browser/assets/app-permissions-config.json;type=application/json"A missing permissions file logs
WARNand is skipped; a failed request aborts the entrypoint. -
Start Nginx (
exec nginx -g 'daemon off;').
6.3. Nginx Routes
docker/ops/nginx.conf defines a single server block listening on 80 and 443:
| Location | Behaviour |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
Nginx |
7. Verification
7.1. Startup Log
Check the container reached a healthy state:
docker compose ps
Expected:
NAME STATUS PORTS
portals healthy 0.0.0.0:8880->80/tcp, 0.0.0.0:8843->443/tcp
Confirm the entrypoint rendered the configuration, patched the apps and uploaded the permissions:
docker compose logs portals | grep -iE "generated|patched|upload|WARN"
Expected lines: nginx.conf generated with FT_UI_BACKEND_URL=…, two Patched app-config.json →
.server.api = /iot-webservice/ lines, and Upload done (sc). / Upload done (mc).
7.2. Endpoint Checks
curl -I http://<host>:8880/support-portal/
curl -I http://<host>:8880/management-portal/
# API reachability through the proxy (not a direct backend call)
curl -I http://<host>:8880/iot-webservice/
Expected: HTTP 200 for the portals, 301 for the no-slash form.
Confirm the runtime config was patched:
docker compose exec portals \
cat /usr/share/nginx/html/support-center/browser/assets/app-config.json | grep '"api"'
Expected value:
"api": "/iot-webservice/"
Finally, open both apps in a browser:
-
Support Portal —
http://<host>:8880/support-portal/ -
Management Portal —
http://<host>:8880/management-portal/
8. Port Reference
Ports are written as published → container. Across hosts you connect to the published port;
inside the Docker bridge you connect to the container port.
8.1. HTTP / HTTPS
| Port | Protocol | Purpose | Exposure |
|---|---|---|---|
|
HTTP |
Serves both portals and proxies |
Public (browsers) |
|
HTTPS |
The same origin over TLS. Configurable via |
Public (browsers) |
The portals listen on no other ports.
# Confirm the portal ports are listening on the host
netstat -tlnp | grep -E '8880|8843'
8.2. Outbound Connections
| Destination | Protocol | Purpose |
|---|---|---|
UI Backend ( |
HTTP / HTTPS |
Proxied API traffic and the entrypoint permissions upload. |
AI Agent ( |
HTTP |
Proxied |
Grafana ( |
HTTPS |
Proxied |
|
HTTPS |
Image pull ( |
8.3. HTTP Endpoints
| Method | Path | Purpose | Auth |
|---|---|---|---|
|
|
Support Portal single-page application. |
None (the app authenticates against the backend) |
|
|
Management Portal single-page application. |
None (the app authenticates against the backend) |
any |
|
Reverse proxy to the UI Backend. |
|
any |
|
Reverse proxy to the AI Agent. |
As required by the AI Agent |
|
|
Nginx |
IP allowlist |
|
|
Outbound: portal permissions upload performed by the entrypoint on start. |
|
9. Stack Management
9.1. Logs
# Container (entrypoint + Nginx)
docker compose logs -f portals
# Nginx access / error logs on the host, when /var/log/nginx is mounted.
# DATA_FOLDER is set in .env for Compose, not in your shell -- use the install root itself.
tail -f /usr/local/ft-system/portals/nginx/logs/access.log
tail -f /usr/local/ft-system/portals/nginx/logs/error.log
9.2. Start, Stop, Restart
docker compose restart portals # re-runs the entrypoint (re-renders config, re-uploads permissions)
docker compose down # stop and remove the container
docker compose up -d # (re)create
Restarting the portals is the way to apply a changed upstream URL or to re-upload permissions
after changing FT_UI_BACKEND_KEYWORD.
|
9.3. Shell Access
docker compose exec portals sh
# Inspect the rendered configuration and the served assets
docker compose exec portals cat /etc/nginx/nginx.conf
docker compose exec portals ls /usr/share/nginx/html
docker compose exec portals cat /usr/share/nginx/html/support-center/browser/assets/app-config.json
9.4. Updating the Portals
# 1. Pin the new tag in compose.yml, then:
docker compose pull
docker compose up -d
For offline servers, replace docker compose pull with the archive transfer flow in
Offline Servers, then run docker compose up -d.
10. Production Checklist
-
Change
FT_UI_BACKEND_KEYWORDfrom the development default and keep it in sync with the backend. -
Set
FT_UI_BACKEND_URLto an address reachable from the container, including the trailing/iot-webservice/. -
Provide a CA-issued certificate as
portals/ssl/friendly.crt+friendly.key; never ship the development pair fromdocker/ssl/. -
Restrict
METRICS_ALLOW_IPto the monitoring network instead of the0.0.0.0/0default. -
Pin the image tag — never deploy
:latestor:devto production. -
Verify the backend is healthy first — use the wait loop so the permissions upload succeeds.
-
Enable auto-restart —
restart: unless-stoppedis set incompose.yml. -
Configure log rotation for
portals/nginx/logs/when the log volume is mounted. -
Restrict key permissions —
chmod 600 portals/ssl/friendly.key.
11. Troubleshooting
11.1. Container Exits or Restarts Repeatedly
- Cause
-
The entrypoint aborted. Under
set -euo pipefaila failed permissions upload (curl -f) stops startup, so an unreachable backend or a rejected keyword takes the container down. A missing certificate stops Nginx itself. - Fix
-
docker compose logs portals | tail -50-
curl: (7) Failed to connect—FT_UI_BACKEND_URLis wrong or the backend is not on the container’s network; see Docker Networking. -
HTTP
401/403—FT_UI_BACKEND_KEYWORDdoes not match the backend. -
cannot load certificate "/etc/nginx/ssl/friendly.crt"— theportals/sslmount is missing or empty; see TLS Certificate (required).
-
11.2. UI Loads but Shows Connection Errors or 404s
- Cause
-
The
/iot-webservice/proxy cannot reach the backend, or.server.apiwas not patched. - Fix
-
docker compose exec portals cat /usr/share/nginx/html/support-center/browser/assets/app-config.json | grep '"api"' curl -I http://<host>:8880/iot-webservice/ docker compose logs portals | grep -i "nginx.conf generated"-
Expected
.server.api:/iot-webservice/. A full URL means the image ran with an older entrypoint. -
502/504from the proxy: the backend is down, orFT_UI_BACKEND_URLpoints at an address the container cannot resolve.
-
11.3. Users Cannot Access Expected Features
- Cause
-
The startup permissions upload was skipped, so the backend still holds the previous permission set.
- Fix
-
docker compose logs portals | grep -i "upload"-
WARN: permissions file … not found (skipping sc upload)— the image does not containapp-permissions-config.json; use a correct image. -
No upload lines at all — the entrypoint aborted earlier; see Container Exits or Restarts Repeatedly.
-
Re-run the upload by restarting:
docker compose restart portals.
-
11.4. Blank Page or 404s on Assets
- Cause
-
The applications were built with a
--base-hrefthat does not match the served path, or the image was built from a staledist/. - Fix
-
-
Confirm the deployed
index.htmlcarries the right base:docker compose exec portals grep '<base' /usr/share/nginx/html/support-center/browser/index.html— expected/support-portal/. -
Rebuild following Building the Image — Local Build, keeping the
--base-hrefvalues.
-
11.5. AI Assistant or Embedded Dashboards Do Not Load
- Cause
-
The optional upstreams are unset, unreachable, or disabled in the backend settings.
- Fix
-
-
The AI assistant appears only when the backend setting
AIServiceConnectionURLis set and the user group has the AI permission; its request path is/ai-agent/. -
Grafana dashboards use the URLs stored in backend settings and the
/grafana-ro/proxy, which always calls the upstream over HTTPS. -
Check the rendered upstreams:
docker compose logs portals | grep "nginx.conf generated".
-
11.6. Port Already in Use on 8880 or 8843
- Cause
-
Another process is bound to the host port.
- Fix
-
sudo lsof -i :8880 # then either free the port or change FT_UI_HTTP_PORT / FT_UI_HTTPS_PORT in portals/.env
11.7. HTTPS on 8843 Fails to Start
- Cause
-
Missing or unreadable certificate/key.
nginx.confalways declareslisten 443 ssl, so this stops the whole container, not only HTTPS. - Fix
-
-
Confirm
portals/ssl/friendly.crtandportals/ssl/friendly.keyexist and are readable inside the container (the mount is read-only by design). -
The file names are fixed by
nginx.conf— a certificate namedserver.crtis not picked up; see TLS Certificate (required). -
Check Nginx errors:
docker compose logs portals | grep -i ssl, orportals/nginx/logs/error.logwhen the log volume is mounted.
-
11.8. Getting Support
Collect the following before contacting Friendly Tech support:
-
compose.yml,.envandportals/.env(maskFT_UI_BACKEND_KEYWORD) -
docker compose logs portals(full) -
portals/nginx/logs/error.log(last 200 lines), when mounted -
The image tag in use (
docker compose images) -
Output of the
.server.apicheck in Endpoint Checks
12. Related Documentation
-
Running the apps from source, without the container — see Contributing.
-
Configuration — the
app-config.jsonruntime contract patched by the entrypoint. -
API Integration — how the apps call
/iot-webservice/. -
Architecture — where the portals sit relative to the UI Backend and the platform services.
-
Observability — healthcheck, logs and
/nginx_status.