Installation & Deployment

This guide covers deploying the UI Backend — the REST API service of the OneIoT Web platform (hub.friendly-tech.com/ui/backend) — on a dedicated host with Docker.

For HTTPS/TLS setup, see SSL Certificate Configuration. For image lifecycle details, see Docker Image Management Guide.

1. Overview

This guide deploys a single container, ui-backend, from the image hub.friendly-tech.com/ui/backend. Docker is the only supported deployment method — the repository builds no distribution archive, and the JRE is bundled in the image.

All persistent infrastructure (database, Hazelcast, FTACS, ClickHouse) is a prerequisite: it must already be running and reachable. This guide does not deploy it — it wires the backend to it via environment variables. See Required External Dependencies and Full System Deployment for deploying that infrastructure.

The UI Backend is the only service deployed here. Its companions are documented and released separately:

  • UI Portals (Support Center & Management Console — Angular / Nginx) — the browser-facing web applications that call this backend. Deploy them as a standalone stack per OneIoT UI, or as a companion service in a combined single-host layout per All in one server deployment.

  • AI Agent — optional assistant service. See AI Agent Stack Deployment.

The backend exposes a dual-datasource REST API (FTACS schema + IOTW schema), talks to FTACS over SOAP for device operations, uses Hazelcast for distributed caching/session sharing, and queries ClickHouse for QoE analytics.

2. Prerequisites

2.1. Host Requirements

Component Minimum Recommended Notes

Docker Engine

20.10+

Latest stable

compose.ui-backend.yaml follows the Compose Specification (top-level name:, no version: key).

Docker Compose

2.0+

Latest stable

All commands in this guide use the docker compose (v2) syntax.

RAM

3 GB free

verify with the deployment owner

The backend JVM defaults to -Xms512m -Xmx2g (FT_UI_BACKEND_JAVA_RAM).

Disk space

15 GB free

verify with the deployment owner

Image, configuration under DATA_FOLDER, and rotating application logs.

Java is not required on the host — the JRE is bundled inside the container image (eclipse-temurin:25-jre). The UI Backend is distributed as a Docker image only; there is no supported bare-metal package for it.

2.2. Required External Dependencies

These services must be installed, running, and reachable from this host before the UI Backend starts. None of them are deployed by this guide.

Component Minimum Version Why It Is Needed Port Required

MySQL or Oracle

MySQL 8.0+ / Oracle 21c+

Main application database. Without it the backend cannot start — both the FTACS and the IOTW schema must be reachable.

3306 (TCP, MySQL) / 1521 (TCP, Oracle)

Yes

Hazelcast

5.5

Distributed cache and session sharing between backend instances. Without it the backend has no shared cache or session store.

5701 (TCP)

Yes

FTACS

verify with the FTACS release owner

ACS SOAP web service. Without it device provisioning, parameter reads and firmware management fail.

8080 (HTTP)

Yes

FT Device Network Service

Released with the backend version

Device-network REST API (network scores, Wi-Fi management) the backend calls at FT_DEVICE_NETWORK_URL. Deployed from its own stack — see Device Network Service. It does not have to run on this host, but its address must answer, otherwise the backend logs UnknownHostException: Failed to resolve 'ft-device-network-service'.

8383 (HTTP)

Yes

ClickHouse

26.2

QoE analytics store. Queries use the native protocol; Flyway migrates the analytics schema over HTTP at startup.

9000 (native TCP) / 8123 (HTTP)

No (optional)

UI Portals (separate stack)

Released with the backend version

Angular front-ends that consume this backend’s REST API. Deployed from compose.portals.yaml; the backend starts without them.

8880 (HTTP) / 8843 (HTTPS)

No (optional)

Hazelcast 5.5 is the client library version compiled into the backend (com.hazelcast:hazelcast 5.5.0 in pom.xml). The ClickHouse client versions are com.github.housepower:clickhouse-native-jdbc 2.7.1 (queries, native TCP) and com.clickhouse:clickhouse-jdbc 0.9.6 (Flyway migrations, HTTP).

ClickHouse 26.2 is the supported version. The development reference compose in this repository (iot-web-app-build/docker/docker-compose.yml) still pins clickhouse/clickhouse-server:24.8; deploy 26.2 unless instructed otherwise — verify the pinned tag with FT DevOps.

2.3. Supported Operating Systems

Deployment Operating system

Docker (Compose or docker run)

Linux (recommended), macOS, or Windows with WSL2

2.4. Registry Access

Network access to the hub.friendly-tech.com Docker registry (or offline image archives — see Offline Servers).

Access to the FT_DISK folders listed in Download Deployment Files for the deployment files.

3. Network Requirements

The backend opens the following outbound connections. Ports are the defaults shipped in the shared .env and in ui-backend/.env.

Destination Port Protocol Purpose

MySQL or Oracle database

3306 / 1521

TCP (JDBC)

FTACS and IOTW schemas (DB_HOST, DB_PORT).

Hazelcast members

5701

TCP

Cluster membership, distributed cache and sessions (HZ_MEMBERS).

FTACS application server

8080

HTTP or HTTPS (SOAP)

Device provisioning, parameter reads, firmware management (ACS_WEB_SERVICE_HOST, ACS_WEB_SERVICE_PORT).

ClickHouse

9000

native TCP

QoE analytics queries (CLICKHOUSE_HOST, CLICKHOUSE_PORT).

ClickHouse

8123

HTTP

Flyway migration of the analytics schema at startup (CLICKHOUSE_HTTP_PORT).

Inbound traffic is limited to the published REST API port — see Port Reference.

If FTACS is configured to push event notifications back to this backend, the address in FTACS_WEBHOOK_BASE_URL must be the backend’s externally reachable host and published port (default 8881), not the internal 8880. See Environment Variable Reference.

3.1. Docker Networking

If the database, Hazelcast, FTACS, or ClickHouse runs on the same host but outside Docker, use host.docker.internal as the hostname in the .env file instead of localhost:

DB_HOST=host.docker.internal
HZ_MEMBERS=host.docker.internal:5701
ACS_WEB_SERVICE_HOST=host.docker.internal
CLICKHOUSE_HOST=host.docker.internal

Inside a container, localhost points to the container itself, not the host. host.docker.internal resolves to the host’s gateway address. compose.ui-backend.yaml already maps it:

