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 |
|
Docker Compose |
2.0+ |
Latest stable |
All commands in this guide use the |
RAM |
3 GB free |
verify with the deployment owner |
The backend JVM defaults to |
Disk space |
15 GB free |
verify with the deployment owner |
Image, configuration under |
|
Java is not required on the host — the JRE is bundled inside the container image ( |
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 |
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 |
8880 (HTTP) / 8843 (HTTPS) |
No (optional) |
|
Hazelcast 5.5 is the client library version compiled into the backend ( ClickHouse 26.2 is the supported version. The development reference compose in this repository
( |
2.3. Supported Operating Systems
| Deployment | Operating system |
|---|---|
Docker (Compose or |
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 ( |
Hazelcast members |
5701 |
TCP |
Cluster membership, distributed cache and sessions ( |
FTACS application server |
8080 |
HTTP or HTTPS (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 ( |
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
|
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 |
|
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 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 |
-
On the offline host, find out which architecture it runs — this is the value you will 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 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 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 ui-backend.tar.gz ui-backend-$TAG.tar. -
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 theui-backend/config/directory). -
On the offline host, load the archive and start the service. Set
TAGto 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-backendWindows (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 |
5. Preparation
5.1. Directory Structure
Create the base directory and the backend’s config / logs folders.
|
|
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.ymland.envof 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 |
|---|---|---|
|
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 |
|
Shared stack environment: database connection, ClickHouse, Hazelcast, FTACS credentials, inter-service hosts, and the values |
Yes |
|
Per-service environment from FT_DISK |
Yes |
|
Nine FT_DISK entries: |
Yes |
|
Backend application logs. |
No |
|
The backend container merges configuration on startup: files you place in |
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 |
|---|---|
|
Database connection ( |
|
Backend log level, HikariCP pool tuning, SSL keystore, LDAP/SAML integration |
|
|
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 |
|---|---|---|
|
|
Public hostname or IP of this server, reachable from end-user browsers and from FTACS. Use a real IP or DNS name, not |
|
|
Hazelcast cluster member address(es). Format |
|
|
Hostname or IP of the FTACS application server. |
|
|
Hostname or IP of the ClickHouse server. Leave the placeholder if ClickHouse is not deployed — analytics features are skipped. |
|
|
Hostname or IP of the database server. The vendor is selected by |
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 ( |
5.2.2. Environment Variable Reference
Shared .env — stack variables
| Variable | Description | Default | Required |
|---|---|---|---|
|
Base directory for persistent data and configuration on the host. Set it to the install root used by this guide, |
|
Yes |
|
Container timezone. |
|
Yes |
|
Host port published for the backend REST API (maps to internal |
|
No (optional) |
|
Backend JVM heap settings. |
|
No (optional) |
|
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 |
|
Yes |
|
Database vendor: |
|
Yes |
|
Scheme of the FTACS SOAP web service connection: |
|
No (optional) |
|
FTACS application host. |
|
Yes |
|
FTACS application port. |
|
Yes |
|
FTACS SOAP web-service credentials (FT Configs UI: |
|
Yes |
|
Backend base URL as reachable by FTACS for webhook event notifications. Set to the externally published host and port. |
No (optional) |
|
|
Hazelcast cluster members (comma-separated |
|
Yes |
|
ClickHouse server address. |
|
No (optional) |
|
ClickHouse native protocol port used for analytics queries. |
|
No (optional) |
|
ClickHouse HTTP port used by Flyway for analytics schema migrations. |
|
No (optional) |
|
ClickHouse database name (mapped to |
|
No (optional) |
|
ClickHouse credentials. Read directly only by the Flyway migration datasource; |
|
No (optional) |
|
The backend reads ClickHouse credentials under two different names. The Flyway migration
datasource reads Setting only |
ui-backend/.env — per-service variables
| Variable | Description | Default | Required |
|---|---|---|---|
|
Backend log level: |
|
No (optional) |
|
Superset instance the backend queries for embedded dashboards. |
No (optional) |
|
|
Superset credentials. Not present in the shipped env files — the backend falls back to these built-in defaults, so add both lines to |
|
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 |
|---|---|---|---|
|
MySQL server address. |
|
Yes |
|
MySQL port. |
|
Yes |
|
Database username (FTACS schema). |
|
Yes |
|
Database password (shared across schemas). |
|
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 |
|---|---|---|---|
|
Oracle server address. |
|
Yes |
|
Oracle listener port. |
|
Yes |
|
Oracle PDB service name. |
|
Yes |
|
Database username for the FTACS schema. |
|
Yes |
|
Oracle username for the IOTW schema. |
|
Yes |
|
Database password (shared across schemas). |
|
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 ( |
|
Master switch for OIDC login through Keycloak. |
|
|
Public portals URL Keycloak redirects back to after login. |
||
|
Support Center OIDC client ID registered in Keycloak. |
|
|
|
Support Center realm issuer URL. |
||
|
Management Console OIDC client ID registered in Keycloak. |
|
|
|
Management Console realm issuer URL. |
||
SAML SSO ( |
|
Master switch for SAML SSO login. |
|
|
Public portals URL the IdP redirects back to. |
||
|
Support Center service-provider entity ID. |
|
|
|
Support Center identity-provider entity ID. |
||
|
Support Center IdP single-sign-on URL. |
||
|
Management Console service-provider entity ID. |
|
|
|
Management Console identity-provider entity ID. |
||
|
Management Console IdP single-sign-on URL. |
||
|
Assertion attribute name mappings: |
|
|
SSL keystore (backend HTTPS) |
|
Keystore path when the backend serves HTTPS directly. |
|
|
Keystore password. |
|
|
|
Key alias inside the keystore. |
(empty) |
|
|
Private-key password, if different from the keystore password. |
(empty) |
|
|
Keystore format. |
|
|
Connection pools (HikariCP) |
|
FTACS-schema pool: max / min idle connections. |
|
|
IOTW-schema pool: max / min idle connections. |
|
|
|
ClickHouse pool: max / min idle connections. |
|
|
|
Idle connection timeout (ms). |
|
|
|
Connection acquisition timeout (ms). |
|
|
|
Maximum connection lifetime (ms). |
|
|
|
Connection validation timeout (ms). |
|
|
OIDC / SAML secrets are files, not variables. The OIDC client secrets live in Both the |
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 |
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 |
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 |
|---|---|---|---|
|
HTTP |
UI Backend REST API under |
Public (portals and FTACS) |
|
HTTPS |
Backend HTTPS connector ( |
Internal (bridge only) |
Verify the published port is listening:
netstat -tlnp | grep 8881
|
The UI Portals publish |
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 |
|
Swagger UI (also the container health check target). |
None |
GET |
|
OpenAPI 3 document. |
None |
GET |
|
Actuator health status. |
None |
GET |
|
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 |
10. Production Checklist
-
Change all default passwords:
DB_PASSWORD,ACS_PASSWORD,CLICKHOUSE_PASSWORDandFT_UI_BACKEND_KEYWORD(the same value on every service that uses it) in the shared.env;SSL_KEYSTORE_PASSWORDand — when Superset is used —SUPERSET_PASSWORDinui-backend/.env. -
Set
FT_UI_HOSTto an address reachable by end-user browsers and by FTACS — notlocalhost. -
Verify
FTACS_WEBHOOK_BASE_URLuses the backend’s externally published host and port (default8881) when FTACS pushes webhooks. -
Pin an explicit image tag (
hub.friendly-tech.com/ui/backend:<version>) rather thanlatest. -
Confirm external dependencies are reachable: database (FTACS + IOTW schemas), Hazelcast, FTACS, and ClickHouse if analytics are used.
-
Configure firewall rules — restrict inbound
8881to the portals/reverse proxy and FTACS; restrict database access to backend hosts. -
Enable auto-restart —
restart: unless-stoppedis set incompose.ui-backend.yaml; add--restart unless-stoppedto standalonedocker 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_RAMto 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.xmlUpdate
HZ_MEMBERSwith the correct addresses and confirmhazelcast-client.xmlreferences$\{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_SERVICEConfirm
ACS_WEB_SERVICE_HOSTandACS_WEB_SERVICE_PORTare 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.OutOfMemoryErrorin the log or a container killed by the OOM killer. - Fix
-
-
Increase the Docker memory limit (Docker Desktop: Settings → Resources → Memory: 4 GB+).
-
Adjust the heap in the shared
.env(Compose interpolates${FT_UI_BACKEND_JAVA_RAM}from the root.env, not fromui-backend/.env):FT_UI_BACKEND_JAVA_RAM="-Xms1g -Xmx4g" -
Recreate the container so the new heap applies:
docker compose -f compose.ui-backend.yaml up -d ui-backend(restartkeeps the oldJAVA_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
localhostinside 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/.