Installation & Deployment

This guide covers deploying FT Configs Service — the centralized configuration management backend for ACS, Angular Console, Provision Portal, Northbound API, and Service API — as a single Docker container.

FT Configs Service does not ship a database or Hazelcast; both are external dependencies you provision separately (see Required External Dependencies). Its companion frontend, FT Configs UI, is a separate container with its own installation guide and is out of scope here. To deploy the two together as one stack, see FT Configs Stack deployment.

1. Overview

FT Configs Service is deployed as a single container (ft-configs-service) — a Spring Boot 4 / Java 25 application — exposing its REST API, Actuator, and Swagger UI under the context path /configs-service. Docker Compose is the only deployment method described here: the repository builds a Spring Boot fat jar and a container image, not a distribution archive. Configurations are persisted in MySQL or Oracle and published to a Hazelcast cache for runtime consumers, chiefly FTACS.

The database, the Hazelcast cluster, and FTACS itself are assumed to be deployed already — see All in one server deployment or Separate server deployment.

Angular Console / Provision Portal / Northbound API  ->  FT Configs Service  ->  MySQL / Oracle  +  Hazelcast  ->  FTACS

FT Configs Service publishes ACS runtime configuration into the shared Hazelcast cluster that FTACS consumes. FTACS treats it as a hard dependency since version 6.5.2: without a published snapshot, FTACS starts with no ACS configuration, no web service users, and no parameter-name cache rules — silently, with no connection error in either log. FT Configs Service and FTACS must be clients of the same Hazelcast cluster. FTACS is not deployed or documented here; see Required External Dependencies.

2. Prerequisites

2.1. Host Requirements

Component Minimum Recommended Notes

Docker Engine

20.10

Latest stable

Runs the ft-configs-service container

Docker Compose

2.0

Latest stable

The compose file in Docker Compose uses Compose v2 syntax

RAM

2 GB

4 GB

The JVM is container-aware (-XX:MaxRAMPercentage=75.0, set in the image)

Free disk space

1 GB

1 GB or more

Image plus the mounted logs/ directory

JDK 25 is only required for local Gradle builds (build.gradle pins the Java toolchain to 25). 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 FT Configs Service starts. None of them are deployed by this guide.

Component Minimum Version Why It Is Needed Port Required

MySQL or Oracle

MySQL 8.4; Oracle — verify with FT DevOps

Stores every configuration the service serves. Liquibase creates the tables on first startup inside an already existing configs schema owned by a dedicated user — see Create the Database Schema and User. Without it startup fails.

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

Yes

Hazelcast

5.7

Receives the published configuration snapshots. It must be the same cluster FTACS connects to; without it FTACS starts with no ACS configuration, no web service users, and no parameter-name cache rules. Not needed when the service runs without FTACS integration.

5701 (TCP)

No (optional)

The Hazelcast minimum version reflects the client library the service ships with (com.hazelcast:hazelcast:5.7.0 in build.gradle); the MySQL minimum reflects the image the repository’s own compose file uses (mysql:8.4). This repository pins no Oracle server version: the Oracle JDBC driver is com.oracle.database.jdbc:ojdbc11:23.26.2.0.0 and the Oracle integration tests run against the Testcontainers image gvenzl/oracle-xe:21, neither of which is a supported-version statement. Confirm the supported Oracle release with FT DevOps before deploying. The configs schema is FT Configs Service’s own schema, separate from the shared ftacs schema that FTACS uses. Deploying this infrastructure is out of scope here — see All in one server deployment or Separate server deployment.

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 — ft-configs/backend folder for the deployment files (compose.yml, the shared .env, and the per-service ft-configs-service/.env) and the configuration templates.

2.5. Create the Database Schema and User

FT Configs Service does not create the database, schema, or user itself. Liquibase migrations run on startup and create the tables inside an already-existing schema, using a user that already has the required privileges. Provision the schema and user before the first startup; the credentials must match the values you set in ft-configs-service/.env in Environment Configuration.

The passwords in the snippets below (ftacs_configs, configs) are defaults intended for development. Use a secure password in production and set the same value in the corresponding .env file.

MySQL

Connect as a privileged user (e.g. root) and create the configs schema and the ftacs_configs user:

CREATE DATABASE IF NOT EXISTS configs;
CREATE USER IF NOT EXISTS 'ftacs_configs'@'%' IDENTIFIED BY 'ftacs_configs';
GRANT ALL PRIVILEGES ON configs.* TO 'ftacs_configs'@'%';
FLUSH PRIVILEGES;
The shared QA provisioning script creates several service databases (flowable, ftacs_quartz, ftacs_qoe_ui, configs) in one file. Only the configs schema and the ftacs_configs user shown above are required by FT Configs Service.
Oracle

Connect as a privileged user (e.g. SYS/SYSTEM) and create the tablespace and the configs user:

CREATE TABLESPACE configs_data
DATAFILE '&1/configs_data01.dbf' SIZE 100M
AUTOEXTEND ON NEXT 10M MAXSIZE UNLIMITED;

CREATE USER configs IDENTIFIED BY configs
DEFAULT TABLESPACE configs_data
TEMPORARY TABLESPACE temp
QUOTA UNLIMITED ON configs_data;

GRANT CONNECT, RESOURCE TO configs;
GRANT CREATE TABLE TO configs;
GRANT CREATE SEQUENCE TO configs;
GRANT CREATE VIEW TO configs;