extra_hosts:
  - "host.docker.internal:host-gateway"

On Docker Desktop (macOS, Windows) this works out of the box; on Linux the extra_hosts mapping above is required (and is already present in compose.ui-backend.yaml). Alternatively, use the host’s real LAN IP address.

4. Registry Authentication

All UI Backend images are pulled from hub.friendly-tech.com as hub.friendly-tech.com/ui/backend:<version>. Authenticate once per host before the first docker compose -f compose.ui-backend.yaml up.

docker login hub.friendly-tech.com

Enter the read-only pull credentials when prompted:

Field Value

Username

readonly

Password

fokxuw-fymte1-taSxyc

The readonly account provides pull-only access to the published images. It cannot push.

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 UI Backend 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 connected machine can run Linux, macOS, or Windows — commands are given for both shells below. It does not need to be the same platform as the offline host.

An explicit --platform matching the offline host’s architecture is required. Without --platform, docker pull selects the host architecture, which may not match the target. On an Apple Silicon (arm64) Mac or an arm64 Windows machine without --platform, the resulting archive will be arm64 and will fail with a platform does not match warning on amd64 servers. The release pipeline can build multi-architecture images (linux/amd64, linux/arm64) — see .github/workflows/build-all-services.yml — but the default build target is linux/amd64, so a specific published tag is not guaranteed to include arm64. If you deploy to arm64, confirm the tag you pull actually contains an arm64 manifest (verify with the release owner), or build that platform explicitly. The examples below use linux/amd64; step 1 shows how to read the correct value off the offline host.

  1. On the offline host, find out which architecture it runs — this is the value you will pass as PLATFORM below. 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 -m on Linux, or echo $env:PROCESSOR_ARCHITECTURE in PowerShell on Windows. Map the result:

    docker version reports uname -m / Windows reports Use as PLATFORM

    amd64

    x86_64 / AMD64

    linux/amd64

    arm64

    aarch64 / ARM64

    linux/arm64

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

  2. 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
  3. Pull and export the image. Paste the whole block as-is; the only lines to change are PLATFORM and TAG on top.

    Linux / macOS (bash):

    PLATFORM=linux/amd64
    TAG=<version>
    
    docker pull --platform "$PLATFORM" "hub.friendly-tech.com/ui/backend:$TAG"
    docker save "hub.friendly-tech.com/ui/backend:$TAG" | gzip > "ui-backend-$TAG.tar.gz"

    Windows (PowerShell):

    $PLATFORM = "linux/amd64"
    $TAG = "<version>"
    
    docker pull --platform $PLATFORM "hub.friendly-tech.com/ui/backend:$TAG"
    docker save -o "ui-backend-$TAG.tar" "hub.friendly-tech.com/ui/backend:$TAG"

    On Windows, always write the archive with docker save -o <file>. Piping or redirecting docker save from PowerShell (docker save …​ > file.tar) corrupts the archive, because the PowerShell pipeline re-encodes the stream as text instead of passing raw bytes. docker load then fails with unexpected EOF or invalid tar header. To compress for transfer, use the bundled tar.exe (Windows 10 1803+ / Server 2019+): tar.exe -czf ui-backend.tar.gz ui-backend-$TAG.tar.

  4. Transfer the archive to the offline host, together with the deployment files listed in Download Deployment Files (compose.ui-backend.yaml, the shared .env, ui-backend/.env, and the ui-backend/config/ directory).

  5. On the offline host, load the archive and start the service. Set TAG to the same value you used above:

    Linux (bash):

    TAG=<version>
    
    gzip -dc "ui-backend-$TAG.tar.gz" | docker load
    docker compose -f compose.ui-backend.yaml up -d ui-backend

    Windows (PowerShell):

    $TAG = "<version>"
    
    docker load -i "ui-backend-$TAG.tar"
    docker compose -f compose.ui-backend.yaml up -d ui-backend

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/backend

Upgrades use the same flow: pull the new tag on the connected machine, transfer and load the archive, then docker compose -f compose.ui-backend.yaml up -d ui-backend. See Updating UI Backend.

This covers the UI Backend image only. The database, Hazelcast, FTACS, and ClickHouse are separate images on their own hosts — for the offline procedure covering a whole stack, see All in one server deployment — Offline Servers, which derives the image list from compose.yml itself.

5. Preparation

5.1. Directory Structure

Create the base directory and the backend’s config / logs folders.

/usr/local/ft-system may require sudo. Alternatively use a path in your home directory (e.g. ~/ft-system) and adjust all paths accordingly. The shared platform root is /usr/local/ft-system, with this service under ui-backend/config and ui-backend/logs — the same convention as the Full System Deployment guide; this deployment prepares only the subset needed for the UI Backend.

mkdir -p /usr/local/ft-system/ui-backend/{config,logs}
cd /usr/local/ft-system

Every volume in compose.ui-backend.yaml 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, and ui-backend/.env does not supply it either, because Compose reads env_file: into the container environment, not into its own variable interpolation. To pin the path independently of the working directory, put DATA_FOLDER in the shared .env next to compose.ui-backend.yaml — that one Compose does read for interpolation.

5.1.1. Download Deployment Files

Download the deployment files from FT_DISK and keep their file names. Every Compose file refers to .env and ./<service>/.env relative to its own location, so it goes into /usr/local/ft-system/ next to the shared .env, never into a service subdirectory, and is passed to Compose with -f (for example docker compose -f compose.ui-backend.yaml up -d).

  • FT_DISK — ONE_IOT_UI/docker/ui-backend — the dedicated Docker Compose stack of the UI Backend:

    • compose.ui-backend.yaml → /usr/local/ft-system/compose.ui-backend.yaml

    • .env (per-service layer) → /usr/local/ft-system/ui-backend/.env

    • config/ (the whole directory) → /usr/local/ft-system/ui-backend/config/

  • FT_DISK — ONE_IOT_UI/docker — the shared layer:

    • .env (shared layer) → /usr/local/ft-system/.env

    • compose.all-in-one.yaml → /usr/local/ft-system/compose.all-in-one.yaml — only when all OneIoT UI services (UI Backend, portals, FT Device Network Service, AI Agent with its PostgreSQL) run on this one host. It replaces the dedicated Compose files; the per-service files still come from each service’s own FT_DISK folder.

  • Full-system deployment only — FT System MySQL Stack or FT System Oracle Stack: compose.yml and .env of the complete platform, including the databases and the infrastructure. This guide does not use them; see All in one server deployment.

