Installation & Deployment
- 1. Overview
- 2. Prerequisites
- 3. Network Requirements
- 4. Registry Authentication
- 5. Preparation
- 6. Deployment
- 7. Verification
- 8. Port Reference
- 9. Stack Management
- 10. Production Checklist
- 11. Troubleshooting
- 11.1. Container Fails to Start
- 11.2. ClickHouse Connection or Migration Failure
- 11.3. Hazelcast Connection Issues
- 11.4. FTACS Reachable, but No New Device Data Arrives
- 11.5. MQTT Connection Errors in the Log
- 11.6. Port Already in Use
- 11.7. Out of Memory
- 11.8. Permission Denied on the Logs Volume
- 11.9. Getting Support
- 12. Related Documentation
This guide covers deploying FT Device Network Service as a single Docker container. It is the only supported deployment method — the service ships as a container image (hub.friendly-tech.com/ftacs/ft-device-network-service) built from the repository’s own Dockerfile; no distribution package or manual JAR/JDK deployment is built or published.
1. Overview
FT Device Network Service is a single container (ft-device-network-service) exposing a REST API under the /api context path for WiFi/network analytics, plus two inbound data-ingestion channels.
The infrastructure this service depends on — ClickHouse, Hazelcast, and FTACS — is assumed to be already deployed and reachable; this guide only points the container at it. To deploy that infrastructure, see All in one server deployment or Separate server deployment.
UI Backend / clients -> FT Device Network Service -> ClickHouse + Hazelcast <- FTACS (Hazelcast queue) [+ optional MQTT broker]
-
UI Backend (and any other REST client) calls the
/api/data/and/api/ui/endpoints to read WiFi status, network scores, and connected-client data by device serial number. -
FTACS parses raw CPE parameters and forwards them into the Hazelcast queue
networkDeviceParameterDataQueue; this service consumes that queue, computes metrics, and writes them to ClickHouse. This is the primary ingestion path. -
An MQTT broker is an optional, secondary ingestion path — the service can also subscribe to the topics in
MQTT_TOPICS(defaultdevice//data,device//diagnostic) on any reachable broker. Left at the defaultMQTT_URL(tcp://localhost:1883, which resolves to the container itself) the client never connects and this path is unused; see the MQTT broker row in Required External Dependencies.
For the architecture and data-processing pipeline, see Architecture Documentation; for the ClickHouse schema, see Database Configuration.
|
FT Device Network Service is itself a mandatory prerequisite for the UI Backend. On startup the UI Backend resolves and calls this service at the hostname |
2. Prerequisites
2.1. Host Requirements
| Component | Minimum | Recommended | Notes |
|---|---|---|---|
Docker Engine |
20.10 |
Latest stable |
The only supported runtime — the service is delivered as a container image |
Docker Compose |
2.0 |
Latest stable |
The reference |
RAM available to the container |
1 GB |
2 GB |
Follows the default JVM heap |
Disk for the logs volume |
verify with platform owner |
verify with platform owner |
Sized by the retention in |
CPU architecture |
|
— |
The release build script publishes both; an individual tag may be amd64-only, see Offline Servers |
2.2. Required External Dependencies
These services must be installed, running, and reachable from this host before the FT Device Network Service starts. None of them are deployed by this guide.
| Component | Minimum Version | Why It Is Needed | Port | Required |
|---|---|---|---|---|
ClickHouse |
26.2 |
Stores every metric this service computes. Flyway migrates the nine |
8123 (HTTP) |
Yes |
Hazelcast |
5.3.6 |
Carries the |
5701 (TCP) |
Yes |
FTACS |
verify with FTACS owner |
Parses raw CPE parameters and pushes them into the Hazelcast queue. Unlike ClickHouse and Hazelcast it does not block startup: the container starts and stays healthy, but no new device data arrives until FTACS is reachable on the same cluster. Nothing in the log flags this — see FTACS Reachable, but No New Device Data Arrives. |
via Hazelcast, 5701 (TCP) |
Yes |
MQTT broker |
verify with platform owner |
Optional secondary ingestion path for device telemetry that does not come through the FTACS queue. The MQTT client starts asynchronously after the application context is up and retries via automatic reconnect, so an unreachable broker never blocks startup. See MQTT Integration. |
1883 (TCP), or as set in |
No (optional) |
|
The service reaches ClickHouse over HTTP only. |
|
The Hazelcast cluster name is hardcoded to |
2.3. Supported Operating Systems
| Deployment | Operating system |
|---|---|
Docker Compose |
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 — the
dedicated compose.ft-device-network.yaml, the shared .env, and the per-service ft-device-network/.env.
Beyond them there is nothing to download: the only volume the container mounts is its logs/ directory, and the Hazelcast client configuration is bundled inside the image rather than mounted from disk.
3. Network Requirements
Outbound connections this container opens:
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
ClickHouse |
8123 |
HTTP |
Flyway migrations and every metric read/write — the service uses the |
Hazelcast |
5701 |
TCP |
Hazelcast client connection (blocking at startup), and the transport of the FTACS ingestion queue |
MQTT broker |
1883 (or as configured) |
TCP |
Optional device-telemetry ingestion, target taken from |
Inbound traffic to this service — the UI Backend and other API clients, and Prometheus scraping /api/actuator/prometheus — arrives on the published HTTP port, see HTTP / HTTPS.
|
For a quick connectivity check from any host:
|
3.1. Docker Networking
Other services on the same Docker network reach this one by the container hostname ft-device-network-service; the UI Backend depends on exactly that resolution (see Overview). The compose file in Docker Compose — and the one shipped in the repository, which matches it — sets both an explicit container_name: ft-device-network-service and the shared external network ft-network, to make that hostname stable. Any service that must resolve it has to join the same network. It has to be external: true and created once per host with docker network create ft-network: declared as a plain driver: bridge it would become the project-scoped ft-device-network_ft-network, which callers in other Compose projects cannot reach.
If a peer runs on the same host but outside Docker, use host.docker.internal rather than localhost — inside a container localhost is the container itself:
HZ_MEMBERS=host.docker.internal:5701
CLICKHOUSE_HOST=host.docker.internal
host.docker.internal resolves to the host’s gateway address. On Docker Desktop (macOS, Windows) it works out of the box; on Linux it exists only if the container maps it, which the compose file in Docker Compose does:
extra_hosts:
- "host.docker.internal:host-gateway"
The alternative is the host’s real LAN IP address.
4. Registry Authentication
The image is pulled from hub.friendly-tech.com, the same registry used by every FT service. 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 target 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:docker version --format '{{.Server.Arch}}'If Docker is not installed there yet, use the operating system instead —
uname -mon Linux/macOS, 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.comThen confirm the tag you intend to transfer actually publishes that architecture:
docker buildx imagetools inspect hub.friendly-tech.com/ftacs/ft-device-network-service:<tag> -
Pull and export the image. Paste the whole block as-is; the only lines to change are
PLATFORMandTAG.Linux / macOS (bash):
PLATFORM=linux/amd64 TAG=latest docker pull --platform "$PLATFORM" "hub.friendly-tech.com/ftacs/ft-device-network-service:$TAG" docker save "hub.friendly-tech.com/ftacs/ft-device-network-service:$TAG" | gzip > "ft-device-network-service-$TAG.tar.gz"Windows (PowerShell):
$PLATFORM = "linux/amd64" $TAG = "latest" docker pull --platform $PLATFORM "hub.friendly-tech.com/ftacs/ft-device-network-service:$TAG" docker save -o "ft-device-network-service-$TAG.tar" "hub.friendly-tech.com/ftacs/ft-device-network-service:$TAG"On Windows, always write the archive with
docker save -o <file>. Pipingdocker savefrom PowerShell (docker save … > file.tar) corrupts the archive, because the pipeline re-encodes the stream as text instead of raw bytes. To compress for transfer, use the bundledtar.exe(Windows 10 1803+ / Server 2019+):tar.exe -czf ft-device-network-service.tar.gz ft-device-network-service-$TAG.tar. -
Transfer the archive to the offline host, together with
compose.ft-device-network.yaml, the shared.env, andft-device-network/.env. -
On the offline host, load the archive and start the service. Set
TAGto the same value used above:Linux (bash):
TAG=latest gzip -dc "ft-device-network-service-$TAG.tar.gz" | docker load cd /usr/local/ft-system docker compose -f compose.ft-device-network.yaml up -d ft-device-network-serviceWindows (PowerShell):
$TAG = "latest" docker load -i "ft-device-network-service-$TAG.tar" docker compose -f compose.ft-device-network.yaml up -d ft-device-network-service
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/ftacs/ft-device-network-service
Upgrades use the same flow: pull the new tag on the connected machine, transfer and load the archive, then docker compose -f compose.ft-device-network.yaml up -d ft-device-network-service.
|
This covers the FT Device Network Service image only.
ClickHouse, Hazelcast, FTACS, and the MQTT broker 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
Create the directory this service needs on the host, download the deployment files into it, and point it at the infrastructure listed in Required External Dependencies. The container mounts no configuration directory — logs/ is its only volume — so the deployment consists only of the shipped Compose and environment files listed in Download Deployment Files.
5.1. Directory Structure
mkdir -p /usr/local/ft-system/ft-device-network/logs
cd /usr/local/ft-system
|
The service image runs as the non-root identity Assign the logs directory to the container identity before the first start, and repeat this one-time migration after upgrading an existing root-running installation:
If |
This follows the platform layout documented in
Full System Deployment:
the shared platform root is /usr/local/ft-system, with this service’s
ft-device-network/ runtime directory below it. The Compose mount for this service is
${DATA_FOLDER:-.}/ft-device-network/logs:/app/logs, which resolves to
/usr/local/ft-system/ft-device-network/logs with the DATA_FOLDER value from the shared .env.
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.ft-device-network.yaml up -d).
-
FT_DISK — ONE_IOT_UI/docker/ft-device-network — the dedicated Docker Compose stack of FT Device Network Service:
-
compose.ft-device-network.yaml→/usr/local/ft-system/compose.ft-device-network.yaml -
.env(per-service layer) →/usr/local/ft-system/ft-device-network/.env
-
-
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
Sliced to this service, the platform layout on the host looks as follows. This service mounts no
configuration directory — its Hazelcast client configuration is bundled inside the image — so
logs/ is the only volume.
/usr/local/ft-system/
├── compose.ft-device-network.yaml # FT_DISK: ONE_IOT_UI/docker/ft-device-network/
├── .env # FT_DISK: ONE_IOT_UI/docker/ (shared layer)
└── ft-device-network/
├── .env # FT_DISK: ONE_IOT_UI/docker/ft-device-network/ (per-service layer)
└── logs/ # -> /app/logs
├── ft-device-network-service.log
├── devices/ # one file per device serial
└── heapdump.hprof # only after an OutOfMemoryError
| Path | Content | Backup |
|---|---|---|
|
The dedicated Compose file of this service, from FT_DISK — ONE_IOT_UI/docker/ft-device-network. Its content is shown in Docker Compose. |
Yes |
|
Shared stack environment — ClickHouse, Hazelcast and the other infrastructure variables every service reads. See Environment Configuration. |
Yes |
|
Per-service environment — the JVM options ( |
Yes |
|
Bind-mounted at |
No |
compose.ft-device-network.yaml mounts ${DATA_FOLDER:-.}/ft-device-network/logs. The shared
.env from FT_DISK already sets DATA_FOLDER=/usr/local/ft-system; keep that value to use this
layout.
|
5.2. Environment Configuration
Environment configuration is split into two layers, exactly as in Full System Deployment:
| File | Purpose |
|---|---|
|
Shared environment — infrastructure this service does not own: ClickHouse, Hazelcast, the container timezone, and the published host port ( |
|
Per-service environment — the JVM options ( |
Both files are listed in this service’s env_file (see Docker Compose); values in the per-service
file are applied last and win on a name collision.
Shipped templates: FT_DISK — ONE_IOT_UI/docker for the shared layer and FT_DISK — ONE_IOT_UI/docker/ft-device-network for the per-service layer (see Download Deployment Files). Replace the placeholders in the copies on the host.
|
This service has no relational-database connection — its only datastore is ClickHouse — so the
MySQL/Oracle choice does not affect it. It reads no |
5.2.1. Shared .env variables
Read by this service from /usr/local/ft-system/.env:
| Variable | Description | Default | Required |
|---|---|---|---|
|
Host port published for the container’s port 8080. Substituted into the |
|
No |
|
Comma- or space-separated Hazelcast member addresses; an entry without a port gets |
|
Yes |
|
ClickHouse hostname or IP |
|
Yes |
|
ClickHouse HTTP port, used for both the datasource and Flyway |
|
No |
|
ClickHouse database holding the |
|
No |
|
ClickHouse user |
|
Yes |
|
ClickHouse password |
|
Yes |
|
Container timezone, applied through |
|
No |
|
Host path whose |
|
No |
5.2.2. Per-service .env variables
Read by this service from /usr/local/ft-system/ft-device-network/.env. The published host port is
not here: FT_DEVICE_NETWORK_HTTP_PORT is substituted into the ports: line of
compose.ft-device-network.yaml, and Compose takes that substitution from the .env next to the
Compose file only — see the shared-layer table above. JAVA_OPTS, however, does belong here: the
Compose file sets no JAVA_OPTS under environment:, so the value in this file (delivered via
env_file:) is what the JVM uses.
| Variable | Description | Default | Required |
|---|---|---|---|
|
JVM options — heap, GC, and heap-dump-on-OOM. Authoritative here because |
|
No |
|
Root and application log level ( |
|
No |
|
Broker URL for the optional MQTT ingestion path |
|
No |
|
MQTT user |
|
No |
|
MQTT password |
|
No |
|
Subscribed topics, comma-separated |
|
No |
|
MQTT quality of service |
|
No |
|
Async executor core pool size |
|
No |
|
Async executor maximum pool size |
|
No |
|
Async executor queue capacity |
|
No |
|
Device-data processing batch size |
|
No |
|
Device-data processing timeout |
|
No |
In the shared /usr/local/ft-system/.env downloaded from FT_DISK, set the values this service
reads. The file also carries variables of the other ONE_IOT_UI services; leave those as they are.
# /usr/local/ft-system/.env
# Common vars
# Host path holding ft-device-network/logs -- the platform layout root.
DATA_FOLDER=/usr/local/ft-system
TZ=Europe/Kyiv
# Published host port (maps to the container's 8080). It has to be in THIS file:
# Compose substitutes ${...} into "ports:" from the .env next to the Compose file only,
# so a copy in ft-device-network/.env would reach the container but never change
# the published port.
FT_DEVICE_NETWORK_HTTP_PORT=8383
# Hazelcast (existing) -- cluster name is fixed to "dev" inside the image
HZ_MEMBERS=<hazelcast-host-ip>:5701 # <-- replace
# ClickHouse (existing) -- the service targets the HTTP port only
CLICKHOUSE_HOST=<clickhouse-host-ip> # <-- replace
CLICKHOUSE_HTTP_PORT=8123
CLICKHOUSE_DATABASE=ftacs_qoe_ui_data
CLICKHOUSE_USERNAME=ftacs
CLICKHOUSE_PASSWORD=<your-ch-password> # <-- replace
The per-service ft-device-network/.env is the FT_DISK template, used as shipped. Change
JAVA_OPTS to size the heap, and uncomment the MQTT or task-tuning lines only when needed:
# ==========================================================
# FT Device Network Service -- PER-SERVICE overrides (canonical template)
# ==========================================================
# Copy to "ft-device-network/.env" next to the compose file. Holds ONLY
# device-network tuning. Shared vars (DATA_FOLDER, TZ, HZ_MEMBERS, CLICKHOUSE_*,
# FT_DEVICE_NETWORK_HOST, FT_DEVICE_NETWORK_HTTP_PORT) live in the shared ".env"
# -- do not duplicate.
# ==========================================================
# JVM options -- authoritative here. The compose template does NOT set JAVA_OPTS
# in "environment:", so this per-service value wins. Raise -Xms/-Xmx together for
# larger fleets. No -Duser.timezone flag: the JVM follows the shared TZ env var.
JAVA_OPTS=-Xms256m -Xmx1g -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/logs/heapdump.hprof
FT_DEVICE_NETWORK_LOG_LEVEL=info
# ==========================================================
# MQTT (set when the device-network MQTT ingest is used)
# ==========================================================
# MQTT_URL=tcp://mqtt:1883
# MQTT_USERNAME=username
# MQTT_PASSWORD=password
# MQTT_CLIENT_ID=ft-device-network-service-
# ==========================================================
# Task executor tuning (optional -- defaults suit most deployments)
# ==========================================================
# TASK_CORE_POOL_SIZE=10
# TASK_MAX_POOL_SIZE=20
# TASK_QUEUE_CAPACITY=500
# DATA_PROCESSING_BATCH_SIZE=100
# DATA_PROCESSING_TIMEOUT_SECONDS=30
|
The port changes together with the host, not only the hostname. Across hosts you connect to the published port; inside the Docker bridge you connect to the container port. Always target ClickHouse’s HTTP port ( |
|
|
|
The same two-layer split applies when this service is deployed as part of a multi-host platform stack — see Separate server deployment — Server F for that variant. |
6. Deployment
6.1. Startup Dependencies
This service has no depends_on on its infrastructure — ClickHouse, Hazelcast, and FTACS live outside this stack, so Compose cannot gate on their health. ClickHouse and Hazelcast must both be reachable before the container starts, since Flyway migrates the schema and the Hazelcast client connects synchronously during startup; either one being unreachable fails the container. FTACS does not block startup, but no device data flows in until it is healthy — see Required External Dependencies.
# Wait for ClickHouse (HTTP port)
until curl -sf http://<clickhouse-host-ip>:8123/ping; do sleep 2; done
# Wait for Hazelcast
until nc -z <hazelcast-host-ip> 5701; do sleep 2; done
# Then start the service
cd /usr/local/ft-system
docker compose -f compose.ft-device-network.yaml up -d ft-device-network-service
The container itself restarts cleanly at any time once ClickHouse and Hazelcast are up — restart: unless-stopped reconnects it after a host reboot.
6.2. Docker Compose
compose.ft-device-network.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.ft-device-network.yaml.
It sets no JAVA_OPTS under environment: — the JVM options come from ft-device-network/.env
through env_file: — and applies no database block, because this service reads no DB_* /
MYSQL_* variables.
Click to expand compose.ft-device-network.yaml
version: "3.8"
name: ft-device-network
# FT Device Network Service as its OWN Compose project (the "ft-special" stack).
#
# REQUIRED external prerequisites -- none of them are deployed here:
# * ClickHouse -- the ONLY datasource this service has. JPA and Flyway both
# connect over the HTTP port (CLICKHOUSE_HTTP_PORT, default 8123), NOT the
# native 9000 one. Flyway migrates the analytics schema at startup, so
# ClickHouse must answer or the service does not come up.
# * Hazelcast -- the service consumes the networkDeviceParameterDataQueue and
# networkDeviceStatusDataQueue. Startup aborts with "Hazelcast is
# unavailable" when the cluster cannot be reached. Set HZ_MEMBERS: the
# bundled hazelcast-client.yml otherwise points at "hazelcast:5701".
# * FTACS -- the source of everything above: it fills the Hazelcast queues and
# publishes the MQTT device topics. There is no ACS URL to configure here;
# without the ACS the service starts but has nothing to process.
#
# This service has NO main-database (MySQL/Oracle) connection: the image reads
# no MYSQL_* / DB_* variables, so no compose file applies a database block
# (*db-env) to it.
#
# The UI Backend is the CONSUMER, not a prerequisite: it calls this service at
# FT_DEVICE_NETWORK_URL (default http://ft-device-network-service:8080/api) and
# its device-network features fail while this stack is down. Deploy this stack
# before, or together with, the UI Backend.
#
# Joins the external "ft-network" so the UI Backend and the AI Agent reach it by
# container name. Create it once per host:
# docker network create ft-network
services:
ft-device-network-service:
image: hub.friendly-tech.com/ftacs/ft-device-network-service:latest # <-- pin a released tag
container_name: ft-device-network-service
env_file:
- .env # shared stack environment (ClickHouse, Hazelcast, TZ, ...)
- ./ft-device-network/.env # per-service overrides (JVM, log level, MQTT, tuning)
# JAVA_OPTS is not overridden here -- it comes from ft-device-network/.env
# (env_file) so the per-service value wins.
ports:
- "${FT_DEVICE_NETWORK_HTTP_PORT:-8383}:8080"
volumes:
- ${DATA_FOLDER:-.}/ft-device-network/logs:/app/logs
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:8080/api/actuator/health" ]
interval: 30s
timeout: 5s
start_period: 40s
retries: 3
restart: unless-stopped
stop_grace_period: 45s
extra_hosts:
- "host.docker.internal:host-gateway" # peers running on this host outside Docker
networks:
- ft-network
networks:
ft-network:
external: true
container_name and the external ft-network are what make the hostname ft-device-network-service resolvable from other Compose projects — see Docker Networking for why they matter. The repository’s own compose.yml is identical to this file.
In the full-system compose.yml of the FT System MySQL/Oracle Stack the same service differs only in
topology: it adds profiles: [portals] and depends_on on ClickHouse, Hazelcast and FTACS, mounts
./ft-device-network/logs, joins the stack’s own ft-network, and has no extra_hosts.
6.2.1. Start the Stack
|
Start the service only after ClickHouse and Hazelcast accept connections — see Startup Dependencies. |
cd /usr/local/ft-system
docker login hub.friendly-tech.com
docker compose -f compose.ft-device-network.yaml up -d ft-device-network-service
docker compose -f compose.ft-device-network.yaml ps
docker compose -f compose.ft-device-network.yaml logs -f ft-device-network-service | grep -i started
curl -s http://localhost:8383/api/actuator/health
7. Verification
7.1. Startup Log
Wait 30-40 seconds after start (the healthcheck start_period), then check the logs:
docker compose -f compose.ft-device-network.yaml logs ft-device-network-service | tail -50
A successful startup logs Flyway applying the ClickHouse migrations from classpath:db/changelog (first start only), followed by the standard Spring Boot ready marker:
INFO ... o.f.core.internal.command.DbMigrate ... Successfully applied N migrations
INFO ... c.f.n.FtWifiManagementServiceApplication ... Started FtWifiManagementServiceApplication in <seconds> seconds
|
The main application class is |
7.2. Endpoint Checks
# Health
curl -s http://localhost:8383/api/actuator/health
# Swagger UI
curl -I http://localhost:8383/api/swagger-ui.html
# OpenAPI spec
curl -s http://localhost:8383/api/v3/api-docs | head -c 200
The health endpoint must return {"status":"UP"}; Swagger UI must return HTTP 200 (redirects to /api/swagger-ui/index.html).
7.3. Database Connectivity
Flyway creates the schema automatically on first start — confirm the tables exist:
USE ftacs_qoe_ui_data;
SHOW TABLES;
Expect the nine tables network_client, network_wifi_band, network_external_interface, network_issue, network_score, network_device_info, network_device_parameters, network_device_status, and network_interface_stats (see Database Configuration — Overview for what each stores), plus Flyway’s own flyway_schema_history.
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 |
|---|---|---|---|
8383 → 8080 |
HTTP |
REST API, Swagger UI and actuator endpoints under the |
Public (published on the host) |
The service terminates plain HTTP only; the image carries no TLS keystore configuration.
8.2. Outbound Connections
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
ClickHouse |
8123 |
HTTP |
Flyway migrations and all metric reads/writes — |
Hazelcast |
5701 |
TCP |
Hazelcast client connection and the FTACS ingestion queue — |
MQTT broker |
1883 |
TCP |
Optional device-telemetry ingestion — |
8.3. HTTP Endpoints
All paths include the /api context path.
| Method | Path | Purpose | Auth |
|---|---|---|---|
GET, POST |
|
Network score, connected clients, network issues, WiFi band and interface data by device serial — current, points, historical and boundary reads. See API Documentation |
None |
GET, POST |
|
UI-oriented aggregates: full network data, current network score, current and historical WiFi and client data by device serial |
None |
POST |
|
Model/serial registration and on-demand diagnostics (speed test, ping, WiFi diagnostics) |
None |
POST |
|
Callbacks from FTACS — parameter changes, device registration/bootstrap, diagnostic and task completion |
None |
GET |
|
Health check, also used by the container healthcheck |
None |
GET |
|
Application metrics |
None |
GET |
|
Prometheus scrape endpoint |
None |
GET |
|
Swagger UI |
None |
GET |
|
OpenAPI specification |
None |
| The service ships no Spring Security configuration — every endpoint above is unauthenticated. Restrict access at the network level, see Production Checklist. |
9. Stack Management
9.1. Logs
The application writes to ft-device-network/logs/ under DATA_FOLDER:
| Log file | Purpose |
|---|---|
|
Main application log — daily rotation, 10 MB per file, 30-day retention |
|
Per-device log, sifted by the |
|
Written on |
docker compose -f compose.ft-device-network.yaml logs -f ft-device-network-service
docker compose -f compose.ft-device-network.yaml logs --tail 100 ft-device-network-service
tail -f /usr/local/ft-system/ft-device-network/logs/ft-device-network-service.log
9.2. Start, Stop, Restart
cd /usr/local/ft-system
docker compose -f compose.ft-device-network.yaml ps
docker compose -f compose.ft-device-network.yaml stop ft-device-network-service
docker compose -f compose.ft-device-network.yaml start ft-device-network-service
docker compose -f compose.ft-device-network.yaml restart ft-device-network-service
docker compose -f compose.ft-device-network.yaml rm -f ft-device-network-service
Always pass -f compose.ft-device-network.yaml. Without it, Compose in /usr/local/ft-system
uses the compose.yml of a full-system stack in the same directory, if there is one — a different
project, whose services docker compose down would stop. Use the named commands above.
|
9.3. Shell Access
docker exec -it ft-device-network-service bash
docker exec ft-device-network-service ls -la /app/logs
9.4. Updating FT Device Network Service
cd /usr/local/ft-system
docker compose -f compose.ft-device-network.yaml pull ft-device-network-service
docker compose -f compose.ft-device-network.yaml up -d ft-device-network-service
Flyway migrates the ClickHouse schema automatically on the first start of a new version; a failed migration is logged and, per the service’s custom retry strategy, marked [SKIPPED] rather than blocking startup — see Database Configuration — Custom Migration Strategy. Verify the expected schema change actually applied after any upgrade that touches the database.
10. Production Checklist
-
Point at production infrastructure —
CLICKHOUSE_HOST,HZ_MEMBERSmust not be left at their placeholder/localhost values. -
Set real ClickHouse credentials — do not run with the shared default
ftacs/ftacsusername and password in production. -
Set
CLICKHOUSE_HTTP_PORT, notCLICKHOUSE_PORT— the application reads only the former. -
Set real MQTT credentials if MQTT ingestion is used — the defaults (
username/password) are placeholders, not disabled values. -
Confirm the Hazelcast cluster name is
dev(or rebuild the image with a differenthazelcast-client.ymlif the platform’s cluster uses another name — it cannot be overridden at runtime). -
Confirm FTACS is healthy and on the same Hazelcast cluster before expecting device data to appear.
-
Confirm the hostname
ft-device-network-serviceresolves from the UI Backend host. -
Firewall the published port (
8383) to trusted callers (UI Backend, monitoring) only — the API has no authentication of its own. -
Size the JVM heap (
-Xms/-XmxinJAVA_OPTS, in the per-serviceft-device-network/.env) for the device fleet — the default-Xms256m -Xmx1gtargets small deployments. -
Verify log rotation and disk sizing for
/app/logs— 30-day main-log retention plus a per-device log file for every serial seen in the last 10 days. -
Enable auto-restart —
restart: unless-stoppedincompose.ft-device-network.yaml.
11. Troubleshooting
11.1. Container Fails to Start
Symptom: The ft-device-network-service container exits immediately after docker compose up.
Fix:
-
Read the error message from the logs:
docker compose -f compose.ft-device-network.yaml logs ft-device-network-service -
Confirm ClickHouse and Hazelcast are both reachable before the container starts — both are blocking dependencies (see Startup Dependencies); a Flyway migration failure or a Hazelcast connection timeout during startup both stop the application.
-
Confirm the environment file is loaded:
docker exec ft-device-network-service env | grep -E 'CLICKHOUSE_|HZ_MEMBERS'
11.2. ClickHouse Connection or Migration Failure
Symptom: Startup fails with a clickhouse-jdbc connection error, or Flyway reports a failed migration.
Fix:
-
Confirm the service targets the HTTP port
8123, not the native TCP port9000— checkCLICKHOUSE_HTTP_PORTin the shared.env. -
Verify ClickHouse answers, from inside the container so the check uses the same network path the service does:
docker exec ft-device-network-service sh -c 'curl -s http://$CLICKHOUSE_HOST:8123/ping' -
Confirm the database named by
CLICKHOUSE_DATABASE(defaultftacs_qoe_ui_data) exists — the service does not create the database itself, only the tables inside it (via Flyway). -
If a migration keeps failing, it may have been auto-marked
[SKIPPED]rather than blocking startup — checkflyway_schema_historyforsuccess = falserows and review the schema manually; see Database Configuration — Custom Migration Strategy.
11.3. Hazelcast Connection Issues
Symptom: The log shows Unable to connect to any address in the config, or the container exits during startup with a Hazelcast client error.
Fix:
-
Verify
HZ_MEMBERSin the shared.envpoints at addresses the container can reach; an entry without a port gets:5701appended. -
Confirm the target cluster’s name is
dev— the client configuration bundled in the image hardcodes it and cannot be overridden without rebuilding the image. -
Ensure port 5701 is open from the container’s host to every cluster member.
11.4. FTACS Reachable, but No New Device Data Arrives
Symptom: The service is healthy and the REST API responds, but /api/data/** returns stale or no data for devices that should be reporting. Nothing in the log flags this.
Cause: device data arrives through the Hazelcast queue networkDeviceParameterDataQueue, populated by FTACS. If FTACS is down, on a different Hazelcast cluster, or not yet processing parameters for the device, nothing arrives — and unlike ClickHouse/Hazelcast, this does not fail the container.
Fix:
-
Confirm FTACS is running and healthy.
-
Confirm FTACS’s
HZ_MEMBERSresolves to the same Hazelcast cluster as this service’s — a mismatch produces two separate clusters that never see each other, with no connection error on either side. -
Confirm the device has actually reported parameters to FTACS — check FTACS’s own session logs for the serial in question.
11.5. MQTT Connection Errors in the Log
Symptom: Repeated MQTT connection failures or reconnect attempts logged at startup and periodically afterward.
Cause: MQTT_URL defaults to tcp://localhost:1883, which resolves to the container itself. Unless MQTT-based ingestion is explicitly configured (see the MQTT broker row in Required External Dependencies), this is expected and does not affect the container’s health or the Hazelcast-based ingestion path.
Fix: if MQTT ingestion is intentionally unused, ignore these log lines. If it is intended to be used, verify MQTT_URL points at a reachable broker and MQTT_USERNAME/MQTT_PASSWORD are correct.
11.6. Port Already in Use
Symptom: docker compose up fails with bind: address already in use.
Fix:
-
Find the process holding the port:
sudo lsof -i :8383 -
Either stop that process, or change
FT_DEVICE_NETWORK_HTTP_PORTin the shared/usr/local/ft-system/.env(not inft-device-network/.env— Compose substitutesports:only from the file next tocompose.ft-device-network.yaml) and rundocker compose -f compose.ft-device-network.yaml up -d ft-device-network-serviceagain.
11.7. Out of Memory
Symptom: The container is killed by the OOM killer, or logs/heapdump.hprof appears.
Fix:
-
Increase the heap through the
-Xms/-XmxinJAVA_OPTSin the per-serviceft-device-network/.env(that value is authoritative —compose.ft-device-network.yamlsets noJAVA_OPTSunderenvironment:, so theenv_filevalue reaches the JVM), e.g.-Xms512m -Xmx2g. -
If the problem persists, analyze the heap dump (Eclipse MAT, VisualVM).
11.8. Permission Denied on the Logs Volume
Symptom: The container logs Permission denied while writing /app/logs, or no application log
appears in the host directory.
Cause: The image runs as 1001:0, but the bind-mounted host directory is owned by another
identity, commonly root:root.
Fix:
sudo chown -R 1001:0 /usr/local/ft-system/ft-device-network/logs
sudo chmod -R u+rwX,g+rwX,o-rwx /usr/local/ft-system/ft-device-network/logs
docker compose -f compose.ft-device-network.yaml up -d ft-device-network-service
Use the actual DATA_FOLDER prefix when it differs from /usr/local/ft-system.
11.9. Getting Support
Collect the following before raising a support request, so the first reply is not a request for more data:
docker compose -f compose.ft-device-network.yaml logs --tail 500 ft-device-network-service > support-logs.txt
docker exec ft-device-network-service env | grep -E 'CLICKHOUSE_|HZ_MEMBERS|MQTT_' >> support-logs.txt
docker inspect ft-device-network-service --format '{{.Config.Image}} {{.State.Health.Status}}' >> support-logs.txt
Include the image tag in use, the serial number of an affected device, and whether ClickHouse and Hazelcast were reachable at the time. Route the request to the FT Device Network Service owner listed for your deployment.
12. Related Documentation
-
Architecture Overview — the component diagram for this service and its dependencies
-
Architecture — Component Dependencies — what this service talks to, and why
-
Database Configuration — Overview — the ClickHouse schema Flyway creates
-
MQTT Integration — the optional ingestion path in full
-
All in one server deployment — deploying the infrastructure this guide assumes
-
OneIoT UI Backend — the consumer that requires this service