This user (configs) and its password must match ORACLE_USER/ORACLE_PASSWORD in ft-configs-service/.env. The connection uses the XEPDB1 pluggable-database service, selected through DB_SERVICE=XEPDB1 in the shared .env — see Switching to Oracle.

3. Network Requirements

Outbound connections FT Configs Service opens to its infrastructure. 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. Inbound connections — API clients (Angular Console, Provision Portal, Northbound API) and Prometheus scraping — arrive on the published HTTP/HTTPS ports listed in Port Reference.

Destination Port Protocol Purpose

Database

3306 (MySQL), 1521 (Oracle)

TCP

JDBC connection to the configs schema

Hazelcast

5701

TCP

Publishes configuration snapshots to the cluster FTACS reads from

For a quick connectivity check from any host:

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

3.1. Docker Networking

The compose file in Docker Compose attaches the container to a user-defined bridge network (ft-network) and adds extra_hosts: "host.docker.internal:host-gateway".

  • Peers on the same bridge are reached by container name and container port (for example mysql:3306).

  • When the database or Hazelcast cluster runs on the host machine rather than in Docker, use host.docker.internal (works on Docker Desktop, and on Linux through the extra_hosts entry above) or the host’s real IP address in MYSQL_HOST / ORACLE_HOST and HZ_MEMBERS. localhost will not work — inside the container it points at the container itself.

  • Peers on another host are reached by that host’s IP and the published port.

4. Registry Authentication

FT Configs Service 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

readonly

Password

fokxuw-fymte1-taSxyc

The readonly account provides pull-only access to the published images. It cannot push. To request elevated Harbor access, contact the DevOps team.

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. The image in Harbor is multi-arch (linux/amd64, linux/arm64); without --platform, docker pull selects the host architecture, which may not match the target. On an Apple Silicon (arm64) Mac or an arm64 Windows machine without --platform, the resulting archive will be arm64 and will fail with a platform does not match warning on amd64 servers. The examples below use linux/amd64; step 1 shows how to read the correct value off the offline host.

  1. On the offline host, find out which architecture it runs — this is the value you will pass as PLATFORM below. Ask Docker itself, since it reports what the daemon will actually accept:

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

    If Docker is not installed there yet, use the operating system instead — uname -m on Linux/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 would be swallowed as input:

    docker login hub.friendly-tech.com
  3. Pull and export the image. Paste the whole block as-is; the only lines to change are PLATFORM and TAG on top.

    Linux / macOS (bash):

    PLATFORM=linux/amd64
    TAG=latest
    
    docker pull --platform "$PLATFORM" "hub.friendly-tech.com/configs/ft-configs-service:$TAG"
    docker save "hub.friendly-tech.com/configs/ft-configs-service:$TAG" | gzip > "ft-configs-service-$TAG.tar.gz"

    Windows (PowerShell):

    $PLATFORM = "linux/amd64"
    $TAG = "latest"
    
    docker pull --platform $PLATFORM "hub.friendly-tech.com/configs/ft-configs-service:$TAG"
    docker save -o "ft-configs-service-$TAG.tar" "hub.friendly-tech.com/configs/ft-configs-service:$TAG"

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

  4. Transfer the archive to the offline host, together with compose.yml, the shared .env, and ft-configs-service/.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-configs-service-$TAG.tar.gz" | docker load
    docker compose up -d ft-configs-service

    Windows (PowerShell):

    $TAG = "latest"
    
    docker load -i "ft-configs-service-$TAG.tar"
    docker compose up -d ft-configs-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/configs/ft-configs-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-configs-service.

This covers the FT Configs Service image only. The database and Hazelcast 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. If you are deploying the paired UI as well, see FT Configs Stack deployment — Offline Servers.

5. Preparation

5.1. Directory Structure

mkdir -p /usr/local/ft-system/ft-configs-service/{config,logs}
cd /usr/local/ft-system

# The container runs as a non-root user (UID 1001) and writes file logs to the
# mounted logs/ directory. Grant write access so the application can write logs:
sudo chown -R 1001:1001 /usr/local/ft-system/ft-configs-service/logs

The container runs as the non-root user appuser (UID/GID 1001). The bind-mounted logs/ directory is created on the host as root, so the container cannot write to it until ownership is granted. Without this step the application logs to stdout only (visible via docker logs), and file logging under /app/logs fails silently. The config/ directory is read-only for the app and needs no ownership change.

Download the deployment files from the FT_DISK — ft-configs/backend folder, or copy the templates from the source repository’s docker/ directory.

5.1.1. Directory Layout

FT Configs Service occupies one subdirectory of the platform working directory, alongside compose.yml and the shared .env:

/usr/local/ft-system/
├── compose.yml                             # from FT_DISK
├── .env                                    # shared stack environment
└── ft-configs-service/
    ├── .env                                # per-service environment
    ├── config/                             # -> /etc/app
    │   └── keystore.p12                    # TLS keystore (only when HTTPS is enabled)
    └── logs/                               # -> /app/logs (written by the container as UID 1001)
Path Content Backup

compose.yml

Stack definition; the FT Configs Service block is shown in Docker Compose.

Yes

.env

Shared stack environment: database connection (DB_PROFILE, DB_HOST, DB_PORT, DB_SERVICE), Hazelcast, inter-service URLs, COMPOSE_PROFILES.