5.1.2. Directory Layout

The config/ subtree below matches the nine entries currently stored in FT_DISK. The logs/ directory is created on the host and populated by the container.

/usr/local/ft-system/                           # DATA_FOLDER
├── compose.ui-backend.yaml                      # FT_DISK: ONE_IOT_UI/docker/ui-backend/
├── .env                                         # FT_DISK: ONE_IOT_UI/docker/  (shared layer)
└── ui-backend/
    ├── .env                                    # per-service environment (from FT_DISK)
    ├── config/                                 # -> /opt/app/conf   (backup required)
    │   ├── ACSWebService.wsdl                  # FTACS SOAP web-service definition
    │   ├── ftacs.keystore                      # Backend keystore (only if backend serves HTTPS)
    │   ├── hazelcast-client.xml                # Hazelcast client configuration
    │   ├── interfaceItems.json                 # Interface seed data, loaded if present
    │   ├── logback-spring.xml                  # External logging configuration
    │   ├── mc-idp.crt                          # Management Console SAML IdP certificate
    │   ├── sc-idp.crt                          # Support Center SAML IdP certificate
    │   ├── customization/                      # Portal customization JSON
    │   │   ├── def/                            # 8 default customization files
    │   │   ├── mc/                             # 6 Management Console customization files
    │   │   └── sc/                             # 14 Support Center customization files
    │   └── ssl/                                # All-in-One TLS material; standalone portals use portals/ssl/ (only used when using HTTPS)
    │       ├── friendly.crt                    # TLS certificate
    │       └── friendly.key                    # TLS private key
    └── logs/                                   # -> /opt/app/logs   (created automatically)
Path Content Backup

compose.ui-backend.yaml

Dedicated UI Backend stack definition from FT_DISK — ONE_IOT_UI/docker/ui-backend. Its UI Backend service block, image tag, published port, environment files and volumes are shown in Docker Compose.

Yes

.env

Shared stack environment: database connection, ClickHouse, Hazelcast, FTACS credentials, inter-service hosts, and the values compose.ui-backend.yaml interpolates — the host port (FT_UI_BACKEND_HTTP_PORT) and the JVM heap (FT_UI_BACKEND_JAVA_RAM).

Yes

ui-backend/.env

Per-service environment from FT_DISK ui-backend/.env: log level, connection pools, optional integrations. The host port and the JVM heap are not here — they are in the shared .env.

Yes

ui-backend/config/

Nine FT_DISK entries: ACSWebService.wsdl, ftacs.keystore, hazelcast-client.xml, interfaceItems.json, logback-spring.xml, mc-idp.crt, sc-idp.crt, plus the customization/ and ssl/ directories shown above. Google credentials belong in ui-ai-agent/keys/, not in the UI Backend configuration.

Yes

ui-backend/logs/

Backend application logs.

No

The backend container merges configuration on startup: files you place in ui-backend/config/ take priority; any missing file is filled in from the image defaults. interfaceItems.json is always refreshed from the image.

5.2. Environment Configuration

The stack uses a two-layer environment file architecture — there is no per-database-vendor environment file. compose.ui-backend.yaml loads both layers, in this order:

env_file:
  - .env                 # shared stack environment
  - ./ui-backend/.env    # per-service environment
File Purpose

.env

Database connection (DB_HOST, DB_PORT, DB_USER, DB_PASSWORD), ClickHouse, Hazelcast, inter-service hosts and URLs, DATA_FOLDER, TZ, the FTACS credentials (ACS_USERNAME, ACS_PASSWORD), and the values compose.ui-backend.yaml reads through ${…​} interpolation — the published host port (FT_UI_BACKEND_HTTP_PORT) and JVM heap (FT_UI_BACKEND_JAVA_RAM)

ui-backend/.env

Backend log level, HikariCP pool tuning, SSL keystore, LDAP/SAML integration

FT_UI_BACKEND_HTTP_PORT and FT_UI_BACKEND_JAVA_RAM belong in the shared .env, not in ui-backend/.env. compose.ui-backend.yaml references them as ${FT_UI_BACKEND_HTTP_PORT:-8881} in ports: and ${FT_UI_BACKEND_JAVA_RAM:-…​} inside JAVA_OPTS, and Compose resolves ${…​} only from the root .env beside compose.ui-backend.yaml (and the shell), never from env_file: entries. Put in ui-backend/.env they would be loaded into the container environment but ignored by the port mapping and heap interpolation, which would silently fall back to their defaults.

MySQL is the default. Switching to Oracle is not a file swap: change DB_PROFILE, DB_HOST, DB_PORT (and the remaining variables listed in Switching to Oracle) inside the single shared .env. compose.ui-backend.yaml maps the generic DB_* names to the vendor-specific ones the backend expects (MYSQL_PORT, MYSQL_USER, ORACLE_PORT, ORACLE_SERVICE, ORACLE_USER_FTACS, …​) explicitly under environment: — see Docker Compose. The full-system compose.yml of the FT System MySQL / Oracle Stack does the same through its x-db-env YAML anchor.

5.2.1. Host Variables

Edit the shared .env and replace every ~PLACEHOLDER~ value with a real hostname or IP address before starting the service.

Placeholder Variable Description

~HOST~

FT_UI_HOST

Public hostname or IP of this server, reachable from end-user browsers and from FTACS. Use a real IP or DNS name, not localhost.

~HAZELCAST_HOST~

HZ_MEMBERS

Hazelcast cluster member address(es). Format host:port, comma-separated for multiple members.

~FTACS_HOST~

ACS_WEB_SERVICE_HOST

Hostname or IP of the FTACS application server.

~CLICKHOUSE_HOST~

CLICKHOUSE_HOST

Hostname or IP of the ClickHouse server. Leave the placeholder if ClickHouse is not deployed — analytics features are skipped.

~DB_HOST~

DB_HOST

Hostname or IP of the database server. The vendor is selected by DB_PROFILE (mysql or oracle), so a single placeholder covers both. Both FTACS and IOTW schemas must be reachable here.

Example (shared .env):

FT_UI_HOST=192.168.1.100
HZ_MEMBERS=192.168.1.50:5701,192.168.1.51:5701
ACS_WEB_SERVICE_HOST=192.168.1.60
CLICKHOUSE_HOST=192.168.1.70
DB_HOST=192.168.1.80

