Docker & Deploy (Advanced)
|
First-time setup? Start with the Installation & Deployment guide instead. This page covers advanced topics: building from source, developer Compose setup, and Kubernetes deployment notes. |
ft-configs-service is a Spring Boot service that manages configuration data and exposes HTTP APIs under /configs-service.
The recommended deployment approach is Docker (standalone docker run, or Docker Compose for local stacks).
For profiles and the full configuration matrix, see Configuration.
2) Getting the Docker image
For pulling the release image from hub.friendly-tech.com (including registry login and offline transfer), see Installation & Deployment — Registry Authentication. The rest of this section covers building the image yourself.
Build locally (developer-only)
The canonical release image is built with Dockerfile.simple (Java 25, Temurin).
Dockerfile.simple copy the application artifact from the build context as build/libs/ft-configs-service.jar, so you must prepare that file before running docker build.
Or You can use Dockerfile which has 2 steps—build and run, so you dont need build project separately.
./gradlew clean bootJar
JAR_PATH=$(find build/libs -maxdepth 1 -name "*.jar" ! -name "*-plain.jar" -print -quit)
cp "$JAR_PATH" build/libs/ft-configs-service.jar
Expected outcome:
-
build/libs/ft-configs-service.jarexists.
If the Gradle build fails to resolve internal dependencies from GitHub Packages, set GITHUB_USERNAME and GITHUB_TOKEN (see Build & Test).
|
docker build -f Dockerfile.simple \
-t ft-configs-service:local \
--build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
.
Expected outcome:
-
docker imagesshowsft-configs-service:local.
Verified runtime characteristics (from Dockerfile.simple and src/main/resources/application.yml):
-
Runtime base image:
eclipse-temurin:25-jre-alpine -
Default profile inside the image:
SPRING_PROFILES_ACTIVE=mysql(override per environment) -
Runs as non-root user
appuser(uid/gid1001) -
Exposes port
8080(context path/configs-service) -
Image healthcheck:
GET http://localhost:8080/configs-service/actuator/health
3) Working directory and docker run (production pattern)
For the production-friendly working directory layout, the --env-file pattern, and the Docker Compose deployment, see Installation & Deployment — Preparation and Deployment — Docker Compose.
Inline env vars (quick, local testing only)
Prefer --env-file (see the guide linked above) to avoid leaking secrets into shell history.
This variant sets DB, Hazelcast, and JWT settings inline for a throwaway local run; adjust values to your environment.
IMAGE="<YOUR_REGISTRY>/configs/ft-configs-service:<tag>" # or: "ft-configs-service:local"
docker run -d --name ft-configs-service \
-e SPRING_PROFILES_ACTIVE=mysql \
-e MYSQL_JDBC_URL="jdbc:mysql://<DB_HOST>:3306/configs?allowPublicKeyRetrieval=true&useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC" \
-e MYSQL_USER="<DB_USER>" \
-e MYSQL_PASSWORD="<DB_PASSWORD>" \
-e HZ_MEMBERS="<HZ_1>:5701,<HZ_2>:5701" \
-e JWT_SECRET="<BASE64_JWT_SECRET>" \
-p 8080:8080 \
--restart unless-stopped \
"$IMAGE"
4) Run with Docker Compose (developer/operator)
docker-compose.yaml starts:
-
ft-configs-service(HTTP:8080, context path/configs-service) -
mysql:8.4(TCP:3306, with a named volume for persistence)
It uses SPRING_PROFILES_ACTIVE=local and overrides the datasource via SPRING_DATASOURCE_*.
It does not start a Hazelcast member, and it does not set HZ_MEMBERS by default.
Quick start
If you run Compose from this repo and keep build: . enabled, prepare the jar first (see [build-locally]).
docker compose up -d
docker compose logs -f ft-configs-service mysql
curl -fsS http://localhost:8080/configs-service/actuator/health
Add Hazelcast (recommended for local stacks)
Create a small override file (for example docker-compose.hazelcast.yaml):
services:
ft-configs-service:
environment:
HZ_MEMBERS: "hazelcast:5701"
hazelcast:
image: hazelcast/hazelcast:5.6.0
restart: unless-stopped
Start with both files:
docker compose -f docker-compose.yaml -f docker-compose.hazelcast.yaml up -d
5) Run & Verify
Health (Actuator):
curl -fsS http://localhost:8080/configs-service/actuator/health
Prometheus metrics (Actuator; exposed by default in this repo):
curl -fsS http://localhost:8080/configs-service/actuator/prometheus | head
Check logs:
docker logs -f ft-configs-service
Swagger UI (if enabled in your environment):
6) Troubleshooting
docker run --env-file … fails with "no such file or directory"
-
Cause: wrong path, missing file, or the file is not readable.
-
Fix:
-
Confirm the file exists:
ls -l /usr/local/ft-configs-service/.env.mysql -
Use an absolute path in
--env-file.
-
Container is restarting / healthcheck is failing
-
Check logs:
docker logs --tail=200 ft-configs-service -
Common causes:
-
DB is not reachable or credentials are wrong (the service won’t start without a working datasource).
-
Liquibase migrations fail (schema mismatch, missing privileges, or wrong DB type/profile).
-
DB connection failures (MySQL / Oracle)
-
Cause: wrong profile or wrong JDBC URL for the selected profile.
-
Fix:
-
Ensure
SPRING_PROFILES_ACTIVEmatches your DB (mysqlororacle). -
For
mysql, set eitherMYSQL_JDBC_URLorMYSQL_HOST/MYSQL_PORT/MYSQL_SCHEMAplusMYSQL_USER/MYSQL_PASSWORD. -
For
oracle, set eitherORACLE_JDBC_URLorORACLE_HOST/ORACLE_PORT/ORACLE_SERVICEplusORACLE_USER/ORACLE_PASSWORD. -
In Compose, containers must connect to
mysql:3306(service name on the compose network), not the published host port.
-
Hazelcast client cannot connect
-
Cause:
HZ_MEMBERSis unset or points to hosts not reachable from inside the container/pod. -
Fix:
-
Set
HZ_MEMBERSto endpoints reachable from the container network (for Compose, usehazelcast:5701). -
If you changed the Hazelcast port, include it in each member entry.
-
Wrong profile selected
-
Symptom: Oracle settings applied while targeting MySQL (or vice versa).
-
Fix: set
SPRING_PROFILES_ACTIVEexplicitly in.env.mysql/ your deployment manifest.
Port conflicts (bind: address already in use)
-
Fix:
-
Pick a different host port (example:
-p 18080:8080). -
Identify the process using the port with your OS tooling.
-
Compose build fails with "`build/libs/ft-configs-service.jar` not found"
-
Cause: the Dockerfiles copy
build/libs/ft-configs-service.jarfrom the build context. -
Fix: run the jar preparation step from [build-locally] before
docker compose up.
7) Kubernetes deployment notes
This repo does not ship Kubernetes manifests. The runtime contract is:
-
Stateless pods (horizontally scalable).
-
External dependencies:
-
shared database (MySQL or Oracle)
-
shared Hazelcast cluster
-
Probes must include the servlet context path /configs-service:
-
Readiness/Liveness:
/configs-service/actuator/health -
Metrics:
/configs-service/actuator/prometheus
readinessProbe:
httpGet:
path: /configs-service/actuator/health
port: 8080
livenessProbe:
httpGet:
path: /configs-service/actuator/health
port: 8080
If you enable separate readiness/liveness actuator groups in your environment, prefer:
-
/configs-service/actuator/health/readiness -
/configs-service/actuator/health/liveness
At a minimum, model the following inputs as ConfigMaps/Secrets:
-
DB connectivity and credentials
-
HZ_MEMBERS -
JWT_SECRET
8) JWT secret rotation
JWT_SECRET signs every access and refresh token. Persistent across normal redeploys (so live sessions survive restarts), but must be rotated on fresh infrastructure.
When to rotate
-
Fresh install — new database created from scratch, regardless of whether the application image is the same. Generate a new secret.
-
Suspected compromise — secret leaked via logs, backups, screenshots, or otherwise exposed.
-
Scheduled rotation — every 6-12 months as defence-in-depth, even without incident.
When NOT to rotate
Routine redeploy of the same service against the same database. Rotating the secret here forcibly logs out every active user without security benefit.
How to generate
openssl rand -base64 32
Store in your secret manager (Vault, AWS Secrets Manager, Kubernetes Secret) and expose to the pod as the JWT_SECRET env var.
Why this matters
users.tokens_valid_after plus the pwh JWT claim already prevent stale-token bypass on a fresh DB: a freshly bootstrapped admin gets a fresh tokens_valid_after and a new BCrypt salt, so old tokens fail both checks. JWT_SECRET rotation is defence in depth — it neutralises a separate threat: long-lived production secrets that may have leaked over time.
9) 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. |