Installation & Deployment
Overview
This guide walks you through installing and running ft-configs-service — a Spring Boot 4 application built with Java 25 and Gradle. Docker is the recommended deployment method for consistency and ease of use.
The deployment includes:
-
ft-configs-service — Spring Boot REST service for telecom/network device configuration management
-
Docker container — Pre-built image from Harbor registry or loaded from archive
-
Database — MySQL 8.4 or Oracle (schema and user created manually — see 3. Create the Database Schema and User; tables auto-created by Liquibase on first startup)
-
Hazelcast — Distributed cache for runtime consumers (optional, but recommended)
Prerequisites
System Requirements
-
Docker Engine 20.10+
-
Docker Compose 2.0+
-
Minimum 2 GB RAM (4+ GB recommended)
-
1 GB free disk space for the image and logs
|
JDK 25 is only required for local Gradle builds. Docker deployment does not need a JDK on the host machine. |
Required External Components
The following components must be installed and accessible:
| Component | Purpose | Default Port |
|---|---|---|
MySQL or Oracle |
Configuration database (schema and user provisioned manually — see 3. Create the Database Schema and User; tables created by Liquibase migrations) |
3306 (MySQL) / 1521 (Oracle) |
Hazelcast |
Distributed cache for runtime consumers (ACS, northbound-api, angular backend) |
5701 |
Supported Operating Systems
-
Linux (recommended)
-
macOS
-
Windows with WSL2
Before starting, make sure:
-
Docker Engine 20.10+ and Docker Compose 2.0+ installed. If Docker is not installed, follow the Docker Installation Guide.
-
You have network access to the database (MySQL or Oracle).
-
You have network access to the Hazelcast cluster (if caching is used).
Quick Start
For experienced users who already have Docker installed and configuration files ready.
The example below uses MySQL; for Oracle replace .env.mysql with .env.oracle.
# 1. Go to the working directory (files should already be in place -- see <<Preparation>>)
cd /usr/local/ft-configs-service
# 2. Get the Docker image (choose one):
# Option A: Pull from Harbor registry (recommended)
docker login hub.friendly-tech.com
# Read-only pull credentials:
# Username: readonly
# Password: fokxuw-fymte1-taSxyc
# Note: "docker pull" is optional if you use docker compose --
# "docker compose up" will pull the image automatically.
docker pull hub.friendly-tech.com/configs/ft-configs-service:latest
# Option B: Load from archive (for offline servers — see "Transfer Image to Offline Server" below)
# gzip -dc ft-configs-service-<version>.tar.gz | docker load
# 3. Edit the environment file for your database
vi .env.mysql # or .env.oracle for Oracle
# set MYSQL_HOST, MYSQL_PASSWORD, JWT_SECRET,
# FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORD, etc.
# 4. Make sure compose.yml references the correct env file
# env_file: .env.mysql (default) or .env.oracle
# 5. Start the application
docker compose up -d
# 6. Verify
curl -s http://localhost:8080/configs-service/actuator/health
|
On first startup a bootstrap admin account is auto-created from the |
|
To switch between MySQL and Oracle, change the
|
After startup the application is available at http://localhost:8080/configs-service.
Preparation
1. Prepare Working Directory
Create the working directory on the host machine:
mkdir -p /usr/local/ft-configs-service/{configs,logs}
cd /usr/local/ft-configs-service
# 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-configs-service/logs
|
The container runs as the non-root user |
Download the deployment files from the FT_DISK on SharePoint and place them on the server. Alternatively, copy the files from the source repository.
Expected Layout
/usr/local/ft-configs-service/
+-- .env.mysql # Environment variables (MySQL profile)
+-- .env.oracle # Environment variables (Oracle profile)
+-- compose.yml # Docker Compose file (create from the example in the Deployment section below)
+-- configs/ # Mounted to /etc/app inside container
| +-- keystore.p12 # TLS keystore (only when HTTPS enabled)
+-- logs/ # Application logs (mounted volume)
|
You only need the env file that matches your database ( Configuration files are also available in the source repository:
|
2. Get the Docker Image
Option A: Pull from Harbor Registry (recommended)
# Authenticate with the registry.
# Read-only pull credentials:
# Username: readonly
# Password: fokxuw-fymte1-taSxyc
docker login hub.friendly-tech.com
# Pull the latest image (optional if using docker compose --
# "docker compose up" will pull the image automatically)
docker pull hub.friendly-tech.com/configs/ft-configs-service:latest
To pin to a specific release version:
docker pull hub.friendly-tech.com/configs/ft-configs-service:v1.0.0-b0.0.1
|
The version |
Optionally, retag the image for shorter references in docker run commands:
docker tag hub.friendly-tech.com/configs/ft-configs-service:latest ft-configs-service:latest
If you skip retagging, use the full image name (hub.friendly-tech.com/configs/ft-configs-service:latest) in all subsequent commands.
The readonly account above provides pull-only access and is sufficient for installation and upgrades.
To request elevated Harbor access (for example, push rights or a dedicated robot token), contact the DevOps team.
Option B: Transfer Image to Offline Server
Use this option when the target server has no internet access and cannot pull images from Harbor directly. The image is pulled on a machine that does have Harbor access, exported to a tar archive, transferred to the offline server, and loaded there.
-
On a machine with Harbor access, log in:
# Read-only pull credentials: # Username: readonly # Password: fokxuw-fymte1-taSxyc docker login hub.friendly-tech.com -
Pull the image for the target server’s architecture:
docker pull --platform linux/amd64 hub.friendly-tech.com/configs/ft-configs-service:<version>An explicit
--platformmatching the offline target server’s architecture is required. The image in Harbor is multi-arch (linux/amd64,linux/arm64); without--platform,docker pullselects the host architecture, which may not match the target. For example, on an Apple Silicon (arm64) Mac without--platform, the resulting archive will be arm64 and will fail with aplatform does not matchwarning on amd64 servers. The example useslinux/amd64; replace it with the platform of your offline target server (linux/arm64, etc.). -
Save the image to a tar archive and compress it:
docker save hub.friendly-tech.com/configs/ft-configs-service:<version> -o ft-configs-service-<version>.tar gzip ft-configs-service-<version>.tar -
Transfer
ft-configs-service-<version>.tar.gzto the offline server (e.g., viascpor removable media). -
On the offline server, load the image:
gzip -dc ft-configs-service-<version>.tar.gz | docker load
Option C: Build from Source (developers only)
# Build the Docker image (multi-stage Dockerfile builds the JAR inside the container)
docker build -t ft-configs-service:latest .
|
Building from source requires network access to Maven Central and GitHub Packages (private dependencies). Loading a pre-built image is faster and avoids build-environment issues.
|
After loading or pulling, verify the image is available:
docker images | grep ft-configs-service
3. Create the Database Schema and User
ft-configs-service does not create the database, schema, or database 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. You must provision the schema and user before the first startup.
The credentials you create here must match the values set in .env.mysql / .env.oracle (MYSQL_SCHEMA/MYSQL_USER/MYSQL_PASSWORD or ORACLE_USER/ORACLE_PASSWORD) in the next step.
|
The passwords in the snippets below ( |
- MySQL
-
Connect as a privileged user (e.g.
root) and create theconfigsschema and theftacs_configsuser:-- ft-configs-service schema and 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;Run it, for example, with the MySQL client:
mysql -h <mysql-host> -u root -p < create-configs-db.sqlThe shared QA provisioning script creates several service databases ( flowable,ftacs_quartz,ftacs_qoe_ui,configs) in one file. Only theconfigsschema and theftacs_configsuser shown above are required by ft-configs-service. - Oracle
-
Connect as a privileged user (e.g.
SYS/SYSTEM) and create the tablespace and theconfigsuser:-- ft-configs-service tablespace and 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;&1is an SQL*Plus substitution variable for the datafile directory inside the database’s persisted volume. Pass it as the first script argument, or replace it with an absolute path:# Pass the datafile directory as the first argument sqlplus sys/<password>@//<oracle-host>:1521/XEPDB1 as sysdba @create-configs-db.sql /opt/oracle/oradata/XE/XEPDB1This user (
configs) and its password must matchORACLE_USER/ORACLE_PASSWORDin.env.oracle. The connection uses theXEPDB1pluggable-database service (ORACLE_SERVICE=XEPDB1).
4. Configure Environment
The ft-configs-service reads its configuration from environment variables loaded via Docker --env-file.
Choose the appropriate environment file:
- MySQL
-
cd /usr/local/ft-configs-service vi .env.mysql# ============================================================ # Spring Profile # ============================================================ SPRING_PROFILES_ACTIVE=mysql # ============================================================ # Server Ports # ============================================================ SERVER_PORT=8080 SERVER_HTTP_PORT=8080 # ============================================================ # HTTPS / TLS Settings # # Dual-mode setup: SERVER_SSL_ENABLED=true enables HTTPS on SERVER_PORT. # For dual-mode (HTTP + HTTPS simultaneously), set: # SERVER_PORT=8443 <- HTTPS primary # SERVER_HTTP_PORT=8080 <- plain HTTP secondary # SERVER_PORT and SERVER_HTTP_PORT MUST differ in dual mode. # # Keystore: Place keystore.p12 in ./configs/ on the host # (mapped to /etc/app/ inside the container via volume mount). # ============================================================ # Set to true to enable HTTPS (default: false = plain HTTP only) SERVER_SSL_ENABLED=false # Place keystore.p12 in ./configs/ on the host (mapped to /etc/app/ inside the container) SERVER_SSL_KEY_STORE=file:/etc/app/keystore.p12 # Replace when HTTPS is enabled SERVER_SSL_KEY_STORE_PASSWORD= SERVER_SSL_KEY_STORE_TYPE=PKCS12 # Replace when HTTPS is enabled SERVER_SSL_KEY_PASSWORD= SERVER_SSL_KEY_ALIAS=server SERVER_SSL_TRUST_STORE= SERVER_SSL_TRUST_STORE_PASSWORD= # ============================================================ # Timezone # ============================================================ TZ=UTC # ============================================================ # Database -- MySQL # ============================================================ DB_MAX_POOL_SIZE=10 DB_MIN_IDLE=5 DB_CONNECTION_TIMEOUT_MS=30000 # Replace with your MySQL host if needed MYSQL_HOST=mysql MYSQL_PORT=3306 MYSQL_SCHEMA=configs MYSQL_USER=ftacs_configs MYSQL_PASSWORD=ftacs_configs MYSQL_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver # ============================================================ # JWT Authentication # ============================================================ # REQUIRED. Base64-encoded HMAC-SHA256 signing key for access/refresh tokens. # Must decode to at least 32 bytes (256-bit); startup fails otherwise. # Generate a fresh, unique value per environment: openssl rand -base64 32 JWT_SECRET= # Access token lifetime in milliseconds (86400000 = 24h) JWT_EXPIRATION=86400000 # Refresh token lifetime in milliseconds (604800000 = 7 days) JWT_REFRESH_TOKEN_EXPIRATION=604800000 # ============================================================ # Cookie # ============================================================ # Mark auth cookies as Secure (sent only over HTTPS). Set true whenever the UI is # served over HTTPS (directly or behind a TLS-terminating proxy); keep false for plain-HTTP dev. COOKIE_SECURE=false # Cookie Domain attribute. Leave blank to scope cookies to the request host. # Set a parent domain (e.g. .example.com) only to share cookies across subdomains. COOKIE_DOMAIN= # ============================================================ # CORS # ============================================================ # Comma-separated list of allowed origins for cross-origin requests. # Must match the URL the user sees in the browser address bar (scheme + host + port); # scheme-sensitive -- http:// and https:// are different origins. # Without proper CORS origins, the browser blocks API requests with "Invalid CORS request". # List every origin the UI is reached on. The UI is served on both HTTP and HTTPS # (defaults: 3001 for HTTP, 3443 for HTTPS, both published by Docker) -- include both, # not only http://. # This is the host the user types in the browser address bar: "localhost" only when # browsing locally, otherwise the public hostname (e.g. https://management.example.com). # NEVER the internal Docker service name -- "http://ft-configs-ui:3001" is wrong here, # the browser never sends it as an Origin (that name belongs in the UI's BACKEND_URL, not CORS). # Replace <ui-host> with that hostname (e.g. localhost for local browsing, or your FQDN). CORS_ALLOWED_ORIGINS=http://<ui-host>:3001,https://<ui-host>:3443 # ============================================================ # Mail (password reset, registration, deletion OTP) # # MAIL_MODE is the PRIMARY control for email delivery: # AUTO (default) -- infer from SMTP config: OFFLINE when MAIL_HOST, # MAIL_USERNAME or MAIL_PASSWORD are not all set. When SMTP # is configured but the server is unreachable, delivery # degrades to OFFLINE behaviour at runtime (see below). # OFFLINE -- never send email. # # OFFLINE behaviour (chosen explicitly, inferred, or reached at runtime when the # configured SMTP server is unreachable) degrades gracefully instead of failing: # * Create user / reset password: the temporary password is RETURNED in the # API response (passwordDelivery=RETURNED) for the admin to hand over # out-of-band, instead of being emailed. # * User deletion: OTP email confirmation is disabled. The request returns # otpRequired=false (no token); the account is deleted in the confirm step, # called without an OTP. # # RUNTIME FALLBACK: under AUTO with SMTP configured, if the server is unreachable # or a send fails, both password delivery and user deletion degrade to the # OFFLINE behaviour above instead of erroring. SMTP connect/read/write timeout is # fixed at 5s for a prompt fallback. # # With the defaults below (empty MAIL_USERNAME/MAIL_PASSWORD) AUTO resolves to # OFFLINE out of the box -- intended for air-gapped customer installs. # ============================================================ # Delivery mode: AUTO | OFFLINE MAIL_MODE=AUTO # SMTP server hostname (example -- replace with your SMTP host) MAIL_HOST=smtp.example.com # SMTP server port (587 for STARTTLS, 465 for SSL) MAIL_PORT=587 # SMTP auth username, usually the sender address (under AUTO, blank => offline) MAIL_USERNAME= # SMTP auth password (under AUTO, blank => offline) MAIL_PASSWORD= # Mail transport protocol (smtp or smtps) MAIL_PROTOCOL=smtp # Enable SMTP authentication; true/false MAIL_SMTP_AUTH=true # Enable STARTTLS encryption for the SMTP connection; true/false MAIL_SMTP_STARTTLS=true # Sender address shown in the From header of outgoing messages (example -- replace with yours) MAIL_FROM=no-reply@example.com # URL for the "Sign in" button in registration emails. Full absolute URL with # http:// or https:// scheme. Blank/non-HTTP => the button is replaced by a text hint. MAIL_LOGIN_URL= # ============================================================ # Bootstrap Admin # ============================================================ FT_CONFIGS_BOOTSTRAP_ADMIN_USERNAME=admin # REQUIRED on first startup. Rotate immediately after calling /auth/first-login FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORD= # Optional admin email FT_CONFIGS_BOOTSTRAP_ADMIN_EMAIL= FT_CONFIGS_BOOTSTRAP_ADMIN_LOCALE= # ============================================================ # Hazelcast # ============================================================ # Path to Hazelcast client configuration; classpath: uses the JAR-bundled default CACHE_CONFIG_PATH=classpath: # Uncomment and set when connecting to an external Hazelcast cluster: # HZ_MEMBERS=hazelcast-host:5701 # ============================================================ # ACS Integration # ============================================================ # Hazelcast cache name from which ACS publishes product-class groups, used by the # dataStoreExclusionModels dropdown. Must match the name ACS publishes to. # Leave commented to use the built-in default (qoeProductClassGroupCache). # FT_CONFIGS_ACS_EXCLUSION_MODELS_CACHE_NAME=qoeProductClassGroupCache # ============================================================ # Audit # ============================================================ # Days to retain audit events (auto-purge at 03:00 daily). Min 3, max 90; 0 = keep forever. AUDIT_RETENTION_DAYS=30 # Extra sensitive field names to redact in audit snapshots (comma-separated), # added on top of the built-in defaults (password, token, secret, apiKey, ...). # AUDIT_REDACTION_FIELDS= # ============================================================ # Logging # ============================================================ LOGGING_LEVEL_COM_FRIENDLY_FTCONFIGSSERVICE=INFOWith MAIL_MODE=AUTO, blank SMTP credentials (MAIL_HOST/MAIL_USERNAME/MAIL_PASSWORDnot all set) select offline mode: temporary passwords are returned in the create-user/reset-password API responses instead of being emailed, and account deletion uses a plain confirmation instead of OTP (the account is still deleted in the confirm step). SetMAIL_MODE=OFFLINEto force this for air-gapped installs, or provide real, reachable SMTP credentials for email delivery (a configured-but-unreachable server underAUTOdegrades to the same offline behaviour instead of failing). - Oracle
-
cd /usr/local/ft-configs-service vi .env.oracle# ============================================================ # Spring Profile # ============================================================ SPRING_PROFILES_ACTIVE=oracle # ============================================================ # Server Ports # ============================================================ SERVER_PORT=8080 SERVER_HTTP_PORT=8080 # ============================================================ # HTTPS / TLS Settings # # Dual-mode setup: SERVER_SSL_ENABLED=true enables HTTPS on SERVER_PORT. # For dual-mode (HTTP + HTTPS simultaneously), set: # SERVER_PORT=8443 <- HTTPS primary # SERVER_HTTP_PORT=8080 <- plain HTTP secondary # SERVER_PORT and SERVER_HTTP_PORT MUST differ in dual mode. # # Keystore: Place keystore.p12 in ./configs/ on the host # (mapped to /etc/app/ inside the container via volume mount). # ============================================================ # Set to true to enable HTTPS (default: false = plain HTTP only) SERVER_SSL_ENABLED=false # Place keystore.p12 in ./configs/ on the host (mapped to /etc/app/ inside the container) SERVER_SSL_KEY_STORE=file:/etc/app/keystore.p12 # Replace when HTTPS is enabled SERVER_SSL_KEY_STORE_PASSWORD= SERVER_SSL_KEY_STORE_TYPE=PKCS12 # Replace when HTTPS is enabled SERVER_SSL_KEY_PASSWORD= SERVER_SSL_KEY_ALIAS=server SERVER_SSL_TRUST_STORE= SERVER_SSL_TRUST_STORE_PASSWORD= # ============================================================ # Timezone # ============================================================ TZ=UTC # ============================================================ # Database -- Oracle # ============================================================ DB_MAX_POOL_SIZE=10 DB_MIN_IDLE=5 DB_CONNECTION_TIMEOUT_MS=30000 # Replace with your Oracle host ORACLE_HOST=oracle ORACLE_PORT=1521 ORACLE_SERVICE=XEPDB1 ORACLE_USER=configs # Replace with a secure password ORACLE_PASSWORD=configs ORACLE_DRIVER_CLASS_NAME=oracle.jdbc.OracleDriver # Optional Hibernate default schema. Leave blank to use the connection user's schema. # ORACLE_SCHEMA= # ============================================================ # JWT Authentication # ============================================================ # REQUIRED. Base64-encoded HMAC-SHA256 signing key for access/refresh tokens. # Must decode to at least 32 bytes (256-bit); startup fails otherwise. # Generate a fresh, unique value per environment: openssl rand -base64 32 JWT_SECRET= # Access token lifetime in milliseconds (86400000 = 24h) JWT_EXPIRATION=86400000 # Refresh token lifetime in milliseconds (604800000 = 7 days) JWT_REFRESH_TOKEN_EXPIRATION=604800000 # ============================================================ # Cookie # ============================================================ # Mark auth cookies as Secure (sent only over HTTPS). Set true whenever the UI is # served over HTTPS (directly or behind a TLS-terminating proxy); keep false for plain-HTTP dev. COOKIE_SECURE=false # Cookie Domain attribute. Leave blank to scope cookies to the request host. # Set a parent domain (e.g. .example.com) only to share cookies across subdomains. COOKIE_DOMAIN= # ============================================================ # CORS # ============================================================ # Comma-separated list of allowed origins for cross-origin requests. # Must match the URL the user sees in the browser address bar (scheme + host + port); # scheme-sensitive -- http:// and https:// are different origins. # Without proper CORS origins, the browser blocks API requests with "Invalid CORS request". # List every origin the UI is reached on. The UI is served on both HTTP and HTTPS # (defaults: 3001 for HTTP, 3443 for HTTPS, both published by Docker) -- include both, # not only http://. # This is the host the user types in the browser address bar: "localhost" only when # browsing locally, otherwise the public hostname (e.g. https://management.example.com). # NEVER the internal Docker service name -- "http://ft-configs-ui:3001" is wrong here, # the browser never sends it as an Origin (that name belongs in the UI's BACKEND_URL, not CORS). # Replace <ui-host> with that hostname (e.g. localhost for local browsing, or your FQDN). CORS_ALLOWED_ORIGINS=http://<ui-host>:3001,https://<ui-host>:3443 # ============================================================ # Mail (password reset, registration, deletion OTP) # # MAIL_MODE is the PRIMARY control for email delivery: # AUTO (default) -- infer from SMTP config: OFFLINE when MAIL_HOST, # MAIL_USERNAME or MAIL_PASSWORD are not all set. When SMTP # is configured but the server is unreachable, delivery # degrades to OFFLINE behaviour at runtime (see below). # OFFLINE -- never send email. # # OFFLINE behaviour (chosen explicitly, inferred, or reached at runtime when the # configured SMTP server is unreachable) degrades gracefully instead of failing: # * Create user / reset password: the temporary password is RETURNED in the # API response (passwordDelivery=RETURNED) for the admin to hand over # out-of-band, instead of being emailed. # * User deletion: OTP email confirmation is disabled. The request returns # otpRequired=false (no token); the account is deleted in the confirm step, # called without an OTP. # # RUNTIME FALLBACK: under AUTO with SMTP configured, if the server is unreachable # or a send fails, both password delivery and user deletion degrade to the # OFFLINE behaviour above instead of erroring. SMTP connect/read/write timeout is # fixed at 5s for a prompt fallback. # # With the defaults below (empty MAIL_USERNAME/MAIL_PASSWORD) AUTO resolves to # OFFLINE out of the box -- intended for air-gapped customer installs. # ============================================================ # Delivery mode: AUTO | OFFLINE MAIL_MODE=AUTO # SMTP server hostname (example -- replace with your SMTP host) MAIL_HOST=smtp.example.com # SMTP server port (587 for STARTTLS, 465 for SSL) MAIL_PORT=587 # SMTP auth username, usually the sender address (under AUTO, blank => offline) MAIL_USERNAME= # SMTP auth password (under AUTO, blank => offline) MAIL_PASSWORD= # Mail transport protocol (smtp or smtps) MAIL_PROTOCOL=smtp # Enable SMTP authentication; true/false MAIL_SMTP_AUTH=true # Enable STARTTLS encryption for the SMTP connection; true/false MAIL_SMTP_STARTTLS=true # Sender address shown in the From header of outgoing messages (example -- replace with yours) MAIL_FROM=no-reply@example.com # URL for the "Sign in" button in registration emails. Full absolute URL with # http:// or https:// scheme. Blank/non-HTTP => the button is replaced by a text hint. MAIL_LOGIN_URL= # ============================================================ # Bootstrap Admin # ============================================================ FT_CONFIGS_BOOTSTRAP_ADMIN_USERNAME=admin # REQUIRED on first startup. Rotate immediately after calling /auth/first-login FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORD= # Optional admin email FT_CONFIGS_BOOTSTRAP_ADMIN_EMAIL= FT_CONFIGS_BOOTSTRAP_ADMIN_LOCALE= # ============================================================ # Hazelcast # ============================================================ # Path to Hazelcast client configuration; classpath: uses the JAR-bundled default CACHE_CONFIG_PATH=classpath: # Uncomment and set when connecting to an external Hazelcast cluster: # HZ_MEMBERS=hazelcast-host:5701 # ============================================================ # ACS Integration # ============================================================ # Hazelcast cache name from which ACS publishes product-class groups, used by the # dataStoreExclusionModels dropdown. Must match the name ACS publishes to. # Leave commented to use the built-in default (qoeProductClassGroupCache). # FT_CONFIGS_ACS_EXCLUSION_MODELS_CACHE_NAME=qoeProductClassGroupCache # ============================================================ # Audit # ============================================================ # Days to retain audit events (auto-purge at 03:00 daily). Min 3, max 90; 0 = keep forever. AUDIT_RETENTION_DAYS=30 # Extra sensitive field names to redact in audit snapshots (comma-separated), # added on top of the built-in defaults (password, token, secret, apiKey, ...). # AUDIT_REDACTION_FIELDS= # ============================================================ # Logging # ============================================================ LOGGING_LEVEL_COM_FRIENDLY_FTCONFIGSSERVICE=INFO
With MAIL_MODE=AUTO, blank SMTP credentials (MAIL_HOST/MAIL_USERNAME/MAIL_PASSWORD not all set) select offline mode: temporary passwords are returned in the create-user/reset-password API responses instead of being emailed, and account deletion uses a plain confirmation instead of OTP (the account is still deleted in the confirm step).
Set MAIL_MODE=OFFLINE to force this for air-gapped installs, or provide real, reachable SMTP credentials for email delivery (a configured-but-unreachable server under AUTO degrades to the same offline behaviour instead of failing).
|
|
If the database or Hazelcast cluster runs on the host machine (not in Docker), use one of the following as the hostname in
|
|
Pre-configured template files are available on FT_DISK and in the source repository under Copy the file that matches your database to the working directory:
|
TLS Keystore (HTTPS)
To enable HTTPS, provide a PKCS12 keystore file.
Place keystore.p12 in the configs/ directory on the host (mounted to /etc/app/ inside the container).
For local development, you can generate a self-signed keystore:
cd /usr/local/ft-configs-service
keytool -genkeypair \
-alias server \
-keyalg RSA \
-keysize 2048 \
-storetype PKCS12 \
-keystore configs/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"
|
The Recommended values for the
Wildcard limitations:
If you generate the certificate for a single concrete host, replace the wildcard with that FQDN in both
|
Then set these values in .env.mysql or .env.oracle:
-
SERVER_SSL_ENABLED=true -
SERVER_PORT=8443(HTTPS primary port) -
SERVER_HTTP_PORT=8080(plain HTTP secondary port) -
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 (.p12).
|
Bootstrap Admin (first install)
On the very first startup, ft-configs-service creates a bootstrap administrator account using environment variables:
-
FT_CONFIGS_BOOTSTRAP_ADMIN_USERNAME— defaults toadmin -
FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORD— must be set or the account will not be created -
FT_CONFIGS_BOOTSTRAP_ADMIN_EMAIL— admin email address -
FT_CONFIGS_BOOTSTRAP_ADMIN_LOCALE— locale preference (optional)
|
After first startup, immediately change the bootstrap admin password by calling the |
Deployment
Option 1: Docker Compose (recommended)
Create a compose.yml file in your working directory:
services:
ft-configs-service:
image: hub.friendly-tech.com/configs/ft-configs-service:latest
container_name: ft-configs-service
env_file:
- .env.mysql # or .env.oracle
ports:
- "8080:8080" # <host-port>:<container-port> -- change the host port
# if 8080 is already used by another application (e.g. "9080:8080")
- "8443:8443" # Optional: publish this mapping only when HTTPS is enabled
volumes:
- ./configs:/etc/app
- ./logs:/app/logs
extra_hosts:
- "host.docker.internal:host-gateway"
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/configs-service/actuator/health"]
interval: 30s
timeout: 3s
start_period: 60s
retries: 3
To change external ports, edit only the left side of each mapping (<host-port>:<container-port>), for example 9080:8080 for HTTP and 9443:8443 for HTTPS.
Publishing both mappings at the same time is valid and does not conflict.
Conflicts happen only if host ports overlap (left side) or if HTTP/HTTPS are configured to the same container port.
For HTTP-only deployment, remove the 8443:8443 mapping.
# Start the application
docker compose up -d
# View logs
docker compose logs -f ft-configs-service
# Stop
docker compose down
|
To deploy ft-configs-service alongside other Friendly Tech Java services as a single stack, see the Java API Stack Deployment Guide. |
Option 2: docker run
cd /usr/local/ft-configs-service
docker run -d \
--name ft-configs-service \
--env-file .env.mysql \
-v $(pwd)/configs:/etc/app \
-v $(pwd)/logs:/app/logs \
-p 8080:8080 \
-p 8443:8443 \
--add-host=host.docker.internal:host-gateway \
--restart unless-stopped \
hub.friendly-tech.com/configs/ft-configs-service:latest
If default host ports are busy, change only the left side:
-p 9080:8080 -p 9443:8443.
For HTTP-only deployment, omit -p 8443:8443.
Replace --env-file .env.mysql with --env-file .env.oracle for Oracle.
If you retagged the image locally, use ft-configs-service:latest instead of the full Harbor path.
Docker Networking
If the ft-configs-service container must communicate with other containers (database, Hazelcast, etc.), create a shared Docker network:
docker network create ft-configs-net
Add --network ft-configs-net to your docker run command or add the network section to your compose.yml.
Use container names (not raw IPs) in .env.mysql / .env.oracle to leverage Docker DNS resolution.
Verify Deployment
After starting the container, run the following checks:
# 1. Check container status
docker ps -f name=ft-configs-service
# Expected: container with status "Up" and port 0.0.0.0:8080->8080/tcp
# 2. Health check
curl -s http://localhost:8080/configs-service/actuator/health
# Expected: {"status":"UP"}
# 2b. HTTPS health check (when SERVER_SSL_ENABLED=true)
curl -ks https://localhost:8443/configs-service/actuator/health
# Expected: {"status":"UP"}
# 3. Check application logs
docker logs ft-configs-service --tail 50
# Look for: "Started FtConfigsServiceApplication in XX.XXX seconds"
# 4. Prometheus metrics
curl -s http://localhost:8080/configs-service/actuator/prometheus | head -20
# Expected: lines starting with "# HELP" and "# TYPE" followed by metric values
# 5. Swagger UI (open in browser)
# http://localhost:8080/configs-service/swagger-ui/index.html
# https://localhost:8443/configs-service/swagger-ui/index.html (when HTTPS enabled)
Environment Variables
|
Do not commit secrets ( |
|
|
A) Quick start (most common knobs)
| Variable | Required | Default | Description |
|---|---|---|---|
|
Yes |
|
Active Spring profile: |
|
No |
|
Primary server port. When |
|
No |
|
Plain HTTP port. In dual-mode (HTTP + HTTPS), set this to a different value than |
|
No |
from Hazelcast client config |
Comma-separated Hazelcast members ( |
|
No |
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: |
|
|
No |
|
Log level for the application package. Values: |
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 |
|---|---|---|---|
|
No |
derived if unset |
Full MySQL JDBC URL. Recommended for Docker/Kubernetes to avoid host, port, and schema drift. |
|
No |
|
Shared DB host fallback used by MySQL and Oracle when profile-specific host is not set. |
|
No |
|
MySQL host used if |
|
No |
|
MySQL port used if |
|
No |
|
MySQL schema used if |
|
No |
|
MySQL username for the service. The env templates in |
|
No |
|
MySQL password for the service. The env templates in |
|
No |
|
Override JDBC driver class if needed. |
|
No |
|
HikariCP max pool size. |
|
No |
|
HikariCP minimum idle connections. |
|
No |
|
HikariCP connection timeout in milliseconds. |
When both *_JDBC_URL and decomposed variables are set, the explicit JDBC URL always takes precedence.
|
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 |
|---|---|---|---|
|
No |
derived if unset |
Full Oracle JDBC URL. Recommended for Docker/Kubernetes to avoid host, port, and service drift. |
|
No |
|
Oracle host used if |
|
No |
|
Oracle port used if |
|
No |
|
Oracle service name used if |
|
No |
empty |
Optional schema mapped to |
|
No |
|
Oracle username for the service. |
|
No |
|
Oracle password for the service. Treat as a secret. |
|
No |
|
Override Oracle JDBC driver class if needed. |
D) Hazelcast / cache
| Variable | Required | Default | Description |
|---|---|---|---|
|
No |
|
Base path for Hazelcast config files. |
|
No |
|
When true, starts an embedded Hazelcast member from |
|
No |
|
Name of the Hazelcast cache from which ACS publishes product-class groups, consumed by the |
E) Auth / JWT & cookies
| Variable | Required | Default | Description |
|---|---|---|---|
|
No |
defined in profile |
Base64-encoded HMAC-SHA256 signing key. Must decode to at least 32 bytes (256-bit) or startup fails. Generate with |
|
No |
|
Access token expiration in milliseconds. |
|
No |
|
Refresh token expiration in milliseconds. |
|
No |
|
Set |
|
No |
empty |
Optional domain for cross-subdomain cookie sharing. |
| Variable | Required | Default | Description |
|---|---|---|---|
|
No |
|
Email delivery mode (case-insensitive; blank or unknown falls back to |
|
No |
|
SMTP host used for registration and deletion flows. Under |
|
No |
|
SMTP port. |
|
No |
SMTP username. Under |
|
|
No |
SMTP password. Treat as a secret. Under |
|
|
No |
|
Usually |
|
No |
|
SMTP authentication toggle. |
|
No |
|
STARTTLS toggle. |
|
No |
|
From address used in sent emails. |
|
No |
empty |
URL for the "Sign In" call-to-action button in emails. Must start with |
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.
|
Audit
| Variable | Required | Default | Description |
|---|---|---|---|
|
No |
|
Number of days to retain audit events. Minimum 3, maximum 90. Set to |
|
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.). |
Bootstrap admin (first install)
| Variable | Required | Default | Description |
|---|---|---|---|
|
No |
|
Username for first startup when no admin exists. |
|
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 |
|
No |
empty |
Optional email assigned to bootstrap admin. |
|
No |
empty |
Optional locale for bootstrap admin. |
JVM / container runtime
| Variable | Required | Default | Description |
|---|---|---|---|
|
No |
set in Docker image |
JVM options passed by |
|
No |
|
Container timezone. |
For comprehensive details on every configuration option, see the Configuration Guide.
Container Management
| Command | Description |
|---|---|
|
Start all services in the background |
|
Stop and remove containers |
|
Follow application logs |
|
Show container status |
|
Restart the application |
|
Update images to latest versions |
|
Enter the application container |
|
Check mounted configuration files |
|
View last 100 log lines |
|
View resource usage |
|
Check environment variables |
Updating
Via Docker Compose (recommended)
docker compose pull downloads the latest image from the registry, then docker compose up -d recreates the container with the new version.
Configuration files and logs are preserved.
# 1. Backup configuration
cd /usr/local/ft-configs-service
tar -czf ft-configs-backup-$(date +%Y%m%d).tar.gz .env.* configs/
# 2. Pull the latest image
docker compose pull ft-configs-service
# 3. Recreate the container with the new image
docker compose up -d ft-configs-service
# 4. Verify
curl -s http://localhost:8080/configs-service/actuator/health
Via docker load (offline servers)
When the upgrade target server has no access to the Harbor registry, the new image must be pulled on a machine that does have Harbor access, exported to a tar archive, transferred to the offline server, and loaded there.
-
On a machine with Harbor access, log in:
# Read-only pull credentials: # Username: readonly # Password: fokxuw-fymte1-taSxyc docker login hub.friendly-tech.com -
Pull the new image for the target server’s architecture:
docker pull --platform linux/amd64 hub.friendly-tech.com/configs/ft-configs-service:<new-version>An explicit
--platformmatching the offline target server’s architecture is required. The image in Harbor is multi-arch (linux/amd64,linux/arm64); without--platform,docker pullselects the host architecture, which may not match the target. For example, on an Apple Silicon (arm64) Mac without--platform, the resulting archive will be arm64 and will fail with aplatform does not matchwarning on amd64 servers. The example useslinux/amd64; replace it with the platform of your offline target server (linux/arm64, etc.). -
Save the image to a tar archive and compress it:
docker save hub.friendly-tech.com/configs/ft-configs-service:<new-version> -o ft-configs-service-<new-version>.tar gzip ft-configs-service-<new-version>.tar -
Transfer
ft-configs-service-<new-version>.tar.gzto the offline server (e.g., viascpor removable media). -
On the offline server, load the new image and replace the running container:
# 1. Load the new image from archive gzip -dc ft-configs-service-<new-version>.tar.gz | docker load # 2. Replace the container docker stop ft-configs-service docker rm ft-configs-service docker run -d \ --name ft-configs-service \ --env-file .env.mysql \ -v $(pwd)/configs:/etc/app \ -v $(pwd)/logs:/app/logs \ -p 8080:8080 \ -p 8443:8443 \ --add-host=host.docker.internal:host-gateway \ --restart unless-stopped \ hub.friendly-tech.com/configs/ft-configs-service:<new-version> # 3. Verify curl -s http://localhost:8080/configs-service/actuator/health
Rollback
docker stop ft-configs-service
docker rm ft-configs-service
# Re-run with the previous image tag
docker run -d --name ft-configs-service \
--env-file .env.mysql \
-v $(pwd)/configs:/etc/app \
-v $(pwd)/logs:/app/logs \
-p 8080:8080 \
-p 8443:8443 \
--add-host=host.docker.internal:host-gateway \
--restart unless-stopped \
hub.friendly-tech.com/configs/ft-configs-service:<previous-version-tag>
Production Checklist
| # | Item | Notes |
|---|---|---|
1 |
Restart policy |
|
2 |
Health check |
Configured for |
3 |
Database credentials |
Replace default values in the |
4 |
JWT secret |
Replace empty |
5 |
CORS origins |
Replace the |
6 |
File permissions |
Restrict env files: |
7 |
Log management |
Mount |
8 |
Timezone |
Set |
9 |
Resource limits |
Add |
10 |
Bootstrap admin password rotated |
After first login, change the bootstrap admin password and remove it from |
Troubleshooting
Start with quick diagnostics:
docker logs ft-configs-service --tail 100
docker inspect ft-configs-service --format '{{json .Config.Env}}'
docker exec ft-configs-service ls -la /etc/app
Common Issues
Env File Not Found
Symptom: Docker fails to start with open .env.mysql: no such file or directory.
Check:
ls -la /usr/local/ft-configs-service/.env.*
Solution:
-
Run
docker runfrom the directory containing the env file, or use an absolute path:--env-file /usr/local/ft-configs-service/.env.mysql. -
Check file permissions:
chmod 640 .env.*. -
In
compose.yml, verify theenv_filepath is correct relative to the compose file location.
Database Connection Failures (MySQL)
Symptom: Communications link failure or Access denied in logs.
Check:
docker exec ft-configs-service nc -zv <db-host> 3306
docker exec ft-configs-service env | grep MYSQL
Solution:
-
Verify the database host is reachable from the container.
-
Check
MYSQL_HOST,MYSQL_PORT,MYSQL_USER, andMYSQL_PASSWORDin your.env.mysqlfile. -
If the database runs on the host machine, use
host.docker.internalinstead oflocalhost. -
If using Docker networking, ensure both containers are on the same network:
docker network inspect ft-configs-net
Database Connection Failures (Oracle)
Symptom: ORA-12514: Cannot connect to Oracle or ORA-01017: invalid username/password in logs.
Check:
docker exec ft-configs-service nc -zv <db-host> 1521
docker exec ft-configs-service env | grep ORACLE
Solution:
-
Verify
ORACLE_HOST,ORACLE_PORT,ORACLE_SERVICE,ORACLE_USER, andORACLE_PASSWORDin.env.oracle. -
Confirm the Oracle service name matches exactly (case-sensitive):
ORACLE_SERVICE=XEPDB1. -
If the database runs on the host machine, use
host.docker.internalinstead oflocalhost.
Hazelcast Connection Failure
Symptom: Unable to connect to any address in logs.
Check:
docker exec ft-configs-service env | grep HZ_MEMBERS
docker exec ft-configs-service env | grep CACHE_CONFIG
Solution:
-
Verify
HZ_MEMBERSis set and points to reachable Hazelcast members (e.g.,HZ_MEMBERS=hazelcast-host:5701). -
Confirm Hazelcast members are running and on the same Docker network.
-
Check that
CACHE_CONFIG_PATHin the env file is correct. The defaultclasspath:loads the JAR-bundledhazelcast-client.yaml; only set afile:path (e.g.file:/etc/app/) when externalizing the config to a mounted volume.
Port Conflict
Symptom: address already in use error.
Check:
lsof -i :8080
# or
docker ps
Solution:
-
Stop the conflicting service, or expose a different host port:
-p 9080:8080.
Container Restarting / Healthcheck Failing
Symptom: docker ps shows the container in a restart loop or health status is unhealthy.
Check:
docker logs ft-configs-service --tail 200
docker inspect ft-configs-service --format '{{json .State.Health}}'
Solution:
-
Review the logs for startup errors (database connection, missing env vars, etc.).
-
The healthcheck has a 60-second start period — wait at least 60 seconds before investigating.
-
Verify the health endpoint is correct:
http://localhost:8080/configs-service/actuator/health.
Wrong Profile Selected
Symptom: Application tries to connect to the wrong database type (e.g., MySQL errors when using Oracle).
Check:
docker exec ft-configs-service env | grep SPRING_PROFILES_ACTIVE
Solution:
-
Verify
SPRING_PROFILES_ACTIVEis set tomysqlororaclein the correct.envfile. -
Ensure
env_fileincompose.ymlpoints to the correct file (.env.mysqlor.env.oracle).
Liquibase Migration Failure
Symptom: LiquibaseException or Migration failed in application logs during startup.
Check:
docker logs ft-configs-service --tail 200 | grep -i liquibase
Solution:
-
Check database connectivity and credentials.
-
Ensure the database user has sufficient privileges to create/alter tables.
-
If upgrading from a previous version, check that the database schema is not corrupted. Restore from backup if necessary.
Bootstrap Admin Not Created
Symptom: Cannot log in after first startup. No admin account exists.
Check:
docker exec ft-configs-service env | grep FT_CONFIGS_BOOTSTRAP
Solution:
-
FT_CONFIGS_BOOTSTRAP_ADMIN_PASSWORDmust be set to a non-empty value before the first startup. -
If the password was empty, set it in
.env, remove the container and its database data, then start fresh:docker compose down -v # WARNING: removes database volume docker compose up -d
Build Fails: Permission Denied (gradlew)
Symptom: ./gradlew: Permission denied when building from source.
Check:
ls -la gradlew
Solution:
-
On the host:
chmod +x gradlew. -
Ensure your source checkout preserves execute permissions.
Build Fails: GitHub Credentials / Secret Errors
Symptom: Could not resolve all dependencies or 401 Unauthorized during Gradle build.
Check:
echo $GITHUB_USERNAME
echo $GITHUB_TOKEN | head -c 5
Solution:
-
Set
GITHUB_USERNAMEandGITHUB_TOKENenvironment variables before running./gradlew bootJar. -
Verify the GitHub token has
read:packagesscope and access to private dependencies.
CI Build Fails: JAR Not Found (Dockerfile.simple)
Symptom: COPY failed: file not found in build context when building with Dockerfile.simple in CI/CD.
Check:
ls -la build/libs/ft-configs-service.jar
Solution:
-
Dockerfile.simpleis for CI/CD pipelines only — it expects a pre-built JAR atbuild/libs/ft-configs-service.jar. -
Run
./gradlew bootJarbeforedocker build -f Dockerfile.simple .. -
For local development, use the multi-stage
Dockerfileinstead:docker build -t ft-configs-service:latest .
Port Reference
| Port | Protocol | Description |
|---|---|---|
8080 |
HTTP |
HTTP connector (REST API, Actuator, Swagger UI) |
8443 |
HTTPS |
TLS connector port (used when |
When HTTPS is enabled (SERVER_SSL_ENABLED=true 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).
Use different values for those container ports to keep both connectors active.
Publishing both Docker mappings (-p 8080:8080 and -p 8443:8443) is safe.
|