Replace the default passwords (ftacs, friendly, superset-12345) before deploying to any non-development environment.

5.2.2. Environment Variable Reference

Shared .env — stack variables
Variable Description Default Required

DATA_FOLDER

Base directory for persistent data and configuration on the host. Set it to the install root used by this guide, /usr/local/ft-system.

/usr/local/ft-system

Yes

TZ

Container timezone.

Europe/Kyiv

Yes

FT_UI_BACKEND_HTTP_PORT

Host port published for the backend REST API (maps to internal 8880). compose.ui-backend.yaml reads it as ${FT_UI_BACKEND_HTTP_PORT:-8881} in ports:, so it must live here in the shared .env.

8881

No (optional)

FT_UI_BACKEND_JAVA_RAM

Backend JVM heap settings. compose.ui-backend.yaml reads it as ${FT_UI_BACKEND_JAVA_RAM:-…​} inside JAVA_OPTS, so it must live here in the shared .env.

-Xms512m -Xmx2g

No (optional)

FT_UI_BACKEND_KEYWORD

Internal keyword the backend accepts for service-to-service calls (the portals use it to upload permissions). Shared with the portals and the AI Agent, so it is set once here and not repeated in ui-backend/.env.

ft12Internal#%^

Yes

DB_PROFILE

Database vendor: mysql or oracle. Selects the vendor-specific datasource configuration.

mysql

Yes

ACS_WEB_SERVICE_SCHEME

Scheme of the FTACS SOAP web service connection: http or https. For https with a self-signed certificate, add the certificate to the JVM truststore (see Application and database setup).

http

No (optional)

ACS_WEB_SERVICE_HOST

FTACS application host.

~FTACS_HOST~

Yes

ACS_WEB_SERVICE_PORT

FTACS application port.

8080

Yes

ACS_USERNAME / ACS_PASSWORD

FTACS SOAP web-service credentials (FT Configs UI: <host>:<port>/acs/ftacsws-access). compose.ui-backend.yaml passes them to the backend as ACS_WEB_SERVICE_USER / ACS_WEB_SERVICE_PASSWORD, so set both here; an unset value reaches the backend as an empty credential.

friendly / ~ACS_PASSWORD~

Yes

FTACS_WEBHOOK_BASE_URL

Backend base URL as reachable by FTACS for webhook event notifications. Set to the externally published host and port.

http://~HOST~:8881

No (optional)

HZ_MEMBERS

Hazelcast cluster members (comma-separated host:port).

~HAZELCAST_HOST~:5701

Yes

CLICKHOUSE_HOST

ClickHouse server address.

~CLICKHOUSE_HOST~

No (optional)

CLICKHOUSE_PORT

ClickHouse native protocol port used for analytics queries.

9000

No (optional)

CLICKHOUSE_HTTP_PORT

ClickHouse HTTP port used by Flyway for analytics schema migrations.

8123

No (optional)

CLICKHOUSE_DATABASE

ClickHouse database name (mapped to CLICKHOUSE_SCHEMA inside the container).

ftacs_qoe_ui_data

No (optional)

CLICKHOUSE_USERNAME / CLICKHOUSE_PASSWORD

ClickHouse credentials. Read directly only by the Flyway migration datasource; compose.ui-backend.yaml also maps them to the names the analytics datasource reads (see the warning below).

ftacs / ftacs

No (optional)

The backend reads ClickHouse credentials under two different names. The Flyway migration datasource reads CLICKHOUSE_USERNAME / CLICKHOUSE_PASSWORD, but the analytics (native TCP) datasource reads CLICKHOUSE_USER / CLICKHOUSE_PASWORD — note the single S, which is a misspelling in application.yaml, not a typo in this guide.

Setting only CLICKHOUSE_USERNAME / CLICKHOUSE_PASSWORD therefore makes migrations succeed while every QoE query authenticates with the built-in default ftacs/ftacs and fails. The compose.ui-backend.yaml block in Docker Compose maps the shared .env values onto both name pairs; keep that mapping if you write your own compose file.

ui-backend/.env — per-service variables
Variable Description Default Required

FT_UI_BACKEND_LOG_LEVEL

Backend log level: info, debug, warn, error.

info

No (optional)

SUPERSET_URL

Superset instance the backend queries for embedded dashboards.

https://superset.friendly-tech.com

No (optional)

SUPERSET_USERNAME / SUPERSET_PASSWORD

Superset credentials. Not present in the shipped env files — the backend falls back to these built-in defaults, so add both lines to ui-backend/.env whenever Superset dashboards are used.

superset / superset-12345

No (optional)

HikariCP pool tuning (FTACS_MAX_POOL, IOTW_MAX_POOL, CLICKHOUSE_MAX_POOL, HIKARI_*), the SSL keystore variables and the optional LDAP/SAML settings also live in this file — see the collapsible blocks below.

Database variables in the shared .env — MySQL (DB_PROFILE=mysql)
Variable Description Default Required

DB_HOST

MySQL server address.

~DB_HOST~

Yes

DB_PORT

MySQL port. compose.ui-backend.yaml passes it to the backend as MYSQL_PORT.

3306

Yes

DB_USER

Database username (FTACS schema). compose.ui-backend.yaml passes it to the backend as MYSQL_USER.

ftacs

Yes

DB_PASSWORD

Database password (shared across schemas).

ftacs

Yes

Optional overrides let you split the FTACS and IOTW schemas onto separate hosts (MYSQL_HOST_IOTW, MYSQL_PORT_IOTW, MYSQL_SCHEMA_IOTW, and the _FTACS equivalents) or supply a full JDBC URL (MYSQL_URL_FTACS, MYSQL_URL_IOTW). All are commented out by default.

Database variables in the shared .env — Oracle (DB_PROFILE=oracle)
Variable Description Default Required

DB_HOST

Oracle server address.

~DB_HOST~

Yes

DB_PORT

Oracle listener port. compose.ui-backend.yaml passes it to the backend as ORACLE_PORT.

1521

Yes

DB_SERVICE

Oracle PDB service name. compose.ui-backend.yaml passes it to the backend as ORACLE_SERVICE.

XEPDB1

Yes

DB_USER