Yes

ft-configs-service/.env

Per-service environment: host ports, schema credentials, HikariCP pool, JVM settings, JWT, bootstrap admin, CORS, mail.

Yes

ft-configs-service/config/

Mounted to /etc/app. Read-only for the application; holds keystore.p12 when HTTPS is terminated in the service.

Yes

ft-configs-service/logs/

Mounted to /app/logs. Application file logs, written by the container as UID 1001; rotated by the application.

No

5.2. Environment Configuration

FT Configs Service reads its configuration from environment variables loaded via --env-file / env_file. The platform uses a two-layer environment file architecture — there is no file per database vendor:

File Purpose

.env

Shared by every service: database connection (DB_PROFILE, DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_SERVICE), Flowable DB, ClickHouse, PostgreSQL, Hazelcast, JWT, inter-service URLs, COMPOSE_PROFILES

ft-configs-service/.env

Per-service only: host ports, the configs schema credentials, HikariCP pool tuning, JVM settings, feature flags

MySQL is the default. Switching to Oracle is not a file swap — change DB_PROFILE, SPRING_PROFILES_ACTIVE, DB_HOST, DB_PORT and DB_SERVICE inside the single root .env, as listed in Switching to Oracle. compose.yml maps the generic DB_* names onto the vendor-specific ones this service expects through the service’s environment: block — MYSQL_HOST/MYSQL_PORT in a MySQL stack, ORACLE_HOST/ORACLE_PORT/ORACLE_SERVICE in an Oracle one — so the application keeps reading MYSQL_* / ORACLE_* unchanged. The block is shown in Docker Compose; when you write your own compose.yml, it must be present, otherwise these variables stay unset in the container and the service falls back to its built-in defaults and cannot reach the database.

The most important values to set, and the layer each belongs to:

Variable Description Default Required

SPRING_PROFILES_ACTIVE

Selects the database profile: mysql or oracle. Set DB_PROFILE in the shared .env; compose.yml injects it under this name.

mysql

Yes

JWT_SECRET

Base64-encoded HMAC-SHA256 key that decodes to at least 32 bytes; startup fails otherwise. Generate per environment: openssl rand -base64 32. Set in ft-configs-service/.env.

empty

Yes

FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORD

Password of the bootstrap admin account (username admin by default); the account is not created if this is empty. Minimum 8 characters. Rotate immediately via /auth/first-login after the first login. Set in ft-configs-service/.env.

empty

Yes, on first startup

MYSQL_HOST / MYSQL_PORT / MYSQL_SCHEMA / MYSQL_USER / MYSQL_PASSWORD

MySQL connection — must match the schema and user created in Create the Database Schema and User. Host and port come from DB_HOST / DB_PORT in the shared .env via the compose environment: block; schema, user and password are set in ft-configs-service/.env.

mysql / 3306 / configs / ftacs_configs / ftacs_configs

Yes, with mysql

ORACLE_HOST / ORACLE_PORT / ORACLE_SERVICE / ORACLE_USER / ORACLE_PASSWORD

Oracle connection — must match the user created in Create the Database Schema and User. Host, port and service come from DB_HOST / DB_PORT / DB_SERVICE in the shared .env via the compose environment: block; user and password are set in ft-configs-service/.env.

oracle / 1521 / XEPDB1

Yes, with oracle

CORS_ALLOWED_ORIGINS

The UI host/port the user types in the browser, for both HTTP and HTTPS. Never the internal Docker service name. In a Docker deployment replace <ui-host> with the real host: http://<ui-host>:3001,https://<ui-host>:3443. Set in ft-configs-service/.env.

http://localhost:9002

No

HZ_MEMBERS

Hazelcast member(s), e.g. hazelcast-host:5701. Must point at the same cluster FTACS uses — see Overview. Set in the shared .env and passed through by the compose environment: block. Commented out in the shipped templates.

unset

No

TZ

Container timezone. Set in the shared .env.

UTC

No

Download both env files from FT_DISK, or create them from the FT Configs Service Environment Variables reference:

cd /usr/local/ft-system
touch .env ft-configs-service/.env

SharePoint may strip the leading dot when downloading dotfiles; rename env back to .env after the download.

When the database or Hazelcast cluster runs on the host machine rather than in Docker, set the host-side addresses as described in Docker Networking.

The full variable reference:

Do not commit secrets (*_PASSWORD, JWT_SECRET) into Git. Use .env locally, and use Secret managers (Kubernetes Secrets, Vault, etc.) in shared environments.

HZ_MEMBERS must be reachable from inside the container/pod (Docker and Kubernetes networking apply).

5.3. A) Quick start (most common knobs)

Variable Required Default Description

SPRING_PROFILES_ACTIVE

Yes

local

Active Spring profile: local, mysql, or oracle. Controls which application-*.yml is loaded. Must be set explicitly in Docker and Kubernetes.

SERVER_PORT

No

8080

Primary server port. When SERVER_SSL_ENABLED=true, this becomes the HTTPS port.

SERVER_HTTP_PORT

No

8080

Plain HTTP port. In dual-mode (HTTP + HTTPS), set this to a different value than SERVER_PORT (e.g., 8080 for HTTP, 8443 for HTTPS).

HZ_MEMBERS

No

from Hazelcast client config

