Installation & Deployment
This guide covers deploying the Northbound API (NBI) — a Spring Boot 4 / Java 25 REST and SOAP integration layer between carrier back-office systems and FTACS — as a single service.
The infrastructure NBI depends on — the ACS relational database, a Hazelcast cluster, and the FTACS SOAP endpoint — is assumed to be already deployed and reachable; this guide only points NBI at it.
To deploy that infrastructure, see All in one server deployment or Separate server deployment — Server C.
To deploy NBI alongside Service API and Provision Portal as one stack, see Java API Stack deployment.
For the full environment variable reference, see Configuration Guide — Environment Variables.
1. Overview
NBI is a single container (northbound-api) exposing REST, SOAP, and Swagger UI under the /iot-webservice context path.
It is a plain Compose service on the ft-network bridge, deployed from /usr/local/ft-system/.
Everything else in the diagram below is a prerequisite, not part of this installation:
Carrier back-office / OSS / CRM -> Northbound API -> MySQL / Oracle (ftacs + iotw schemas) + Hazelcast + FTACS (SOAP)
|
NBI uses the Compose project name |
2. Prerequisites
2.1. Host Requirements
| Component | Minimum | Recommended | Notes |
|---|---|---|---|
Docker Engine |
20.10 |
Latest stable |
Required for the only supported deployment method |
Docker Compose |
2.0 |
Latest stable |
The |
CPU |
2 cores |
2 cores |
Single Spring Boot container |
RAM |
2 GB |
4 GB |
JVM heap is sized by |
Disk |
1 GB |
5 GB |
Image plus |
Network |
100 Mbps |
1 Gbps |
Interface used for database, Hazelcast and FTACS traffic |
JDK |
25 |
25 |
Only when building from source with Gradle; the Docker deployment needs no JDK on the host ( |
2.2. Required External Dependencies
These services must be installed, running, and reachable from this host before the Northbound API starts. None of them are deployed by this guide.
| Component | Minimum Version | Why It Is Needed | Port | Required |
|---|---|---|---|---|
MySQL or Oracle |
verify with FT DevOps — this repo pins no server version (JDBC drivers only: |
Holds the |
3306 (TCP) for MySQL, 1521 (TCP) for Oracle |
Yes |
FTACS |
verify with the FTACS team |
NBI calls FTACS over SOAP to execute every device operation. Without it device RPCs fail with a connection error. |
8080 (HTTP) by default ( |
Yes |
Hazelcast |
5.6 (client library |
Distributed cache used by the shared |
5701 (TCP) by default, or the port advertised in |
No (optional) |
Both schemas live on the same server: the iotw datasource reuses the host, port, user and password of the ftacs datasource, only the schema differs (MYSQL_SCHEMA_IOTW, default iotw).
|
Deploying that infrastructure is out of scope here — see All in one server deployment for a single-host stack, or Separate server deployment — Server C for FTACS, and Server A for the database, on a split topology. |
2.3. Supported Operating Systems
| Deployment | Operating system |
|---|---|
Docker |
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 — northbound-api folder for the deployment files (.env, northbound-api/.env) and the pre-configured northbound-api configuration package (api.properties, hazelcast-client.yaml).
3. Network Requirements
Outbound connections NBI opens to its infrastructure, and inbound connections it accepts. Each row is a connection that must be open through firewalls when the peer is on another host; traffic to peers on the same Docker bridge needs no rule.
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
Database ( |
3306 (MySQL), 1521 (Oracle) |
TCP |
JDBC connections opened by NBI |
Hazelcast |
5701 |
TCP |
Hazelcast client connection (optional — only when caching is used) |
FTACS |
8080, or the ACS host’s HTTPS port |
TCP |
SOAP calls to FTACS for device operations |
Northbound API (inbound from carrier back-office / OSS / CRM, Service API, Provision Portal) |
9880 → 8080, and 9444 → 8443 when HTTPS is enabled |
TCP |
REST, SOAP, Swagger UI, Actuator on the published host ports |
Northbound API (inbound from monitoring) |
9880 → 8080 |
TCP |
Prometheus scrapes |
|
For a quick connectivity check from any host:
|
3.1. Docker Networking
NBI runs on the ft-network bridge defined in compose.yml.
Peers on the same bridge are reached by container name and by their container port — no firewall rule and no published port are involved.
The service declares extra_hosts: - "host.docker.internal:host-gateway", so host.docker.internal resolves to the Docker host from inside the container.
Use it in DB_HOST or ACS_URL, and in the member addresses in hazelcast-client.yaml, when the peer runs directly on the host rather than in a container.
4. Registry Authentication
All NBI images are pulled from hub.friendly-tech.com.
Authenticate once per host before the first docker compose up.
docker login hub.friendly-tech.com
Enter the read-only pull credentials when prompted:
| Field | Value |
|---|---|
Username |
|
Password |
|
The 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 NBI 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.
NBI ships as a single registry image — there is no local build step to transfer. 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=latest docker pull --platform "$PLATFORM" "hub.friendly-tech.com/api/northbound-api:$TAG" docker save "hub.friendly-tech.com/api/northbound-api:$TAG" | gzip > "northbound-api-$TAG.tar.gz"Windows (PowerShell):
$PLATFORM = "linux/amd64" $TAG = "latest" docker pull --platform $PLATFORM "hub.friendly-tech.com/api/northbound-api:$TAG" docker save -o "northbound-api-$TAG.tar" "hub.friendly-tech.com/api/northbound-api:$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 northbound-api.tar.gz northbound-api-$TAG.tar. -
Transfer the archive to the offline host, together with
compose.yml,.env,northbound-api/.env, and the files innorthbound-api/config/. -
On the offline host, load the archive and start the service. Set
TAGto the same value you used above:Linux (bash):
TAG=latest gzip -dc "northbound-api-$TAG.tar.gz" | docker load docker compose up -d northbound-apiWindows (PowerShell):
$TAG = "latest" docker load -i "northbound-api-$TAG.tar" docker compose up -d northbound-api
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/api/northbound-api
Upgrades use the same flow: pull the new tag on the connected machine, transfer and load the archive, then docker compose up -d northbound-api.
|
This covers the NBI image only. The database, Hazelcast, and FTACS are separate images on their own hosts — see All in one server deployment — Offline Servers for the procedure covering a whole stack. |
5. Preparation
Create the directories NBI needs on the host and point it at the infrastructure listed in Prerequisites.
5.1. Directory Structure
mkdir -p /usr/local/ft-system/northbound-api/{config,logs}
cd /usr/local/ft-system
5.1.1. Directory Layout
Once the files below are in place, the NBI slice of /usr/local/ft-system/ looks like this:
/usr/local/ft-system/
├── compose.yml # from FT_DISK
├── .env # shared stack environment
└── northbound-api/
├── .env # per-service environment
├── config/ # -> /etc/app (mounted read-only)
│ ├── api.properties # from FT_DISK -> northbound-api
│ ├── hazelcast-client.yaml # from FT_DISK -> northbound-api
│ └── keystore.p12 # only when HTTPS is enabled
└── logs/ # -> /app/logs
| Path | Content | Backup |
|---|---|---|
|
Stack definition, including the |
Yes |
|
Shared stack environment — database connection, FTACS, Hazelcast, time zone |
Yes |
|
Per-service environment — container ports, HikariCP pool, config paths, JWT, logging |
Yes |
|
|
Yes |
|
Application logs written by the container at |
No |
|
This guide follows the platform layout defined in
Full System Deployment:
the directory is |
Download the following from FT_DISK or the source repository (docker/ for .env, src/main/resources/ for api.properties, src/main/resources/config/ for hazelcast-client.yaml):
| Component | FT_DISK | Target directory | Files |
|---|---|---|---|
NBI environment |
|
Only one file: the |
|
NBI configuration |
|
|
The northbound-api/config/ directory is mounted read-only at /etc/app inside the container (./northbound-api/config:/etc/app:ro), and northbound-api/logs/ is mounted at /app/logs (./northbound-api/logs:/app/logs):
| File | Purpose |
|---|---|
|
API behavioral settings — logging, timeouts, device handling, authentication, file upload |
|
Hazelcast cache cluster client configuration |
|
SSL keystore for the HTTPS port (only when HTTPS is enabled — see TLS Keystore (optional)) |
See Configuration Guide for the reference on each configuration item.
5.2. Environment Configuration
The stack uses a two-layer environment file architecture — there is no environment file per database vendor.
| File | Purpose |
|---|---|
|
Database connection ( |
|
Per-service settings: container ports ( |
Both files are loaded by the env_file: list of the northbound-api service, in that order.
MySQL is the default; switching to Oracle changes variables inside the root .env, it does not swap a file — see [oracle-profile].
The MYSQL_* and ORACLE_* variables in the table below are not written by hand: compose.yml derives them from the generic DB_* values through the x-db-env anchor (MYSQL_HOST: ${DB_HOST} and so on), so one database block in the root .env serves every service.
Every variable below is read by NBI itself.
Default is the value that applies when the variable is unset — taken from application.yml / application-mysql.yml / application-oracle.yml where the placeholder carries one, otherwise from the environment templates shipped on FT_DISK.
| Variable | Description | Default | Required |
|---|---|---|---|
|
Active Spring profile; selects the datasource set and dialect ( |
|
Yes |
|
Container time zone; must match the zone in which the ACS stores its |
|
No |
|
HTTP connector port inside the container ( |
|
No |
|
HTTPS connector port inside the container; used only when |
|
No |
|
Enables the HTTPS connector |
|
No |
|
Path to the PKCS12 keystore serving the HTTPS port |
empty; |
Yes when HTTPS is enabled |
|
Keystore password |
empty |
Yes when HTTPS is enabled |
|
Keystore format |
|
No |
|
Private key password, when it differs from the keystore password |
empty |
No |
|
Alias of the key entry used for TLS |
|
No |
|
Trust store used for outbound TLS verification |
empty |
No |
|
Trust store password |
empty |
No |
|
MySQL host for both the |
|
Yes on the |
|
MySQL port |
|
No |
|
Schema of the ACS datasource |
|
No |
|
Schema of the second (OneIoT) datasource that powers the Group Update API |
|
No |
|
User for both MySQL datasources |
|
Yes on the |
|
Password for both MySQL datasources |
|
Yes on the |
|
JDBC driver class |
|
No |
|
Oracle listener host; falls back to |
|
Yes on the |
|
Oracle listener port |
|
No |
|
Oracle service name (thin driver) |
|
No |
|
Account owning the ACS schema |
|
Yes on the |
|
Password for |
|
Yes on the |
|
Account owning the |
|
No |
|
Password for |
falls back to |
No |
|
JDBC driver class |
|
No |
|
HikariCP maximum pool size, applied to both datasources |
|
No |
|
HikariCP minimum idle connections |
|
No |
|
HikariCP connection timeout in milliseconds |
|
No |
|
Base URL of the FTACS SOAP endpoint, including the scheme |
|
Yes |
|
Port of the FTACS SOAP endpoint |
empty; |
Yes |
|
FTACS web service account |
|
Yes |
|
Password of the FTACS web service account |
|
Yes |
|
Spring resource path to |
|
Yes |
|
Directory holding |
|
Yes |
|
Spring resource path to the device template directory |
|
No |
|
Login query for the global domain scope |
no default; set in the shipped |
Yes |
|
Login query for a list of allowed domain ids |
no default; set in the shipped |
Yes |
|
Login query treating an unset domain as global |
no default; set in the shipped |
Yes |
|
HMAC-SHA256 signing key for JWT tokens; the shipped value is a development default |
no default; |
Yes |
|
Token lifetime in milliseconds |
no default; |
Yes |
|
Log level for |
|
No |
|
The sample blocks below also show |
# /usr/local/ft-system/.env
TZ=Europe/Kyiv
# Database (existing) -- MySQL profile
DB_HOST=<db-host-ip> # <-- replace; use the container name if it shares this bridge
DB_PORT=3306
DB_USER=ftacs
DB_PASSWORD=<your-db-password> # <-- replace
DB_PROFILE=mysql
SPRING_PROFILES_ACTIVE=mysql
# Hazelcast (existing, optional)
HZ_MEMBERS=<hazelcast-host-ip>:5701 # <-- replace
# FTACS (existing)
ACS_URL=http://<acs-host-ip> # <-- replace; https:// if ACS is exposed via HTTPS
ACS_PORT=8080 # 8080 (HTTP) or 443/8443 (HTTPS)
ACS_USERNAME=<acs-user> # <-- replace
ACS_PASSWORD=<acs-password> # <-- replace (get from FT Configs UI: <host>:<port>/acs/ftacsws-access)
|
|
For Oracle, keep the same root .env and change the database values inside it — DB_PROFILE=oracle, SPRING_PROFILES_ACTIVE=oracle, DB_HOST=<your-oracle-host>, DB_PORT=1521, and DB_SERVICE=XEPDB1 (DB_USER / DB_PASSWORD stay as they are).
compose.yml maps them to the ORACLE_* names the application expects, so no per-vendor environment file is involved.
See Switching to Oracle for the full variable list, and Configuration Guide — Oracle Profile for the datasource details.
Create northbound-api/.env with the per-service variables documented in Configuration Guide — Environment Variables; the full platform-wide reference is Northbound API Environment Variables.
Click to expand a commented northbound-api/.env reference
# =============================================================================
# Northbound API
# =============================================================================
# Container connector ports. The host ports are set in the ports: mapping of
# compose.yml (9880 -> 8080, and 9444 -> 8443 when HTTPS is enabled), not here.
# PORT is the HTTP connector port inside the container.
PORT=8080
# HTTPS connector port inside the container. Only used when SERVER_SSL_ENABLED=true;
# it must differ from PORT so both connectors can bind.
# HTTPS_PORT=8443
# =============================================================================
# Configuration Paths (inside container)
# =============================================================================
# Path to api.properties -- contains API behavioral settings (logging, timeouts,
# device handling, authentication, file upload). Required for NBI to start.
API_CONFIG_PATH=file:/etc/app/api.properties
# Path to Hazelcast client configuration directory
CACHE_CONFIG_PATH=file:/etc/app/
# =============================================================================
# HikariCP Connection Pool
# =============================================================================
DB_MAX_POOL_SIZE=10
DB_MIN_IDLE=5
DB_CONNECTION_TIMEOUT_MS=30000
# =============================================================================
# Group Update -- OneIoT (iotw) Datasource
# The Group Update API reads condition views from the OneIoT (iotw) schema
# through a second datasource. The main DB host/port/credentials come from
# root .env; only the items below are NBI-specific.
# =============================================================================
# MySQL: schema name of the OneIoT database. Same host/port/user/password as
# the main ftacs datasource -- only the schema differs. Default: iotw
MYSQL_SCHEMA_IOTW=iotw
# Oracle: the iotw schema is identified by a dedicated account. Set these only
# when running the Oracle profile (each falls back to ORACLE_USER/ORACLE_PASSWORD).
# ORACLE_USER_IOTW=iotw
# ORACLE_PASSWORD_IOTW=<your-iotw-password> # <-- replace
# =============================================================================
# HTTPS/TLS (optional)
# HTTP and HTTPS can run together when they use different container ports.
# SERVER_SSL_ENABLED=false -> HTTP only.
# SERVER_SSL_ENABLED=true -> HTTPS on HTTPS_PORT (e.g. 8443).
# In dual mode, keep PORT different from HTTPS_PORT.
# =============================================================================
SERVER_SSL_ENABLED=false
# SERVER_SSL_KEY_STORE=file:/etc/app/keystore.p12
# SERVER_SSL_KEY_STORE_PASSWORD= # <-- replace if enabled
# SERVER_SSL_KEY_STORE_TYPE=PKCS12
# SERVER_SSL_KEY_PASSWORD=
# SERVER_SSL_KEY_ALIAS=server
# SERVER_SSL_TRUST_STORE=
# SERVER_SSL_TRUST_STORE_PASSWORD=
# =============================================================================
# SQL Login Queries
# Choose ONE set depending on which portal stack is deployed.
# Current Java/Spring stack (Support Portal, Management Portal) -- iotw_user table.
# Legacy C# stack (CSR, CPEAdmin) -- admin.login table.
# =============================================================================
SQL_LOGIN_BY_NAME_AND_LOCATION0="SELECT password, domain_id FROM iotw_user WHERE username = :name AND domain_id = 0"
SQL_LOGIN_BY_NAME_AND_LOCATIONS="SELECT password, domain_id FROM iotw_user WHERE username = :name AND domain_id IN (:ids)"
SQL_LOGIN_BY_NAME_NULL_OR_0="SELECT password, domain_id FROM iotw_user WHERE username = :name AND (domain_id IS NULL OR domain_id = 0)"
# -- Legacy C# stack (csr / cpeAdmin portals) -- admin.login table --
# SQL_LOGIN_BY_NAME_AND_LOCATION0="SELECT password, location_id FROM admin.login WHERE name = :name AND location_id = 0"
# SQL_LOGIN_BY_NAME_AND_LOCATIONS="SELECT password, location_id FROM admin.login WHERE name = :name AND location_id IN (:ids)"
# SQL_LOGIN_BY_NAME_NULL_OR_0="SELECT password, location_id FROM admin.login WHERE name = :name AND (location_id IS NULL OR location_id = 0)"
# =============================================================================
# Authentication (JWT)
# =============================================================================
# Secret key used to sign and verify JWT tokens (HMAC-SHA256).
# Minimum 32 characters. Must be unique per environment (dev / staging / prod).
# The default value is for development only -- never use it in production.
# Generate a secure value: openssl rand -base64 48
JWT_TOKEN_SECRET=friendly-tech-jwt-secret-key-2025
# Token expiration time in milliseconds (36000000 = 10 hours)
JWT_TOKEN_EXPIRED=36000000
# =============================================================================
# Device Operation Timeout
# =============================================================================
# Max wait time (seconds) for device RPC operations (GetParameterValues,
# SetParameterValues, Download, etc.), per the platform environment reference.
# This build resolves its device and ACS timeouts from api.properties
# (api.settings.timeout.*) -- see the note above.
TIMEOUT=60
# =============================================================================
# Logging
# =============================================================================
# Log level for the application package (com.friendly.northboundapi).
# Values: ERROR, WARN, INFO, DEBUG. In DEBUG mode business errors include stack traces.
# Can also be changed at runtime via Actuator -- see Configuration Guide.
LOGGING_LEVEL_COM_FRIENDLY_NORTHBOUNDAPI=DEBUG
5.3. TLS Keystore (optional)
NBI serves its HTTPS port from a PKCS12 keystore, keystore.p12.
To enable HTTPS:
-
Generate the keystore (or import an existing certificate into one):
keytool -genkeypair \ -alias server \ -keyalg RSA \ -keysize 2048 \ -storetype PKCS12 \ -keystore keystore.p12 \ -validity 3650 \ -storepass <keystore-password> \ -keypass <key-password> \ -dname "CN=localhost, OU=Dev, O=Friendly, L=Local, ST=Local, C=US" \ -ext "SAN=dns:localhost,ip:127.0.0.1" -
Place
keystore.p12innorthbound-api/config/(mounted read-only at/etc/app). -
Set
SERVER_SSL_ENABLED=true,HTTPS_PORT=8443,SERVER_SSL_KEY_STORE=file:/etc/app/keystore.p12,SERVER_SSL_KEY_STORE_PASSWORD, andSERVER_SSL_KEY_PASSWORDinnorthbound-api/.env. -
Uncomment the
9444:8443line in theports:mapping ofcompose.yml, then recreate the container — the HTTPS port is not published by default.
|
|
For production, use a certificate issued by your CA/security team, exported to PKCS12 format.
6. Deployment
6.1. Startup Dependencies
NBI has no depends_on on its infrastructure — the database, Hazelcast, and FTACS live outside this stack, so Compose cannot gate on their health.
All of them must be reachable before NBI launches.
Wait for each of them explicitly before starting the service:
# Wait for the database
until nc -z <db-host-ip> 3306; do sleep 2; done
# Wait for Hazelcast (if caching is used)
until nc -z <hazelcast-host-ip> 5701; do sleep 2; done
# Wait for FTACS
until curl -sf http://<acs-host-ip>:8080/rest/swagger-ui/index.html; do sleep 2; done
# Then start NBI
docker compose up -d northbound-api
NBI itself restarts cleanly at any time once the infrastructure is up — restart: unless-stopped reconnects it after a host reboot.
6.2. Docker Compose
Click to expand compose.yml (Northbound API)
x-db-env: &db-env
DB_VENDOR: ${DB_PROFILE}
MYSQL_HOST: ${DB_HOST}
MYSQL_PORT: ${DB_PORT}
MYSQL_USER: ${DB_USER}
MYSQL_PASSWORD: ${DB_PASSWORD}
MYSQL_SCHEMA: ftacs
services:
northbound-api:
image: hub.friendly-tech.com/api/northbound-api:latest
container_name: northbound-api
# The database, Hazelcast and FTACS are external to this stack, so there
# is no depends_on. They must be reachable before northbound-api starts --
# DB_HOST and ACS_URL in .env, and the member list in
# hazelcast-client.yaml, point at them.
env_file:
- .env
- ./northbound-api/.env
environment:
<<: *db-env
API_CONFIG_PATH: file:/etc/app/api.properties
CACHE_CONFIG_PATH: file:/etc/app/
ports:
- "9880:8080" # HTTP
# - "9444:8443" # Optional: publish when HTTPS is enabled
volumes:
- ./northbound-api/config:/etc/app:ro
- ./northbound-api/logs:/app/logs
extra_hosts:
- "host.docker.internal:host-gateway"
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/iot-webservice/actuator/health"]
interval: 30s
timeout: 3s
start_period: 60s
retries: 3
restart: unless-stopped
networks:
- ft-network
networks:
ft-network:
driver: bridge
For Oracle, the env_file: list stays the same — only the x-db-env anchor changes to Oracle-specific mappings, and the DB_* values are updated in the root .env (see [oracle-profile] and Switching to Oracle):
x-db-env: &db-env
DB_VENDOR: ${DB_PROFILE}
ORACLE_HOST: ${DB_HOST}
ORACLE_PORT: ${DB_PORT}
ORACLE_SERVICE: ${DB_SERVICE}
ORACLE_USER: ${DB_USER}
ORACLE_PASSWORD: ${DB_PASSWORD}
The 9444:8443 mapping is only meaningful once SERVER_SSL_ENABLED=true — it is commented out in the reference compose (docker/compose.yml in the source repository), so an HTTP-only deployment publishes 9880 only.
6.2.1. Start the Stack
|
Start NBI only after the database, and Hazelcast (if used), accept connections, and FTACS answers — see Startup Dependencies. |
cd /usr/local/ft-system
docker login hub.friendly-tech.com
docker compose up -d northbound-api
docker compose ps
# NBI takes a few seconds to a couple of minutes to start
docker compose logs -f northbound-api | grep -i "started"
curl -s http://localhost:9880/iot-webservice/actuator/health
To run NBI as a single container without Compose:
cd /usr/local/ft-system
docker run -d \
--name northbound-api \
--restart unless-stopped \
--env-file .env --env-file ./northbound-api/.env \
-p 9880:8080 \
-p 9444:8443 \
-v ./northbound-api/config:/etc/app:ro \
-v ./northbound-api/logs:/app/logs \
--add-host=host.docker.internal:host-gateway \
hub.friendly-tech.com/api/northbound-api:latest
|
|
7. Verification
7.1. Startup Log
Wait a few seconds after start, then check the logs:
# Docker Compose
docker compose logs northbound-api | tail -20
# Docker standalone
docker logs northbound-api | tail -20
A successful startup ends with the StartupInfoLogger summary banner (timestamp, PID, version, build, and Java runtime vary; the active profile and connector reflect your deployment):
INFO --- [northbound-api] [main] c.f.n.config.StartupInfoLogger :
============================================================
Northbound API startup summary
------------------------------------------------------------
Active profiles : <active-profile>
Connectors : http (:8080)
Context path : /iot-webservice
Swagger UI : /iot-webservice/swagger-ui/index.html
SOAP Endpoint : /iot-webservice/FTACSWS/ACSWS
SOAP WSDL : /iot-webservice/FTACSWS/ACSWS?wsdl
API version : <version>
API build : <build>
Java runtime : <java-version>
PID : <pid>
============================================================
7.2. Endpoint Checks
# Health check
curl -s http://localhost:9880/iot-webservice/actuator/health
# Expected: {"status":"UP"}
# HTTPS health check (when SERVER_SSL_ENABLED=true)
curl -ks https://localhost:9444/iot-webservice/actuator/health
# Swagger UI
curl -I http://localhost:9880/iot-webservice/swagger-ui/index.html
# SOAP WSDL
curl -s http://localhost:9880/iot-webservice/FTACSWS/ACSWS?wsdl | head -5
The health check and Swagger UI must return HTTP 200.
The web interface is reachable at http://localhost:9880 (and https://localhost:9444 when HTTPS is enabled and that mapping is uncommented).
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.
The published ports are the literal values in the ports: mapping of the compose file above; the container ports come from PORT (default 8080) and HTTPS_PORT (default 8443).
8.1. HTTP / HTTPS
| Port | Protocol | Purpose | Exposure |
|---|---|---|---|
9880 → 8080 |
HTTP |
REST, SOAP, Swagger UI and Actuator; container port set by |
Public (back-office clients) |
9444 → 8443 |
HTTPS |
Same endpoints over TLS, only when |
Public (back-office clients) once published |
8.2. Outbound Connections
Ports NBI dials on the existing infrastructure.
These are the published ports of those services — confirm them against whoever operates them, and set the matching variables in .env.
| Destination | Port | Protocol | Purpose |
|---|---|---|---|
Relational database ( |
3306 (MySQL), 1521 (Oracle) |
TCP |
JDBC connections; set through |
Hazelcast cluster (optional) |
5701 |
TCP |
Cache client connection; member addresses come from |
FTACS SOAP endpoint |
8080, or 443/8443 for HTTPS |
TCP |
Device operations over SOAP; set through |
8.3. HTTP Endpoints
| Method | Path | Purpose | Auth |
|---|---|---|---|
GET |
|
Health check used by the container healthcheck |
None |
GET |
|
Prometheus metrics scrape endpoint |
None |
GET |
|
Swagger UI (REST + SOAP) |
None |
GET |
|
SOAP web service WSDL |
None |
The security filter chain permits every request (SecurityConfig.java), so these endpoints are open on whatever interface the ports are published on.
Restrict access at the firewall. For how the REST and SOAP operations themselves are authenticated, see API & Integration — Authentication.
|
All endpoints are exposed under the |
9. Stack Management
9.1. Logs
# Follow logs
docker compose logs -f northbound-api
# Last 100 lines
docker compose logs --tail 100 northbound-api
The application writes to northbound-api/logs/ on the host (mounted at /app/logs inside the container).
9.2. Start, Stop, Restart
cd /usr/local/ft-system
docker compose ps # status of every service
docker compose stop northbound-api # stop the service
docker compose start northbound-api # start it again
docker compose restart northbound-api # restart it
docker compose down # stop and remove all containers
9.4. Updating Northbound API
cd /usr/local/ft-system
# 1. Back up the NBI configuration and logs
tar -czf northbound-api-backup-$(date +%Y%m%d).tar.gz ./northbound-api
# 2. Update the image tag in compose.yml, then:
docker compose pull northbound-api
docker compose up -d northbound-api
# 3. Verify
curl -s http://localhost:9880/iot-webservice/actuator/health
Configuration files and logs are preserved across the update.
10. Production Checklist
-
Change all default passwords in
.env/northbound-api/.env(DB_PASSWORD,ACS_PASSWORD,SERVER_SSL_KEY_STORE_PASSWORD). -
Replace the default JWT secret —
JWT_TOKEN_SECRETships with a development value; generate a secure one withopenssl rand -base64 48. -
Use a valid SSL certificate — replace any self-signed
keystore.p12with a CA-issued certificate. -
Configure firewall rules — allow only the required ports; restrict database access to NBI’s host only.
-
Select the correct SQL Login Queries set — current Java/Spring stack (
iotw_user) or legacy C# stack (admin.login); comment out the one not in use. -
Secure configuration files — restrict permissions:
chmod 600 northbound-api/.env. -
Enable auto-restart —
restart: unless-stoppedincompose.yml, or--restart unless-stoppedwithdocker run. -
Verify the health check — the compose file defines it; add
--health-cmdfor standalonedocker run. -
Set up monitoring — deploy Prometheus and Grafana against
/iot-webservice/actuator/prometheus. -
Configure log rotation —
northbound-api/logs/grows unbounded without externallogrotate. -
Set resource limits — add
--memory=2gordeploy.resources.limitsin compose.yml for production.
11. Troubleshooting
11.1. Container Fails to Start
Symptom: The northbound-api container exits immediately after docker compose up.
Fix:
-
Read the error message from the logs:
docker compose logs northbound-api docker events --filter container=northbound-api -
Confirm the environment files are loaded:
docker exec northbound-api env | grep -E "DB_|MYSQL_|ACS_" -
Confirm
northbound-api/config/exists and is populated — an empty mount at/etc/appis a common cause ofCould not resolve placeholder 'API_CONFIG_PATH'.
11.2. Database Connection Failure
Symptom: MySQL Communications link failure or Oracle ORA-12514 in logs.
Fix:
-
Test connectivity from inside the container:
docker exec northbound-api nc -zv <db-host-ip> 3306 -
Verify
DB_HOST,DB_PORT,DB_USER, andDB_PASSWORDin.envmatch the database configuration, and that the container can reachMYSQL_SCHEMA_IOTW(defaultiotw) on the same host.
11.3. FTACS Connection Failure
Symptom: Device operations fail with a SOAP fault or connection timeout; server.log shows a connection refused/timeout to the ACS host.
Fix:
-
Verify FTACS answers from inside the container:
docker exec northbound-api curl -s http://<acs-host-ip>:8080/rest/swagger-ui/index.html -
Confirm
ACS_URLandACS_PORTin.envpoint at the correct scheme (http://orhttps://) and port for how FTACS is exposed. -
Confirm
ACS_USERNAME/ACS_PASSWORDmatch a valid FTACS web service account (FT Configs UI:<host>:<port>/acs/ftacsws-access).
11.4. Hazelcast Connection Failure
Symptom: Unable to connect to any address in logs (only relevant when Hazelcast caching is used).
Fix:
-
Verify Hazelcast members are running and reachable on port 5701.
-
Check that
cluster-nameandcluster-membersinhazelcast-client.yamlmatch the server configuration. -
Confirm
CACHE_CONFIG_PATHinnorthbound-api/.envpoints to the mounted/etc/app/directory.
11.5. Could not Resolve Placeholder 'API_CONFIG_PATH'
Symptom: PlaceholderResolutionException: Could not resolve placeholder 'API_CONFIG_PATH'.
Fix:
-
Verify
API_CONFIG_PATH=file:/etc/app/api.propertiesis set innorthbound-api/.env. -
Confirm
api.propertiesexists innorthbound-api/config/and the volume mount (./northbound-api/config:/etc/app:ro) is in place.
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 :9880 netstat -tulpn | grep 9880 -
Either stop that process, or change the published port in the
ports:mapping ofcompose.ymland rundocker compose up -dagain.
11.7. Permission Denied on Mounted Volumes
Symptom: The container cannot write to northbound-api/logs.
Fix:
sudo chown -R $(whoami):$(whoami) /usr/local/ft-system/northbound-api
chmod -R 755 /usr/local/ft-system/northbound-api
11.8. Out of Memory
Symptom: java.lang.OutOfMemoryError in the logs, or the container is killed by the OOM killer.
Fix:
-
Raise the container memory limit (Docker Desktop: Settings → Resources → Memory), or add
deploy.resources.limits.memoryincompose.yml. -
If the problem persists, capture a heap dump and analyse it for leaks.
11.9. Build Fails: Permission Denied: ./gradlew
Symptom: docker buildx build from source fails on ./gradlew (developers building from source only — not applicable to a registry image pull).
Fix:
-
On the host:
chmod +x gradlew. -
Ensure your source checkout preserves execute permissions.
11.10. Getting Support
If the issue persists, collect the following and contact Friendly Tech support:
-
.envandnorthbound-api/.envwith passwords masked. -
northbound-api/logs/— the last 1000 lines. -
java -versionoutput, if building from source. -
Operating system version.
-
Database type and version.
-
docker versionoutput.
12. Related Documentation
-
Configuration Guide — Environment Variables — every variable referenced in
.envandnorthbound-api/.env. -
Configuration Guide — Cache Configuration —
hazelcast-client.yamland the optional Hazelcast dependency. -
Database — Data Source Configuration — the
ftacsandiotwdatasources this guide points at. -
API & Integration — Authentication — how requests to the published ports are authenticated.
-
All in one server deployment — deploying the database, Hazelcast and FTACS on a single host.
-
Separate server deployment — Server C — the split topology this service joins.
-
Java API Stack deployment — NBI together with Service API and Provision Portal.
-
Northbound API Environment Variables — the platform-wide variable reference.