Database username for the FTACS schema. compose.ui-backend.yaml passes it to the backend as ORACLE_USER_FTACS.

ftacs

Yes

ORACLE_USER_IOTW

Oracle username for the IOTW schema.

iotw

Yes

DB_PASSWORD

Database password (shared across schemas).

ftacs

Yes

Optional integrations in ui-backend/.env (all commented out by default)

Every variable below ships commented out — uncomment only the block for the integration you enable. All of them are optional.

Integration Variable Description Default

OIDC login (ldap / windows)

OIDC_ENABLED

Master switch for OIDC login through Keycloak.

false

OIDC_REDIRECT_HOST

Public portals URL Keycloak redirects back to after login.

https://<PORTALS_HOST>:8843

OIDC_SC_CLIENT_ID

Support Center OIDC client ID registered in Keycloak.

support-portal

OIDC_SC_ISSUER_URI

Support Center realm issuer URL.

https://<KEYCLOAK_HOST>/realms/oneiot-sc

OIDC_MC_CLIENT_ID

Management Console OIDC client ID registered in Keycloak.

management-portal

OIDC_MC_ISSUER_URI

Management Console realm issuer URL.

https://<KEYCLOAK_HOST>/realms/oneiot-mc

SAML SSO (saml)

SAML_ENABLED

Master switch for SAML SSO login.

false

SAML_REDIRECT_HOST

Public portals URL the IdP redirects back to.

https://<PORTALS_HOST>:8843

SAML_SC_SP_ENTITY_ID

Support Center service-provider entity ID.

oneiot-sp-sc

SAML_SC_IDP_ENTITY_ID

Support Center identity-provider entity ID.

https://<KEYCLOAK_HOST>/realms/oneiot-sc

SAML_SC_IDP_SSO_URL

Support Center IdP single-sign-on URL.

https://<KEYCLOAK_HOST>/realms/oneiot-sc/protocol/saml

SAML_MC_SP_ENTITY_ID

Management Console service-provider entity ID.

oneiot-sp-mc

SAML_MC_IDP_ENTITY_ID

Management Console identity-provider entity ID.

https://<KEYCLOAK_HOST>/realms/oneiot-mc

SAML_MC_IDP_SSO_URL

Management Console IdP single-sign-on URL.

https://<KEYCLOAK_HOST>/realms/oneiot-mc/protocol/saml

SAML_ATTR_*

Assertion attribute name mappings: SAML_ATTR_USERNAME, _EMAIL, _CLIENT_TYPE, _DOMAIN, _USER_GROUP, _EXPIRE_DATE.

username, email, portal-type, domain, user-group, expire-date

SSL keystore (backend HTTPS)

SSL_KEYSTORE

Keystore path when the backend serves HTTPS directly.

${APP_HOME}/conf/ftacs.keystore

SSL_KEYSTORE_PASSWORD

Keystore password.

friendly

SSL_KEY_ALIAS

Key alias inside the keystore.

(empty)

SSL_KEY_PASSWORD

Private-key password, if different from the keystore password.

(empty)

SSL_KEYSTORE_TYPE

Keystore format.

JKS

Connection pools (HikariCP)

FTACS_MAX_POOL / FTACS_MIN_POOL

FTACS-schema pool: max / min idle connections.

30 / 5

IOTW_MAX_POOL / IOTW_MIN_POOL

IOTW-schema pool: max / min idle connections.

30 / 5

CLICKHOUSE_MAX_POOL / CLICKHOUSE_MIN_POOL

ClickHouse pool: max / min idle connections.

10 / 2

HIKARI_IDLE_TIMEOUT_MS

Idle connection timeout (ms).

60000

HIKARI_CONN_TIMEOUT_MS

Connection acquisition timeout (ms).

45000

HIKARI_MAX_LIFETIME_MS

Maximum connection lifetime (ms).

180000

HIKARI_VALIDATION_TIMEOUT_MS

Connection validation timeout (ms).

3000

OIDC / SAML secrets are files, not variables. The OIDC client secrets live in conf/sc-oidc-secret and conf/mc-oidc-secret; the SAML IdP signing certificates are conf/sc-idp.crt / conf/mc-idp.crt (mount them in the compose when SAML is enabled — see Docker Compose). For OIDC the backend JVM must also trust Keycloak’s TLS certificate (truststore via JAVA_OPTS).

Both the ldap and windows login types use the same backend OIDC client and authenticate through Keycloak — direct LDAP bind from the backend has been removed. Deploy Keycloak per Keycloak Deployment, then follow LDAP Login via Keycloak (OIDC), Windows AD SSO (Kerberos via Keycloak) or SAML SSO Login.

5.3. TLS Keystore (optional)

TLS termination for browsers is normally handled by the portals reverse proxy, so the backend serves plain HTTP on 8880 internally. Configure a backend keystore only when the backend must serve HTTPS directly.

The keystore file is method-neutral — any JKS or PKCS12 keystore works, however you obtain it (CA-issued, internal PKI, or self-signed for testing). Place it in ui-backend/config/ and point the backend at it:

SSL_KEYSTORE=${APP_HOME}/conf/ftacs.keystore
SSL_KEYSTORE_PASSWORD=<your-keystore-password>
SSL_KEYSTORE_TYPE=JKS

For full guidance across deployment scenarios, see SSL Certificate Configuration.

6. Deployment

6.1. Startup Dependencies

The backend does not manage its infrastructure and must not depends_on external containers. Before starting it, confirm each prerequisite is reachable. A simple wait-loop blocks until a dependency accepts connections.

The loops below read DB_HOST, HZ_MEMBERS and ACS_WEB_SERVICE_HOST from the shared .env. Compose reads that file for the container, but your shell does not — load it first, otherwise the variables expand to empty and the loops never terminate:

cd /usr/local/ft-system
set -a; . ./.env; set +a
# Wait for the database (MySQL example; use 1521 for Oracle)
until nc -z ${DB_HOST:-host.docker.internal} 3306; do
  echo "waiting for database..."; sleep 3
done

# Wait for Hazelcast (host taken from HZ_MEMBERS, whose format is host:port)
until nc -z ${HZ_MEMBERS%%:*} 5701; do
  echo "waiting for hazelcast..."; sleep 3
done

# Wait for FTACS
until curl -sf http://${ACS_WEB_SERVICE_HOST:-host.docker.internal}:8080 >/dev/null; do
  echo "waiting for FTACS..."; sleep 3