Comma-separated Hazelcast members (host:port). Required in Docker/Kubernetes. Default port 5701 is added automatically if missing.

CORS_ALLOWED_ORIGINS

No

http://localhost:9002

Comma-separated list of allowed CORS origins. Must match the URL the user sees in the browser address bar (where the frontend is loaded from), never the internal Docker service name. Scheme-sensitive: http:// and https:// are different origins — the UI is served on both HTTP (default port 3001) and HTTPS (default port 3443, both published by Docker), so list both. Examples: http://localhost:9002 (dev), http://<ui-host>:3001,https://<ui-host>:3443 (Docker, HTTP + HTTPS UI), https://management.example.com (production).

LOGGING_LEVEL_COM_FRIENDLY_FTCONFIGSSERVICE

No

INFO

Log level for the application package. Values: ERROR, WARN, INFO, DEBUG. Can also be changed at runtime via Actuator.

5.4. B) Database (MySQL profile: SPRING_PROFILES_ACTIVE=mysql)

Provide either MYSQL_JDBC_URL or the decomposed MYSQL_HOST/MYSQL_PORT/MYSQL_SCHEMA inputs.

Variable Required Default Description

MYSQL_JDBC_URL

No

derived if unset

Full MySQL JDBC URL. Recommended for Docker/Kubernetes to avoid host, port, and schema drift.

DB_HOST

No

localhost

Shared DB host fallback used by MySQL and Oracle when profile-specific host is not set.

MYSQL_HOST

No

${DB_HOST:localhost}

MySQL host used if MYSQL_JDBC_URL is not set.

MYSQL_PORT

No

3306

MySQL port used if MYSQL_JDBC_URL is not set.

MYSQL_SCHEMA

No

configs

MySQL schema used if MYSQL_JDBC_URL is not set.

MYSQL_USER

No

ftacs (template: ftacs_configs)

MySQL username for the service. The env templates in docker/ ship with ftacs_configs.

MYSQL_PASSWORD

No

ftacs (template: ftacs_configs)

MySQL password for the service. The env templates in docker/ ship with ftacs_configs. Treat as a secret.

MYSQL_DRIVER_CLASS_NAME

No

com.mysql.cj.jdbc.Driver

Override JDBC driver class if needed.

DB_MAX_POOL_SIZE

No

10

HikariCP max pool size.

DB_MIN_IDLE

No

5

HikariCP minimum idle connections.

DB_CONNECTION_TIMEOUT_MS

No

30000

HikariCP connection timeout in milliseconds.

When both *_JDBC_URL and decomposed variables are set, the explicit JDBC URL always takes precedence.

5.5. C) Database (Oracle profile: SPRING_PROFILES_ACTIVE=oracle)

Provide either ORACLE_JDBC_URL or the decomposed ORACLE_HOST/ORACLE_PORT/ORACLE_SERVICE (plus optional ORACLE_SCHEMA) inputs.

Variable Required Default Description

ORACLE_JDBC_URL

No

derived if unset

Full Oracle JDBC URL. Recommended for Docker/Kubernetes to avoid host, port, and service drift.

ORACLE_HOST

No

${DB_HOST:localhost}

Oracle host used if ORACLE_JDBC_URL is not set.

ORACLE_PORT

No

1521

Oracle port used if ORACLE_JDBC_URL is not set.

ORACLE_SERVICE

No

XEPDB1

Oracle service name used if ORACLE_JDBC_URL is not set.

ORACLE_SCHEMA

No

empty

Optional schema mapped to hibernate.default_schema.

ORACLE_USER

No

ftacs

Oracle username for the service.

ORACLE_PASSWORD

No

ftacs

Oracle password for the service. Treat as a secret.

ORACLE_DRIVER_CLASS_NAME

No

oracle.jdbc.OracleDriver

Override Oracle JDBC driver class if needed.

5.6. D) Hazelcast / cache

Variable Required Default Description

CACHE_CONFIG_PATH

No

classpath:

Base path for Hazelcast config files. hazelcast-client.yaml is resolved relative to this path.

CACHE_IS_SERVER

No

false

When true, starts an embedded Hazelcast member from ${cache-config.path}hazelcast.yaml. Not used in production.

FT_CONFIGS_ACS_EXCLUSION_MODELS_CACHE_NAME

No

qoeProductClassGroupCache

Name of the Hazelcast cache from which ACS publishes product-class groups, consumed by the dataStoreExclusionModels dropdown. Must match the name ACS publishes to; the default matches the standard ACS deployment.

5.7. E) Auth / JWT & cookies

Variable Required Default Description

JWT_SECRET

No

defined in profile

Base64-encoded HMAC-SHA256 signing key. Must decode to at least 32 bytes (256-bit) or startup fails. Generate with openssl rand -base64 32. Must be unique per environment. Never commit to Git.

JWT_EXPIRATION

No

86400000

Access token expiration in milliseconds.

JWT_REFRESH_TOKEN_EXPIRATION

No

604800000

Refresh token expiration in milliseconds.

COOKIE_SECURE

No

true (template: false)

Set true when HTTPS is used end-to-end or via a trusted TLS-terminating proxy. The env templates ship with false for local development.

COOKIE_DOMAIN

No

empty

Optional domain for cross-subdomain cookie sharing.

5.7.1. Mail

Variable Required Default Description

MAIL_MODE

No

AUTO

