Installation & Deployment

← Back | Main Page

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 (default device//data,device//diagnostic) on any reachable broker. Left at the default MQTT_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 ft-device-network-service (container port 8080, published as 8383); if it cannot resolve or reach it, the UI Backend logs UnknownHostException: Failed to resolve 'ft-device-network-service' and cannot serve WiFi analytics. Deploying the UI Backend itself is out of scope here — see Separate server deployment — Server D.

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 compose.yml uses Compose v2 syntax

RAM available to the container

1 GB

2 GB

Follows the default JVM heap -Xms256m -Xmx1g (FT_DEVICE_NETWORK_JAVA_RAM); raise both together for larger device fleets

Disk for the logs volume

verify with platform owner

verify with platform owner

Sized by the retention in logback-spring.xml: 10 MB per main-log file with 30 days of history, plus one log file per device serial seen

CPU architecture

linux/amd64 or linux/arm64

 — 

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 network_* tables into it during startup, so an unreachable server fails the container. The database named by CLICKHOUSE_DATABASE (default ftacs_qoe_ui_data, shared with FT QoE Web) must already exist — the service creates only the tables inside it, see Database Migrations. 26.2 is the supported version set by the documentation owner; this repository pins no ClickHouse version at all — its compose.yml defines only the service container, and no ClickHouse test container or image tag exists anywhere in the build.

8123 (HTTP)

Yes

Hazelcast

5.3.6

Carries the networkDeviceParameterDataQueue this service consumes. The client connects synchronously at startup (async-start: false), so if no member is reachable the container fails to start. The minimum is the com.hazelcast:hazelcast client version in build.gradle; confirm the server version with the platform owner.

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 MQTT_URL

No (optional)

The service reaches ClickHouse over HTTP only. application.yml builds both the datasource and the Flyway JDBC URL from CLICKHOUSE_HTTP_PORT (default 8123) using com.clickhouse.jdbc.ClickHouseDriver; the native TCP port (9000) is never used and does not need to be reachable.

The Hazelcast cluster name is hardcoded to dev in the image (hazelcast-client.yml on the classpath) — only the member address list is overridable, via HZ_MEMBERS. If the platform’s Hazelcast cluster uses a different cluster name, the client fails to join with no data flowing, and no error naming the mismatch — confirm the cluster name matches before troubleshooting anything else.

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

The deployment files for this service are compose.yml, the shared stack .env, and the per-service ft-device-network/.env, all reproduced in Docker Compose and Environment Configuration. There is no separate pre-configured configuration package 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 clickhouse-jdbc HTTP driver

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 MQTT_URL

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:

# Replace with target host IP and port
nc -zv <target-ip> <port>

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 repository’s own compose.yml sets neither container_name nor a named network, so the compose file in Docker Compose adds both — an explicit container_name: ft-device-network-service and an ft-network bridge — to make that hostname stable. Any service that must resolve it has to join the same network.

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

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 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 --platform matching the offline host’s architecture is required; without it, docker pull selects the connected machine’s 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 build script produces linux/amd64 and linux/arm64, but the CI publish path defaults to linux/amd64 only, so a given tag may be amd64-only: if the tag is amd64-only and your offline host is arm64, the --platform linux/arm64 pull fails with a no matching manifest error; use an amd64 host or request a multi-arch build of that tag. 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:

    docker version --format '{{.Server.Arch}}'

    If Docker is not installed there yet, use the operating system instead — uname -m on Linux/macOS, 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

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

    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>. Piping docker save from 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 bundled tar.exe (Windows 10 1803+ / Server 2019+): tar.exe -czf ft-device-network-service.tar.gz ft-device-network-service-$TAG.tar.

  4. Transfer the archive to the offline host, together with compose.yml, the shared .env, and ft-device-network/.env.

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

    Linux (bash):

    TAG=latest
    
    gzip -dc "ft-device-network-service-$TAG.tar.gz" | docker load
    docker compose up -d ft-device-network-service

    Windows (PowerShell):

    $TAG = "latest"
    
    docker load -i "ft-device-network-service-$TAG.tar"
    docker compose 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 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 compose.yml itself.

5. Preparation

Create the directory this service needs on the host and point it at the infrastructure listed in Required External Dependencies. No configuration files need to be downloaded — the only volume this container mounts is its logs/ directory.

5.1. Directory Structure

mkdir -p /usr/local/ft-system/ft-device-network/logs
cd /usr/local/ft-system

This follows the platform layout documented in Full System Deployment: one directory per service directly under /usr/local/ft-system, whose compose mount for this service is ./ft-device-network/logs:/app/logs.

5.1.1. 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.yml                             # from FT_DISK
├── .env                                    # shared stack environment
└── ft-device-network/
    ├── .env                                # per-service environment
    └── logs/                               # -> /app/logs
        ├── ft-device-network-service.log
        ├── devices/                        # one file per device serial
        └── heapdump.hprof                  # only after an OutOfMemoryError
Path Content Backup

compose.yml

The stack compose file, from FT_DISK. Reproduced for this service in Docker Compose.

Yes

.env

Shared stack environment — ClickHouse, Hazelcast and the other infrastructure variables every service reads. See Environment Configuration.

Yes

ft-device-network/.env

Per-service environment — published port, JVM heap, log level, the optional MQTT ingestion path, and async task / data-processing tuning.

Yes

ft-device-network/logs/

Bind-mounted at /app/logs. Main application log, per-device logs under devices/, and any heap dump. Created by the mkdir -p above; the files themselves are written by the container. See Logs.

No

The compose.yml and the .env shipped in this repository use a different layout — the mount is written ${DATA_FOLDER:-.}/ft-data/ft-device-network/logs and the reference .env sets DATA_FOLDER=/usr/local/ft-services. Neither the ft-data level nor the ft-services root exists in the platform layout. This guide documents the platform layout; if you deploy the repository files unchanged, create ${DATA_FOLDER}/ft-data/ft-device-network/logs instead. Recorded in docs/decisions/LOG.md — verify the intended convention with FT DevOps.

5.2. Environment Configuration

Environment configuration is split into two layers, exactly as in Full System Deployment:

File Purpose

/usr/local/ft-system/.env

Shared stack environment — infrastructure this service does not own: ClickHouse, Hazelcast, and the container timezone. In a full-stack deployment the same file also carries the relational database connection (DB_HOST, DB_PORT, DB_USER, DB_PASSWORD), Flowable, PostgreSQL, JWT, inter-service URLs, and COMPOSE_PROFILES — none of which this service reads.

/usr/local/ft-system/ft-device-network/.env

Per-service environment — published host port, JVM settings, log level, the optional MQTT ingestion path, and async task / data-processing tuning.

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.

There is no environment file per database vendor. MySQL is the platform default, and switching to Oracle means changing DB_PROFILE, SPRING_PROFILES_ACTIVE, DB_HOST and the other variables listed in Switching to Oracle inside the single root .env — compose.yml maps the generic DB_* names onto the vendor-specific ones each application expects (MYSQL_HOST, ORACLE_HOST, …​) through the x-db-env / x-flowable-env YAML anchors. This service stores nothing in the relational database — its only datastore is ClickHouse — so the vendor choice does not affect it.

5.2.1. Shared .env variables

Read by this service from /usr/local/ft-system/.env:

Variable Description Default Required

HZ_MEMBERS

Comma- or space-separated Hazelcast member addresses; an entry without a port gets :5701 appended. Overrides the member list bundled in the image.

hazelcast:5701 (bundled)

Yes

CLICKHOUSE_HOST

ClickHouse hostname or IP

localhost

Yes

CLICKHOUSE_HTTP_PORT

ClickHouse HTTP port, used for both the datasource and Flyway

8123

No

CLICKHOUSE_DATABASE

ClickHouse database holding the network_* tables

ftacs_qoe_ui_data

No

CLICKHOUSE_USERNAME

ClickHouse user

ftacs

Yes

CLICKHOUSE_PASSWORD

ClickHouse password

ftacs

Yes

TZ

Container timezone, applied through -Duser.timezone

UTC (image default)

No

DATA_FOLDER

Host path whose ft-device-network/logs subdirectory is mounted at /app/logs (the repository default writes ft-data/ft-device-network/logs)

.

No

5.2.2. Per-service .env variables

Read by this service from /usr/local/ft-system/ft-device-network/.env:

Variable Description Default Required

FT_DEVICE_NETWORK_HTTP_PORT

Host port published for the container’s port 8080

8383

No

FT_DEVICE_NETWORK_JAVA_RAM

JVM heap options, passed into JAVA_OPTS

-Xms256m -Xmx1g

No

FT_DEVICE_NETWORK_LOG_LEVEL

Root and application log level (logback-spring.xml)

info

No

MQTT_URL

Broker URL for the optional MQTT ingestion path

tcp://localhost:1883

No

MQTT_USERNAME

MQTT user

username

No

MQTT_PASSWORD

MQTT password

password

No

MQTT_TOPICS

Subscribed topics, comma-separated

device//data,device//diagnostic

No

MQTT_QOS

MQTT quality of service

1

No

TASK_CORE_POOL_SIZE

Async executor core pool size

10

No

TASK_MAX_POOL_SIZE

Async executor maximum pool size

20

No

TASK_QUEUE_CAPACITY

Async executor queue capacity

500

No

DATA_PROCESSING_BATCH_SIZE

Device-data processing batch size

100

No

DATA_PROCESSING_TIMEOUT_SECONDS

Device-data processing timeout

30

No

Create the shared .env alongside compose.yml:

# /usr/local/ft-system/.env

# Common vars
# Host path holding ft-device-network/logs -- the platform layout root.
# This repository's own reference .env ships /usr/local/ft-services instead; see
# the note under Directory Structure.
DATA_FOLDER=/usr/local/ft-system
TZ=Europe/Kiev

# 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

Then create the per-service ft-device-network/.env:

# /usr/local/ft-system/ft-device-network/.env

# Service HTTP port (published on the host)
FT_DEVICE_NETWORK_HTTP_PORT=8383

# JVM heap -- default fits small deployments; raise for larger device fleets
FT_DEVICE_NETWORK_JAVA_RAM=-Xms256m -Xmx1g
FT_DEVICE_NETWORK_LOG_LEVEL=info

# MQTT device-data ingestion -- optional, see the Overview. Leave unset to
# leave this ingestion path disabled.
# MQTT_URL=tcp://<mqtt-broker-host>:1883
# MQTT_USERNAME=<mqtt-username>
# MQTT_PASSWORD=<mqtt-password>
# MQTT_TOPICS=device/+/data,device/+/diagnostic
# MQTT_QOS=1

# Async task processing -- defaults shown, tune under sustained high load
# 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 platform’s own per-service ft-device-network/.env (Environment Variables Reference — ft-device-network/.env) names the published port DEVICE_NETWORK_PORT and additionally carries HikariCP variables (DB_MAX_POOL_SIZE, DB_MIN_IDLE, DB_CONNECTION_TIMEOUT_MS). The compose.yml in this guide, like the one shipped in the service repository, reads FT_DEVICE_NETWORK_HTTP_PORT; the HikariCP variables have no effect here, since this service has no relational datasource. Recorded in docs/decisions/LOG.md — verify the intended variable name with FT DevOps.

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 (8123), never the native TCP port (9000) — the service uses the clickhouse-jdbc HTTP driver and never opens the native port.

The reference .env shipped in the service repository still contains CLICKHOUSE_PORT=9000. The application never reads that variable — application.yml resolves the ClickHouse port from CLICKHOUSE_HTTP_PORT only. Setting CLICKHOUSE_PORT has no effect; set CLICKHOUSE_HTTP_PORT instead.

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
docker compose 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

Click to expand compose.yml (FT Device Network Service)
name: ft-device-network

services:
  ft-device-network-service:
    image: hub.friendly-tech.com/ftacs/ft-device-network-service:latest
    container_name: ft-device-network-service
    # ClickHouse and Hazelcast are external to this stack, so there is no
    # depends_on -- both must be reachable before this container starts, and
    # FTACS must be healthy for device data to flow in. See "Startup
    # Dependencies" above.
    env_file:
      - .env
      - ./ft-device-network/.env
    environment:
      JAVA_OPTS: "${FT_DEVICE_NETWORK_JAVA_RAM:--Xms256m -Xmx1g} -Duser.timezone=${TZ:-UTC} -XX:+UseG1GC -XX:+UseStringDeduplication -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/logs/heapdump.hprof"
    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
    extra_hosts:
      - "host.docker.internal:host-gateway"   # peers running on this host outside Docker
    networks:
      - ft-network

networks:
  ft-network:
    driver: bridge

container_name and the ft-network bridge are set explicitly on top of what the repository’s own compose.yml defines — see Docker Networking for why they matter.

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 up -d ft-device-network-service
docker compose ps
docker compose 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 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 FtWifiManagementServiceApplication — an older name than the current service, left over from before it was renamed FT Device Network Service.

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 /api context path, including the Prometheus scrape endpoint. The published port is set by FT_DEVICE_NETWORK_HTTP_PORT.

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 — CLICKHOUSE_HOST, CLICKHOUSE_HTTP_PORT

Hazelcast

5701

TCP

Hazelcast client connection and the FTACS ingestion queue — HZ_MEMBERS

MQTT broker

1883

TCP

Optional device-telemetry ingestion — MQTT_URL

8.3. HTTP Endpoints

All paths include the /api context path.

Method Path Purpose Auth

GET, POST

/api/data/**

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

/api/ui/**

UI-oriented aggregates: full network data, current network score, current and historical WiFi and client data by device serial

None

POST

/api/monitoring/**

Model/serial registration and on-demand diagnostics (speed test, ping, WiFi diagnostics)

None

POST

/api/notification/**

Callbacks from FTACS — parameter changes, device registration/bootstrap, diagnostic and task completion

None

GET

/api/actuator/health

Health check, also used by the container healthcheck

None

GET

/api/actuator/metrics

Application metrics

None

GET

/api/actuator/prometheus

Prometheus scrape endpoint

None

GET

/api/swagger-ui.html

Swagger UI

None

GET

/api/v3/api-docs

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

ft-device-network-service.log

Main application log — daily rotation, 10 MB per file, 30-day retention

devices/{serial}.log

Per-device log, sifted by the deviceSerial MDC key — 10-day retention (see Logging — Device-specific Logging)

heapdump.hprof

Written on OutOfMemoryError

docker compose logs -f ft-device-network-service
docker compose 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 ps
docker compose stop ft-device-network-service
docker compose start ft-device-network-service
docker compose restart ft-device-network-service
docker compose down

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 pull ft-device-network-service
docker compose 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_MEMBERS must not be left at their placeholder/localhost values.

  • Set real ClickHouse credentials — do not run with the shared default ftacs/ftacs username and password in production.

  • Set CLICKHOUSE_HTTP_PORT, not CLICKHOUSE_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 different hazelcast-client.yml if 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-service resolves 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 (FT_DEVICE_NETWORK_JAVA_RAM) for the device fleet — the default -Xms256m -Xmx1g targets 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-stopped in compose.yml.

11. Troubleshooting

11.1. Container Fails to Start

Symptom: The ft-device-network-service container exits immediately after docker compose up.

Fix:

  1. Read the error message from the logs:

    docker compose logs ft-device-network-service
  2. 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.

  3. 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:

  1. Confirm the service targets the HTTP port 8123, not the native TCP port 9000 — check CLICKHOUSE_HTTP_PORT in the shared .env.

  2. 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'
  3. Confirm the database named by CLICKHOUSE_DATABASE (default ftacs_qoe_ui_data) exists — the service does not create the database itself, only the tables inside it (via Flyway).

  4. If a migration keeps failing, it may have been auto-marked [SKIPPED] rather than blocking startup — check flyway_schema_history for success = false rows 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:

  1. Verify HZ_MEMBERS in the shared .env points at addresses the container can reach; an entry without a port gets :5701 appended.

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

  3. 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:

  1. Confirm FTACS is running and healthy.

  2. Confirm FTACS’s HZ_MEMBERS resolves 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.

  3. 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:

  1. Find the process holding the port:

    sudo lsof -i :8383
  2. Either stop that process, or change FT_DEVICE_NETWORK_HTTP_PORT in ft-device-network/.env and run docker compose up -d again.

11.7. Out of Memory

Symptom: The container is killed by the OOM killer, or logs/heapdump.hprof appears.

Fix:

  1. Increase the heap through FT_DEVICE_NETWORK_JAVA_RAM in ft-device-network/.env, e.g. -Xms512m -Xmx2g.

  2. If the problem persists, analyze the heap dump (Eclipse MAT, VisualVM).

11.8. Permission Denied on the Logs Volume

Symptom: The container cannot write to /app/logs.

Fix:

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

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

← Back | Main Page