done

ClickHouse is optional — skip its wait-loop if analytics are not deployed. The backend starts without it and disables analytics features.

6.2. Docker Compose

compose.ui-backend.yaml is the dedicated Compose file from FT_DISK (see Download Deployment Files). Run it from /usr/local/ft-system with docker compose -f compose.ui-backend.yaml. The complete file:

name: ft-ui-backend
# UI Backend on a dedicated host. The Portals are a SEPARATE stack -- see
# compose.portals.yaml.
# DB/Hazelcast/FTACS/ClickHouse are EXTERNAL prerequisites (set in .env).
# Joins the external ft-network so the separately-composed Portals, Keycloak,
# AI Agent and FT Device Network Service reach it by container name.
# Run first: docker network create ft-network
services:
  ui-backend:
    image: hub.friendly-tech.com/ui/backend:latest   # <-- pin a released tag
    container_name: ui-backend
    ports:
      - "${FT_UI_BACKEND_HTTP_PORT:-8881}:8880"
    env_file:
      - .env
      - ./ui-backend/.env
    environment:
      USE_NOHUP: 0
      APP_HOME: "/opt/app"
      DB_VENDOR: "${DB_PROFILE}"
      SPRING_PROFILES_ACTIVE: "${DB_PROFILE}"
      DB_HOST: "${DB_HOST}"
      DB_PASSWORD: "${DB_PASSWORD:-ftacs}"
      MYSQL_PORT: "${DB_PORT:-3306}"
      MYSQL_USER: "${DB_USER:-ftacs}"
      ORACLE_PORT: "${DB_PORT:-1521}"
      ORACLE_SERVICE: "${DB_SERVICE:-XEPDB1}"
      ORACLE_USER_FTACS: "${DB_USER:-ftacs}"
      ORACLE_USER_IOTW: "${ORACLE_USER_IOTW:-iotw}"
      ACS_WEB_SERVICE_USER: "${ACS_USERNAME}"
      ACS_WEB_SERVICE_PASSWORD: "${ACS_PASSWORD}"
      CLICKHOUSE_SCHEMA: "${CLICKHOUSE_DATABASE}"
      # The analytics datasource reads CLICKHOUSE_USER / CLICKHOUSE_PASWORD (single S);
      # only Flyway reads CLICKHOUSE_USERNAME / CLICKHOUSE_PASSWORD. Map both.
      CLICKHOUSE_USER: "${CLICKHOUSE_USERNAME}"
      CLICKHOUSE_PASWORD: "${CLICKHOUSE_PASSWORD}"
      JAVA_OPTS: "${FT_UI_BACKEND_JAVA_RAM:--Xms512m -Xmx2g} -Duser.timezone=${TZ}"
    volumes:
      - ${DATA_FOLDER:-.}/ui-backend/config:/opt/app/conf
      - ${DATA_FOLDER:-.}/ui-backend/logs:/opt/app/logs
      # SAML (uncomment when enabled):
      # - ${DATA_FOLDER:-.}/ui-backend/config/sc-idp.crt:/opt/app/conf/sc-idp.crt
      # - ${DATA_FOLDER:-.}/ui-backend/config/mc-idp.crt:/opt/app/conf/mc-idp.crt
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped
    healthcheck:
      test: [ "CMD", "wget", "--spider", "-q", "-T", "5", "http://127.0.0.1:8880/iot-webservice/swagger-ui/index.html" ]
      interval: 20s
      timeout: 5s
      retries: 15
      start_period: 60s
    stop_grace_period: 45s
    networks:
      - ft-network

# Shared external network: the Portals, Keycloak and the AI Agent run as separate
# Compose projects and reach this backend by container name only over it.
# Create it once per host before the first start:
#   docker network create ft-network
networks:
  ft-network:
    external: true

The ui-backend service of the full-system compose.yml (FT System MySQL / Oracle Stack) is the same definition. It differs only where that single-project topology requires it: the database variables come from the x-db-env anchor (<<: *db-env) instead of the explicit DB_* mapping, the volumes use ./ui-backend/…​, profiles and depends_on are added, and the stack’s own ft-network replaces the external network and extra_hosts. The UI Portals are a separate stack — see OneIoT UI.

6.2.1. Shared Docker Network

When the UI Backend runs as its own Compose project and must reach another Compose project on the same host by container name — most commonly the FT Device Network Service (“ft-special”) — the ui-backend service joins the external network ft-network. Compose does not create an external network for you: create it once per host before the first docker compose -f compose.ui-backend.yaml up, otherwise the stack fails with network ft-network declared as external, but could not be found.

docker network create ft-network

compose.ui-backend.yaml declares it as external:

networks:
  ft-network:
    external: true

Any other Compose project that must talk to this backend by name attaches the same external ft-network. When every service instead runs inside a single Compose project (see All in one server deployment), Compose creates the project network automatically and this manual step is not required.

Start the backend:

cd /usr/local/ft-system
docker compose -f compose.ui-backend.yaml up -d ui-backend

Monitor startup:

docker compose -f compose.ui-backend.yaml logs -f ui-backend

6.2.2. Docker Run (Standalone)

To run the backend without Compose:

cd /usr/local/ft-system
set -a; . ./.env; set +a          # load the shared values used by the flags below

docker run -d \
  --name ui-backend \
  --env-file .env \
  --env-file ui-backend/.env \
  -e USE_NOHUP=0 -e APP_HOME=/opt/app \
  -e DB_VENDOR="$DB_PROFILE" -e SPRING_PROFILES_ACTIVE="$DB_PROFILE" \
  -e MYSQL_PORT="${DB_PORT:-3306}" -e MYSQL_USER="${DB_USER:-ftacs}" \
  -e ORACLE_PORT="${DB_PORT:-1521}" -e ORACLE_SERVICE="${DB_SERVICE:-XEPDB1}" \
  -e ORACLE_USER_FTACS="${DB_USER:-ftacs}" -e ORACLE_USER_IOTW="${ORACLE_USER_IOTW:-iotw}" \
  -e ACS_WEB_SERVICE_USER="$ACS_USERNAME" -e ACS_WEB_SERVICE_PASSWORD="$ACS_PASSWORD" \
  -e CLICKHOUSE_SCHEMA="$CLICKHOUSE_DATABASE" \
  -e CLICKHOUSE_USER="$CLICKHOUSE_USERNAME" -e CLICKHOUSE_PASWORD="$CLICKHOUSE_PASSWORD" \
  -e JAVA_OPTS="${FT_UI_BACKEND_JAVA_RAM:--Xms512m -Xmx2g} -Duser.timezone=$TZ" \
  -p "${FT_UI_BACKEND_HTTP_PORT:-8881}:8880" \
  --add-host host.docker.internal:host-gateway \
  -v /usr/local/ft-system/ui-backend/config:/opt/app/conf \
  -v /usr/local/ft-system/ui-backend/logs:/opt/app/logs \
  --restart unless-stopped \
  hub.friendly-tech.com/ui/backend:<version>