Email delivery mode (case-insensitive; blank or unknown falls back to AUTO with a warning log). The supported values are AUTO and OFFLINE. AUTO infers the mode from SMTP config: it is treated as OFFLINE when MAIL_HOST, MAIL_USERNAME, or MAIL_PASSWORD are not all set (non-blank); when they are all set it attempts email, but if the SMTP server is unreachable at runtime it degrades to offline behaviour. OFFLINE never sends email. In offline mode (configured or degraded at runtime), create-user and reset-password return the temporary password in the API response (passwordDelivery=RETURNED) instead of emailing it, and user-deletion requests skip OTP (returning otpRequired=false); the account is deleted in the confirm step, called without an OTP.

MAIL_HOST

No

smtp.gmail.com

SMTP host used for registration and deletion flows. Under MAIL_MODE=AUTO, leaving this blank (together with the credentials) selects offline mode.

MAIL_PORT

No

587

SMTP port.

MAIL_USERNAME

No

SMTP username. Under MAIL_MODE=AUTO, leaving this blank selects offline mode (no email is sent; secrets are returned in API responses). The local profile uses an empty default (${MAIL_USERNAME:}).

MAIL_PASSWORD

No

SMTP password. Treat as a secret. Under MAIL_MODE=AUTO, leaving this blank selects offline mode. The local profile uses an empty default (${MAIL_PASSWORD:}).

MAIL_PROTOCOL

No

smtp

Usually smtp.

MAIL_SMTP_AUTH

No

true

SMTP authentication toggle.

MAIL_SMTP_STARTTLS

No

true

STARTTLS toggle.

MAIL_FROM

No

no-reply@friendly.local

From address used in sent emails.

MAIL_LOGIN_URL

No

empty

URL for the "Sign In" call-to-action button in emails. Must start with http:// or https://. If blank or non-HTTP, the CTA button is replaced by a generic text hint. Example: https://management.example.com/configs-service.

The MAIL_FROM_NAME variable has been removed. The sender display name is now localized via the mail.brand message key (mail_messages*.properties), returning "Configuration Center" (EN) or the localized equivalent for other locales.

5.7.2. Audit

Variable Required Default Description

AUDIT_RETENTION_DAYS

No

30

Number of days to retain audit events. Minimum 3, maximum 90. Set to 0 to disable retention. Values outside range are clamped with a warning.

AUDIT_REDACTION_FIELDS

No

empty

Comma-separated list of additional sensitive field names to redact in audit snapshots and change records. Added on top of the 18 built-in defaults (password, token, secret, apiKey, etc.).

5.7.3. Bootstrap admin (first install)

Variable Required Default Description

FT_CONFIGS_BOOTSTRAP_ADMIN_USERNAME

No

admin

Username for first startup when no admin exists.

FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORD

Yes (first startup)

empty

Password for bootstrap admin. Must be set on first startup or the admin account will not be created. Rotate immediately after bootstrap via /auth/first-login. Treat as a secret.

FT_CONFIGS_BOOTSTRAP_ADMIN_EMAIL

No

empty

Optional email assigned to bootstrap admin.

FT_CONFIGS_BOOTSTRAP_ADMIN_LOCALE

No

empty

Optional locale for bootstrap admin.

5.7.4. JVM / container runtime

Variable Required Default Description

JAVA_OPTS

No

set in Docker image

JVM options passed by docker-entrypoint.sh. Add only needed flags.

TZ

No

UTC

Container timezone.

For comprehensive details on every configuration option, see the Configuration Guide.

5.8. TLS Keystore (optional)

To enable HTTPS, provide a PKCS12 keystore file. Place keystore.p12 in the config/ directory on the host (mounted to /etc/app/ inside the container).

For local development, generate a self-signed keystore:

cd /usr/local/ft-system/ft-configs-service

keytool -genkeypair \
  -alias server \
  -keyalg RSA \
  -keysize 2048 \
  -storetype PKCS12 \
  -keystore config/keystore.p12 \
  -validity 3650 \
  -storepass <keystore-password> \
  -keypass <key-password> \
  -dname "CN=*.friendly-tech.com, OU=Dev, O=Friendly, L=Local, ST=Local, C=US" \
  -ext "SAN=dns:*.friendly-tech.com,dns:friendly-tech.com,dns:localhost,ip:127.0.0.1"

Then set in ft-configs-service/.env:

  • SERVER_SSL_ENABLED=true

  • SERVER_PORT=8443 (HTTPS primary), SERVER_HTTP_PORT=8080 (HTTP secondary — must differ)

  • SERVER_SSL_KEY_STORE=file:/etc/app/keystore.p12

  • SERVER_SSL_KEY_STORE_PASSWORD=<keystore-password>

  • SERVER_SSL_KEY_PASSWORD=<key-password>

For production, use a certificate issued by your CA/security team and export it to PKCS12 format.

6. Deployment

6.1. Startup Dependencies

FT Configs Service has no depends_on on its infrastructure — the database and Hazelcast cluster live outside this stack, so Compose cannot gate on their health. Both must be up before it launches: Liquibase applies the schema migrations on first start, and the Hazelcast client connects during context initialisation.

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
until nc -z <hazelcast-host-ip> 5701; do sleep 2; done

# Then start FT Configs Service
docker compose up -d ft-configs-service