Ensure .env exists in /usr/local/ft-system and ui-backend/.env in /usr/local/ft-system/ui-backend, and that the database, Hazelcast, FTACS, and (optionally) ClickHouse are reachable before starting the container.

7. Verification

7.1. Startup Log

Check the container status first:

docker compose -f compose.ui-backend.yaml ps

Expected output:

NAME         STATUS     PORTS
ui-backend   healthy    0.0.0.0:8881->8880/tcp

Allow 30—​60 seconds after start, then inspect the logs:

docker compose -f compose.ui-backend.yaml logs ui-backend | tail -30

The backend prints a startup banner (from AcsAppConfig) followed by the application ready marker. Values in angle brackets vary per deployment:

==================================================
    HTTP ports: 8880
    HTTPS ports: 8843
    ACS WS URL: http://<ftacs-host>:8080/ACSServer-ACS/ACSWebService?wsdl
    Swagger UI: /iot-webservice/swagger-ui/index.html
==================================================
INFO ... Started FtIotWebApp in <seconds> seconds (process running for <seconds>)

7.2. Endpoint Checks

# Swagger UI (also the container health endpoint)
curl -I http://localhost:8881/iot-webservice/swagger-ui/index.html

# OpenAPI document
curl http://localhost:8881/iot-webservice/v3/api-docs

# Actuator health (exposed endpoints: health, prometheus)
curl http://localhost:8881/actuator/health

Expected: HTTP 200 responses.

7.3. Database Connectivity

Both the FTACS and the IOTW schema must be reachable from the container:

# MySQL
docker compose -f compose.ui-backend.yaml exec ui-backend sh -c 'nc -zv "$DB_HOST" 3306'

# Oracle
docker compose -f compose.ui-backend.yaml exec ui-backend sh -c 'nc -zv "$DB_HOST" 1521'

The single quotes matter: $DB_HOST must be expanded by the shell inside the container, which has the env files loaded, not by your host shell.

Check the remaining external dependencies the same way:

# Hazelcast
docker compose -f compose.ui-backend.yaml exec ui-backend nc -zv <hazelcast-host> 5701

# FTACS SOAP endpoint
curl http://<ftacs-host>:8080

# ClickHouse (optional): 9000 native TCP for queries, 8123 HTTP for Flyway migrations
docker compose -f compose.ui-backend.yaml exec ui-backend nc -zv <clickhouse-host> 9000
docker compose -f compose.ui-backend.yaml exec ui-backend nc -zv <clickhouse-host> 8123

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

8881 → 8880

HTTP

UI Backend REST API under /iot-webservice, consumed by the UI Portals and by FTACS webhook callbacks. Configurable via FT_UI_BACKEND_HTTP_PORT.

Public (portals and FTACS)

8843 (container only)

HTTPS

Backend HTTPS connector (HTTPS_PORTS), active only when a keystore is configured. compose.ui-backend.yaml does not publish it.

Internal (bridge only)

Verify the published port is listening:

netstat -tlnp | grep 8881

The UI Portals publish 8880 → 80 (HTTP) and 8843 → 443 (HTTPS). They are deployed from their own stack (compose.portals.yaml), not here.

8.2. Outbound Connections

Destination Port Protocol Purpose

MySQL or Oracle database

3306 / 1521

TCP (JDBC)

FTACS and IOTW schemas.

Hazelcast members

5701

TCP

Distributed cache and session sharing.

FTACS application server

8080

HTTP (SOAP)

Device provisioning, parameter reads, firmware management.

ClickHouse

9000

native TCP

QoE analytics queries.

ClickHouse

8123

HTTP

Flyway migration of the analytics schema at startup.

8.3. HTTP Endpoints

Method Path Purpose Auth

GET

/iot-webservice/swagger-ui/index.html

Swagger UI (also the container health check target).

None

GET

/iot-webservice/v3/api-docs

OpenAPI 3 document.

None

GET

/actuator/health

Actuator health status.

None

GET

/actuator/prometheus

Prometheus metrics.

None

9. Stack Management

9.1. Logs

# Container logs
docker compose -f compose.ui-backend.yaml logs -f ui-backend

# On-disk application log
tail -f /usr/local/ft-system/ui-backend/logs/application.log

To raise the log level, set it in ui-backend/.env and recreate the container (restart keeps the environment the container was created with):

FT_UI_BACKEND_LOG_LEVEL=debug
docker compose -f compose.ui-backend.yaml up -d ui-backend

9.2. Start, Stop, Restart

docker compose -f compose.ui-backend.yaml stop ui-backend
docker compose -f compose.ui-backend.yaml start ui-backend
docker compose -f compose.ui-backend.yaml restart ui-backend

To stop and remove the container:

# Stop (keeps the container, config and logs)
docker compose -f compose.ui-backend.yaml stop ui-backend

# Stop and remove only the UI Backend container
docker compose -f compose.ui-backend.yaml rm -f ui-backend
Always pass -f compose.ui-backend.yaml. /usr/local/ft-system also holds the Compose files of the other stacks (for example compose.portals.yaml), and docker compose without -f does not select this one.

9.3. Shell Access

docker compose -f compose.ui-backend.yaml exec ui-backend bash

# Inspect loaded environment
docker compose -f compose.ui-backend.yaml exec ui-backend env | grep -E 'DB_|HZ_|ACS_|CLICKHOUSE_'

9.4. Updating UI Backend

# 1. Back up the config directory
tar -czf ui-backend-config-$(date +%Y%m%d).tar.gz /usr/local/ft-system/ui-backend/config/