FT Configs Service 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 (FT Configs Service)
services:
  ft-configs-service:
    image: hub.friendly-tech.com/configs/ft-configs-service:latest
    container_name: ft-configs-service
    # The database and Hazelcast are external to this stack, so there is no
    # depends_on. They must be reachable before the service starts -- see
    # <<startup-dependencies>>.
    env_file:
      - .env
      - ./ft-configs-service/.env
    environment:
      # Maps the generic DB_* names from the shared .env onto the vendor-specific
      # names this service reads. With DB_PROFILE=oracle, replace the MYSQL_* lines
      # with: ORACLE_HOST: ${DB_HOST} / ORACLE_PORT: ${DB_PORT} / ORACLE_SERVICE: ${DB_SERVICE}
      SPRING_PROFILES_ACTIVE: ${DB_PROFILE}
      MYSQL_HOST: ${DB_HOST}
      MYSQL_PORT: ${DB_PORT}
      HZ_MEMBERS: ${HZ_MEMBERS}
      JAVA_OPTS: "${FT_CONFIGS_SERVICE_JAVA_RAM:--Xms512m -Xmx1g} -Duser.timezone=${TZ:-UTC}"
    ports:
      - "${FT_CONFIGS_SERVICE_HTTP_PORT:-8087}:8080"
      - "${FT_CONFIGS_SERVICE_HTTPS_PORT:-8447}:8443"
    volumes:
      - ./ft-configs-service/config:/etc/app
      - ./ft-configs-service/logs:/app/logs
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://127.0.0.1:8080/configs-service/actuator/health"]
      interval: 10s
      timeout: 5s
      retries: 50
    networks:
      - ft-network

networks:
  ft-network:
    driver: bridge

The default published ports are 8087 (HTTP) and 8447 (HTTPS) rather than 8080/8443, to avoid clashing with FT QoE Web (8086) when FT Configs Service runs alongside the rest of the platform on a shared host — see FT Configs Service Environment Variables. To change them, set FT_CONFIGS_SERVICE_HTTP_PORT / FT_CONFIGS_SERVICE_HTTPS_PORT in ft-configs-service/.env, or edit only the left side of each ports: mapping.

cd /usr/local/ft-system
docker login hub.friendly-tech.com
docker compose up -d ft-configs-service
docker compose ps
docker compose logs -f ft-configs-service

To run it as a single container without Compose (docker run has no equivalent of the compose environment: block, so the vendor-specific names must be passed explicitly with -e; use the ORACLE_* names with SPRING_PROFILES_ACTIVE=oracle):

cd /usr/local/ft-system/ft-configs-service

docker run -d \
  --name ft-configs-service \
  --env-file ../.env \
  --env-file .env \
  -e SPRING_PROFILES_ACTIVE=mysql \
  -e MYSQL_HOST=<db-host> \
  -e MYSQL_PORT=3306 \
  -e HZ_MEMBERS=<hazelcast-host>:5701 \
  -v $(pwd)/config:/etc/app \
  -v $(pwd)/logs:/app/logs \
  -p 8087:8080 \
  -p 8447:8443 \
  --add-host=host.docker.internal:host-gateway \
  --restart unless-stopped \
  hub.friendly-tech.com/configs/ft-configs-service:latest

Both layers are passed in order — the shared .env first, the per-service .env second, so per-service values win. For Oracle, change the database variables in the shared .env as described in Switching to Oracle; the command itself does not change. For HTTP-only deployment, omit -p 8447:8443.

7. Verification

After starting the container, run the checks below. The examples use the default published ports 8087 (HTTP) and 8447 (HTTPS).

7.1. Startup Log

# Container status
docker ps -f name=ft-configs-service
# Expected: container with status "Up" and port 0.0.0.0:8087->8080/tcp

# Application logs
docker logs ft-configs-service --tail 50

A successful startup ends with the StartupInfoLogger summary banner (timestamp, PID, version, build, and Java runtime vary; the active profile reflects your deployment, and the connector shows the in-container port 8080):

INFO --- [] c.f.f.utils.config.StartupInfoLogger :
============================================================
FT Configs Service startup summary
------------------------------------------------------------
Active profiles  : <active-profile>
Connectors       : http (:8080)
Context path     : /configs-service
Swagger UI       : /configs-service/swagger-ui/index.html
Version          : <version>
Build            : <build>
Java runtime     : <java-version>
PID              : <pid>
============================================================

7.2. Endpoint Checks

# 1. Health check
curl -s http://localhost:8087/configs-service/actuator/health
# Expected: {"status":"UP"}

# 2. HTTPS health check (when SERVER_SSL_ENABLED=true)
curl -ks https://localhost:8447/configs-service/actuator/health
# Expected: {"status":"UP"}

# 3. Swagger UI (open in browser)
# http://localhost:8087/configs-service/swagger-ui/index.html

/actuator/health and /actuator/info are the only Actuator endpoints reachable without authentication. /actuator/metrics and /actuator/prometheus are exposed but require a JWT — see HTTP Endpoints.

7.3. Database Connectivity

Liquibase applies the schema migrations during startup, so a container that reaches the state above has already connected to the database. To check it explicitly:

# Reachability from inside the container (MySQL; use 1521 for Oracle)
docker exec ft-configs-service nc -zv <db-host> 3306

# Liquibase applied the changelog
docker logs ft-configs-service | grep -i liquibase