# 2. Pull the new tag (registry-connected hosts)
docker compose -f compose.ui-backend.yaml pull ui-backend

# 3. Recreate the container with the new image
docker compose -f compose.ui-backend.yaml up -d ui-backend

For offline servers, replace step 2 with the manual image transfer flow in Offline Servers, then run docker compose -f compose.ui-backend.yaml up -d ui-backend.

10. Production Checklist

  • Change all default passwords: DB_PASSWORD, ACS_PASSWORD, CLICKHOUSE_PASSWORD and FT_UI_BACKEND_KEYWORD (the same value on every service that uses it) in the shared .env; SSL_KEYSTORE_PASSWORD and — when Superset is used — SUPERSET_PASSWORD in ui-backend/.env.

  • Set FT_UI_HOST to an address reachable by end-user browsers and by FTACS — not localhost.

  • Verify FTACS_WEBHOOK_BASE_URL uses the backend’s externally published host and port (default 8881) when FTACS pushes webhooks.

  • Pin an explicit image tag (hub.friendly-tech.com/ui/backend:<version>) rather than latest.

  • Confirm external dependencies are reachable: database (FTACS + IOTW schemas), Hazelcast, FTACS, and ClickHouse if analytics are used.

  • Configure firewall rules — restrict inbound 8881 to the portals/reverse proxy and FTACS; restrict database access to backend hosts.

  • Enable auto-restart — restart: unless-stopped is set in compose.ui-backend.yaml; add --restart unless-stopped to standalone docker run.

  • Configure TLS — terminate HTTPS at the portals reverse proxy, or configure the backend keystore (see TLS Keystore (optional)).

  • Set up monitoring — scrape /actuator/prometheus.

  • Configure backups — back up ui-backend/config/; rely on the external DB’s own backup strategy.

  • Tune the JVM — set FT_UI_BACKEND_JAVA_RAM to match host memory.

11. Troubleshooting

11.1. Container Fails to Start

Cause

Configuration error, missing environment file, or an unreachable dependency.

Fix
docker compose -f compose.ui-backend.yaml logs ui-backend
docker inspect ui-backend
docker compose -f compose.ui-backend.yaml exec ui-backend env | grep -E 'DB_|HZ_|ACS_'

11.2. Database Connection Failure

Cause

Database not running, wrong host/port, or bad credentials. Both FTACS and IOTW schemas must be reachable.

Fix
telnet <DB_HOST> 3306    # MySQL
telnet <DB_HOST> 1521    # Oracle
docker compose -f compose.ui-backend.yaml exec ui-backend env | grep -E 'DB_HOST|MYSQL_PORT|ORACLE_PORT|DB_PROFILE'

Check that DB_HOST, DB_PORT, DB_PASSWORD, DB_PROFILE, and the service name in the shared .env match the database.

11.3. Hazelcast Connection Failure

Cause

The Hazelcast cluster is not running or not reachable.

Fix
telnet <HAZELCAST_HOST> 5701
docker compose -f compose.ui-backend.yaml exec ui-backend env | grep HZ_MEMBERS
cat /usr/local/ft-system/ui-backend/config/hazelcast-client.xml

Update HZ_MEMBERS with the correct addresses and confirm hazelcast-client.xml references $\{env.HZ_MEMBERS}.

11.4. Device Operations Fail (FTACS Unreachable)

Cause

The backend cannot reach the FTACS SOAP web service.

Fix
curl http://<ACS_WEB_SERVICE_HOST>:8080
docker compose -f compose.ui-backend.yaml exec ui-backend env | grep ACS_WEB_SERVICE

Confirm ACS_WEB_SERVICE_HOST and ACS_WEB_SERVICE_PORT are correct and FTACS is running.

11.5. Port Already in Use

Cause

Another process is bound to the host port.

Fix
sudo lsof -i :8881
# Either free the port, or change the mapping:
#   FT_UI_BACKEND_HTTP_PORT=9881 in the shared .env

11.6. Permission Denied on Mounted Volumes

Cause

Host directory permissions do not allow the container user to read/write.

Fix
sudo chown -R $(whoami):$(whoami) /usr/local/ft-system
chmod -R 755 /usr/local/ft-system

11.7. Out of Memory

Cause

JVM heap exceeds available memory, or the Docker memory limit is too low. The symptom is java.lang.OutOfMemoryError in the log or a container killed by the OOM killer.

Fix
  1. Increase the Docker memory limit (Docker Desktop: Settings → Resources → Memory: 4 GB+).

  2. Adjust the heap in the shared .env (Compose interpolates ${FT_UI_BACKEND_JAVA_RAM} from the root .env, not from ui-backend/.env):

    FT_UI_BACKEND_JAVA_RAM="-Xms1g -Xmx4g"
  3. Recreate the container so the new heap applies: docker compose -f compose.ui-backend.yaml up -d ui-backend (restart keeps the old JAVA_OPTS).

11.8. Container Not Reachable From Host

Cause

The host firewall blocks traffic on Docker bridge interfaces (br-*) in the host INPUT/OUTPUT path. This is not a Compose or application issue.

Signs:

  • The container is healthy and responds on localhost inside the container.

  • Published ports look correct (0.0.0.0:8881→8880/tcp).

  • Requests from the host hang / time out (no ECONNREFUSED).

    Fix
    # IPv4 (all Docker bridges)
    iptables -I INPUT  1 -i br+ -j ACCEPT
    iptables -I OUTPUT 1 -o br+ -j ACCEPT
    
    # IPv6 (separate stack)
    ip6tables -I INPUT  1 -i br+ -j ACCEPT
    ip6tables -I OUTPUT 1 -o br+ -j ACCEPT

IPv4 working does not imply IPv6 works — they are separate firewall stacks. Docker programs NAT and FORWARD rules but does not override host INPUT/OUTPUT policies.

11.9. Getting Support

Collect the following before contacting Friendly Technologies support:

  • The image tag in use (docker compose -f compose.ui-backend.yaml images ui-backend).

  • Container logs (docker compose -f compose.ui-backend.yaml logs ui-backend > ui-backend.log).

  • The loaded environment with secrets removed (docker compose -f compose.ui-backend.yaml exec ui-backend env | grep -E 'DB_|HZ_|ACS_|CLICKHOUSE_').

  • The on-disk application log from /usr/local/ft-system/ui-backend/logs/.