The Liquibase tracking tables (DATABASECHANGELOG, DATABASECHANGELOGLOCK) and the application tables must exist in the configs schema created in Create the Database Schema and User.

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. Both published ports are configurable through the FT_CONFIGS_SERVICE_HTTP_PORT / FT_CONFIGS_SERVICE_HTTPS_PORT variables shown in Docker Compose.

8.1. HTTP / HTTPS

Port Protocol Purpose Exposure

8087 → 8080

HTTP

REST API, Actuator, Swagger UI under the context path /configs-service. Published port set by FT_CONFIGS_SERVICE_HTTP_PORT.

Published (API clients, monitoring)

8447 → 8443

HTTPS

TLS connector, active when SERVER_SSL_ENABLED=true. Published port set by FT_CONFIGS_SERVICE_HTTPS_PORT.

Published (API clients)

When HTTPS is enabled and keystore settings are provided, the application can run with two connectors: HTTPS on SERVER_PORT (typically 8443) and HTTP on SERVER_HTTP_PORT (typically 8080). Publishing both Docker mappings is safe.

8.2. Outbound Connections

Ports FT Configs Service 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 the shared .env (DB_HOST, DB_PORT) and in ft-configs-service/.env.

Destination Port Protocol Purpose

Relational database

3306 (MySQL), 1521 (Oracle)

TCP

JDBC connection; addressed through MYSQL_HOST/MYSQL_PORT or ORACLE_HOST/ORACLE_PORT

Hazelcast cluster

5701

TCP

Publishes configuration snapshots; addressed through HZ_MEMBERS

8.3. HTTP Endpoints

Paths include the /configs-service context path. Auth follows the access rules declared in application.yml (ft-configs.security.endpoints): every path not listed as public requires a JWT.

Method Path Purpose Auth

GET

/configs-service/actuator/health

Health status; used by the container healthcheck

None

GET

/configs-service/actuator/info

Build and version information

None

GET

/configs-service/actuator/metrics

Micrometer metrics

JWT

GET

/configs-service/actuator/prometheus

Prometheus scrape endpoint

JWT

GET

/configs-service/swagger-ui/index.html

Swagger UI

None

GET

/configs-service/v3/api-docs

OpenAPI document

None

POST

/configs-service/auth/login

Obtains a JWT

None

POST

/configs-service/auth/first-login

Rotates the bootstrap admin password on first login

None

9. Stack Management

9.1. Logs

Command Description

docker compose logs -f ft-configs-service

Follow application logs

docker logs ft-configs-service --tail 200

Show the last 200 log lines

ls -la /usr/local/ft-system/ft-configs-service/logs

File logs written to the mounted /app/logs directory

9.2. Start, Stop, Restart

Command Description

docker compose up -d ft-configs-service

Start the service in the background

docker compose down

Stop and remove the container

docker compose restart ft-configs-service

Restart the application

docker compose ps

Show container status

9.3. Shell Access

Command Description

docker exec -it ft-configs-service sh

Enter the application container

docker exec ft-configs-service ls -la /etc/app

Check mounted configuration files

docker inspect ft-configs-service --format '{{json .Config.Env}}'

Check environment variables

9.4. Updating FT Configs Service

docker compose pull downloads the latest image, then docker compose up -d recreates the container with the new version. Configuration files and logs are preserved.

# 1. Backup configuration (run from the stack root, where compose.yml lives)
cd /usr/local/ft-system
tar -czf ft-configs-backup-$(date +%Y%m%d).tar.gz \
  compose.yml .env ft-configs-service/.env ft-configs-service/config/

# 2. Pull the latest image and recreate the container
docker compose pull ft-configs-service
docker compose up -d ft-configs-service

# 3. Verify
curl -s http://localhost:8087/configs-service/actuator/health

10. Production Checklist

  • Database credentials: default values in the .env file replaced (MYSQL_PASSWORD / ORACLE_PASSWORD).

  • JWT secret: empty JWT_SECRET replaced with a secure value (openssl rand -base64 32).

  • Bootstrap admin password rotated after the first login (/auth/first-login) and removed from .env.

  • CORS origins: CORS_ALLOWED_ORIGINS set to the host users type in the browser, listing both the HTTP and HTTPS origins — http://<ui-host>:3001,https://<ui-host>:3443 with <ui-host> replaced by the real host.

  • Hazelcast cluster: HZ_MEMBERS points at the same cluster FTACS uses — a mismatch is silent (no connection error) and leaves FTACS with no configuration.

  • File permissions: chmod 600 .env ft-configs-service/.env (run from /usr/local/ft-system).

  • Log management: /app/logs mounted, external log rotation configured (for example logrotate).

  • Resource limits: deploy.resources.limits added in compose.yml.

  • Restart policy: restart: unless-stopped present in compose.yml.

11. Troubleshooting

11.1. Env File Not Found

Symptom: Docker fails to start with open .env: no such file or directory.

Fix:

  1. Confirm both layers exist: ls -la /usr/local/ft-system/.env /usr/local/ft-system/ft-configs-service/.env.

  2. Run docker run from the directory containing the env file, or use an absolute path in --env-file.

  3. In compose.yml, verify both env_file entries (.env and ./ft-configs-service/.env) are correct relative to the compose file location — docker compose must be run from /usr/local/ft-system.

11.2. Database Connection Failure (MySQL)

Symptom: Communications link failure or Access denied in logs.

Fix:

  1. Test connectivity from inside the container: docker exec ft-configs-service nc -zv <db-host> 3306.

  2. Check MYSQL_HOST, MYSQL_PORT, MYSQL_USER, and MYSQL_PASSWORD against the schema and user created in Create the Database Schema and User.

  3. If the database runs on the host machine, use host.docker.internal instead of localhost.

11.3. Database Connection Failure (Oracle)

Symptom: ORA-12514: Cannot connect to Oracle or ORA-01017: invalid username/password in logs.

Fix:

  1. Test connectivity from inside the container: docker exec ft-configs-service nc -zv <db-host> 1521.

  2. Verify DB_HOST, DB_PORT and DB_SERVICE in the shared .env and ORACLE_USER / ORACLE_PASSWORD in ft-configs-service/.env. Confirm DB_SERVICE=XEPDB1 matches exactly (case-sensitive).

  3. If the database runs on the host machine, use host.docker.internal instead of localhost.

11.4. Hazelcast Connection Failure

Symptom: Unable to connect to any address in logs.

Fix:

  1. Verify HZ_MEMBERS is set and points to reachable Hazelcast members (e.g., HZ_MEMBERS=hazelcast-host:5701).

  2. Confirm Hazelcast members are running and on the same Docker network, or reachable across hosts.

  3. Check that CACHE_CONFIG_PATH is correct — the default classpath: loads the JAR-bundled hazelcast-client.yaml; only set a file: path when externalizing the config to a mounted volume.

11.5. FTACS Does Not Receive Published Configuration

Symptom: FT Configs Service is healthy and its UI shows the configuration as saved, but FTACS starts with configuration falling back to defaults and no error appears in either log.

Cause: FT Configs Service and FTACS are Hazelcast clients of different clusters — this failure is silent by design, since Hazelcast never reports "no such publisher."

Fix:

  1. Compare HZ_MEMBERS on both services — both must resolve to the same cluster.

  2. Compare the Hazelcast cluster name FT Configs Service uses with the one in FTACS’s hazelcast-client.xml (default: dev).

  3. Confirm the configuration was actually saved through FT Configs UI, not left in draft.

11.6. Port Already in Use

Symptom: address already in use error.

Fix:

  1. Find the process holding the port: lsof -i :8087 or docker ps.

  2. Stop the conflicting service, or expose a different host port via FT_CONFIGS_SERVICE_HTTP_PORT in ft-configs-service/.env.

11.7. Container Restarting or Healthcheck Failing

Symptom: docker ps shows the container in a restart loop or health status is unhealthy.

Fix:

  1. Review the logs for startup errors: docker logs ft-configs-service --tail 200.

  2. The healthcheck allows 50 retries at a 10s interval before Compose reports it unhealthy — a slow database connection can still be the cause even after that window.

  3. Verify the health endpoint responds directly: http://localhost:8087/configs-service/actuator/health.

11.8. Wrong Profile Selected

Symptom: The application tries to connect to the wrong database type (e.g., MySQL errors when using Oracle).

Fix:

  1. Check the active profile: docker exec ft-configs-service env | grep SPRING_PROFILES_ACTIVE.

  2. Verify DB_PROFILE (and SPRING_PROFILES_ACTIVE) is mysql or oracle in the shared .env — compose.yml injects it into the container. See Switching to Oracle.

11.9. Liquibase Migration Failure

Symptom: LiquibaseException or Migration failed in application logs during startup.

Fix:

  1. Check database connectivity and credentials — see Database Connectivity.

  2. Ensure the database user has sufficient privileges to create/alter tables — see Create the Database Schema and User.

  3. If upgrading from a previous version, check the schema is not corrupted; restore from backup if necessary.

11.10. Bootstrap Admin Not Created

Symptom: Cannot log in after first startup. No admin account exists.

Cause: AdminBootstrap runs on every startup, but only creates the account when the configs schema contains no user with the ADMIN role and both FT_CONFIGS_BOOTSTRAP_ADMIN_USERNAME and FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORD are non-empty. With either value missing it logs Admin bootstrap skipped and continues.

Fix:

  1. Check docker exec ft-configs-service env | grep FT_CONFIGS_BOOTSTRAP — both the username and the password must be non-empty (password: minimum 8 characters).

  2. Confirm the skip in the log: docker logs ft-configs-service | grep -i "admin bootstrap".

  3. Set the missing values in ft-configs-service/.env and recreate the container. No database wipe is needed — the bootstrap retries on the next startup, and the database is external to this stack, so docker compose down -v would delete nothing:

    cd /usr/local/ft-system
    docker compose up -d --force-recreate ft-configs-service
    docker compose logs -f ft-configs-service | grep -i "bootstrap administrator"

    If an ADMIN user does already exist in the configs schema, the bootstrap stays skipped by design — recover that account instead of expecting a new one.

11.11. Getting Support

When an issue is not covered above, collect the following before contacting the FT Configs Service maintainers:

  • docker logs ft-configs-service --tail 500 and the file logs from the mounted logs/ directory.

  • docker inspect ft-configs-service --format '{{json .Config.Env}}', with secrets removed.

  • The active profile, the image tag, and the output of curl -s http://localhost:8087/configs-service/actuator/health.

For issues outside deployment, see the Troubleshooting Guide.