Installation & Deployment

This guide walks you through installing and running the Provision Portal — a Spring Boot 3 application built with Java 25 and Gradle. Docker is the recommended deployment method for consistency and ease of use.

1. Prerequisites

1.1. System Requirements

Component Requirement

Java

JDK 25 (only for local Gradle builds; not needed for Docker deployment)

Database

MySQL 8.0+ or Oracle 21c XE / 19c (ACS schema + Flowable schema must already exist)

Docker

Docker Engine 20.10+ and Docker Compose 2.0+

OS

Linux (recommended), macOS, or Windows with WSL2

RAM

2 GB minimum (4+ GB recommended)

Disk

1 GB free space for the image and logs

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 ACS database (MySQL or Oracle).

  • You have created the Flowable database schema (see Flowable Database Setup).

  • You have network access to the Northbound API instance.

  • You have network access to the Hazelcast cluster (if caching is used).

1.2. Prepare Working Directory

Create the working directory on the host machine:

mkdir -p /usr/local/provision-portal
cd /usr/local/provision-portal

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 after placing the files:

/usr/local/provision-portal/
+-- .env.mysql                    # Environment variables (MySQL profile)
+-- .env.oracle                   # Environment variables (Oracle profile)
+-- hazelcast-client.yaml         # Hazelcast cache cluster config
+-- Configuration.xml             # General system-wide configuration
+-- objects.xml                   # TR-069 data objects mapping
+-- params.xml                    # TR-069 data parameters mapping
+-- CSVSettings.xml               # CSV parsing settings
+-- replaceCPE.xml                # CPE replacement procedure config
+-- CustomStatus.xml              # Custom status code logging config
+-- encrypted-parameters.txt      # Parameters requiring encryption (optional)

You only need the env file that matches your database (.env.mysql or .env.oracle), not both.

Configuration files are also available in the source repository:

File Path in repository

.env.mysql, .env.oracle

docker/

XML files (Configuration.xml, objects.xml, params.xml, CSVSettings.xml, replaceCPE.xml, CustomStatus.xml)

src/main/resources/xml/

hazelcast-client.yaml

provided on FT_DISK (not in repository)

More info about XML configuration files: XML Configuration Guide.

2. Flowable Database Setup

The Provision Portal uses the Flowable workflow engine for business process management. A dedicated database schema and user are required before first startup.

  • The application automatically runs Liquibase migrations on startup to create and update Flowable tables.

  • No manual table creation is required — only the schema and user must exist.

2.1. MySQL

CREATE DATABASE flowable;
CREATE USER 'flowable'@'%' IDENTIFIED WITH mysql_native_password BY 'your_secure_password';
GRANT ALL PRIVILEGES ON flowable.* TO 'flowable'@'%';
FLUSH PRIVILEGES;

2.2. Oracle

-- Run as SYS/SYSTEM (or a user with CREATE USER/TABLESPACE).
-- If your database is CDB/PDB, switch to the target PDB first:
-- ALTER SESSION SET CONTAINER = XEPDB1;

-- Create tablespace (optional but recommended)
CREATE TABLESPACE FLOWABLE_DATA
  DATAFILE SIZE 500M
  AUTOEXTEND ON NEXT 50M MAXSIZE UNLIMITED;

-- Create user/schema
CREATE USER FLOWABLE IDENTIFIED BY "your_secure_password"
  DEFAULT TABLESPACE FLOWABLE_DATA
  TEMPORARY TABLESPACE "TEMP"
  QUOTA UNLIMITED ON FLOWABLE_DATA;

-- Grant minimal privileges
GRANT CREATE SESSION, CREATE TABLE, CREATE SEQUENCE, CREATE VIEW,
      CREATE PROCEDURE, CREATE TRIGGER, CREATE TYPE
  TO FLOWABLE;
Oracle pitfalls
  • ORA-00922 / ORA-00933: caused by running multiple SQL statements as one command or unquoted passwords with special characters. In DBeaver, use Execute Script (Alt+X) or run statements one by one.

  • ORA-01950: no privileges on tablespace 'USERS': fix by setting default tablespace: ALTER USER FLOWABLE DEFAULT TABLESPACE FLOWABLE_DATA;

  • ORA-01430: column already exists: Flowable schema was partially created. For a fresh install: DROP USER FLOWABLE CASCADE; and re-run the setup.

3. Option A: 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 "Prerequisites")
cd /usr/local/provision-portal

# 2. Get the Docker image (choose one):

#    Option A: Pull from Harbor registry (recommended)
docker login hub.friendly-tech.com
#    Note: "docker pull" is optional if you use docker compose --
#    "docker compose up" will pull the image automatically.
docker pull hub.friendly-tech.com/api/provision-portal:latest

#    Option B: Load from archive (for offline servers — see "Transfer Image to Offline Server" below)
#    gzip -dc provision-portal-<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, NORTHBOUND_API_URL, 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 -ks https://localhost:8443/prov-portal/actuator/health

To switch between MySQL and Oracle, change the env_file value in compose.yml:

env_file:
  - .env.oracle    # instead of .env.mysql

After startup the application is available at https://localhost:8443. For detailed step-by-step instructions, continue with Option B: Detailed Installation.

4. Option B: Detailed Installation

Step-by-step guide covering each stage of the installation process.

4.1. Step 1: Get the Docker Image

# Authenticate with the registry
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/api/provision-portal:latest

To pin to a specific release version:

docker pull hub.friendly-tech.com/api/provision-portal:v1.0.0-b0.0.6

The version v1.0.0-b0.0.6 is an example. Always use the version tag that corresponds to your deployment.

Optionally, retag the image for shorter references in docker run commands:

docker tag hub.friendly-tech.com/api/provision-portal:latest provision-portal:latest

If you skip retagging, use the full image name (hub.friendly-tech.com/api/provision-portal:latest) in all subsequent commands.

To request Harbor access, contact the DevOps team for a user account or robot token.

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

  1. On a machine with Harbor access, log in and pull the image for the target server’s architecture:

    docker login hub.friendly-tech.com
    docker pull --platform linux/amd64 \
      hub.friendly-tech.com/api/provision-portal:<version>

    An explicit --platform matching the offline target server’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. For example, on an Apple Silicon (arm64) Mac without --platform, the resulting archive will be arm64 and will fail with a platform does not match warning on amd64 servers. The example uses linux/amd64; replace it with the platform of your offline target server (linux/arm64, etc.).

  2. Save the pulled image to a tar archive and compress it:

    docker save hub.friendly-tech.com/api/provision-portal:<version> \
      -o provision-portal-<version>.tar
    gzip provision-portal-<version>.tar
  3. Transfer provision-portal-<version>.tar.gz to the offline server (e.g., via scp or removable media).

  4. On the offline server, load the image:

    gzip -dc provision-portal-<version>.tar.gz | docker load

4.1.3. Option C: Build from Source (developers only)

docker buildx build \
  --platform linux/amd64,linux/arm64 \
  --secret id=github_token,env=GITHUB_TOKEN \
  --secret id=github_user,src=<(printf '%s' "Friendly-Technologies") \
  -t provision-portal:latest \
  --load .

Building from source requires valid GitHub credentials with access to 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 provision-portal

4.2. Step 2: Configure Environment

The Provision Portal reads its configuration from:

  • .env.mysql or .env.oracle — environment variables loaded via Docker --env-file

  • XML configuration files — service definition files (mounted to /etc/app/)

  • hazelcast-client.yaml — cache configuration (mounted to /etc/app/hazelcast-client.yaml)

  • encrypted-parameters.txt — optional list of parameters to encrypt (mounted to /etc/app/encrypted-parameters.txt)

Edit the environment file that matches your database:

cd /usr/local/provision-portal
vi .env.mysql               # or .env.oracle for Oracle

If the database, Hazelcast cluster, or Northbound API runs on the host machine (not in Docker), use one of the following as the hostname in MYSQL_HOST / ORACLE_HOST, FLOWABLE_MYSQL_HOST / FLOWABLE_ORACLE_HOST, NORTHBOUND_API_URL, and in hazelcast-client.yaml:

  • host.docker.internal — works on Docker Desktop (macOS, Windows) and on Linux with --add-host=host.docker.internal:host-gateway (for docker run). The provided compose.yml already includes extra_hosts for this.

  • The host machine’s real IP address (e.g., 192.168.1.10)

localhost or 127.0.0.1 will not work — inside the container these point to the container itself, not the host.

4.2.1. .env.mysql

# ==========================================================
# Provision Portal - Environment Configuration (MySQL)
# ==========================================================

# Active Spring profile: mysql or oracle
SPRING_PROFILES_ACTIVE=mysql

# Application HTTP port inside the container
PORT=8080

# Application HTTPS port inside the container
HTTPS_PORT=8443

# Container timezone
TZ=Europe/Kiev

# ==========================================================
# ACS Database (MySQL)
# ==========================================================
# Database timezone (used in JDBC URL serverTimezone parameter)
DB_TIMEZONE=Europe/Kiev

# HikariCP connection pool settings
DB_MAX_POOL_SIZE=10
DB_MIN_IDLE=5
DB_CONNECTION_TIMEOUT_MS=30000

# MySQL connection for ACS schema
# For Docker networking: use container name or host.docker.internal
# For external database: use IP address or hostname
MYSQL_HOST=<your-mysql-host>            # <-- replace
MYSQL_PORT=3306
MYSQL_SCHEMA=ftacs
MYSQL_USER=ftacs
MYSQL_PASSWORD=<your-db-password>       # <-- replace
MYSQL_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver

# ==========================================================
# Flowable Database (MySQL)
# ==========================================================
# Flowable workflow engine uses a separate database schema.
# Create this schema before first startup (see Flowable Database Setup).
FLOWABLE_MYSQL_HOST=<your-mysql-host>   # <-- replace
FLOWABLE_MYSQL_PORT=3306
FLOWABLE_MYSQL_SCHEMA=flowable
FLOWABLE_MYSQL_USER=flowable
FLOWABLE_MYSQL_PASSWORD=<your-flowable-db-password>  # <-- replace
FLOWABLE_MYSQL_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver

# ==========================================================
# Northbound API
# ==========================================================
# URL of the Northbound API service
# If both services are in the same Docker network, use container name:
#   http://northbound-api:8080/iot-webservice
# If Northbound API runs on a separate host:
#   http://<host>:<port>/iot-webservice
NORTHBOUND_API_URL=http://<your-nbi-host>:8080/iot-webservice  # <-- replace

# ==========================================================
# Configuration Paths
# ==========================================================
# Path to XML configuration files inside the container
XML_PATH=file:/etc/app/

# Path to additional configuration files (e.g., encrypted-parameters.txt)
CONFIG_PATH=file:/etc/app/

# ==========================================================
# Logging
# ==========================================================
# Log level for the application package (com.friendly.provisionportal).
# Values: ERROR, WARN, INFO, DEBUG. In DEBUG mode business errors include stack traces.
# Can also be changed at runtime via Actuator.
LOGGING_LEVEL_COM_FRIENDLY_PROVISIONPORTAL=DEBUG

# ==========================================================
# Timeouts
# ==========================================================
# Default timeout for device operations (seconds)
TIMEOUT=60
# Extra delay (ms) after transaction response (default: 1000, increase under heavy ACS load)
TRANSACTION_DELAY=1000

4.2.2. .env.oracle

# ==========================================================
# Provision Portal - Environment Configuration (Oracle)
# ==========================================================

# Active Spring profile: mysql or oracle
SPRING_PROFILES_ACTIVE=oracle

# Application HTTP port inside the container
PORT=8080

# Application HTTPS port inside the container
HTTPS_PORT=8443

# Container timezone
TZ=Europe/Kiev

# ==========================================================
# ACS Database (Oracle)
# ==========================================================
# HikariCP connection pool settings
DB_MAX_POOL_SIZE=10
DB_MIN_IDLE=5
DB_CONNECTION_TIMEOUT_MS=30000

# Oracle connection for ACS schema
# For Docker networking: use container name or host.docker.internal
# For external database: use IP address or hostname
ORACLE_HOST=<your-oracle-host>          # <-- replace
ORACLE_PORT=1521
ORACLE_SERVICE=XEPDB1
ORACLE_USER=ftacs
ORACLE_PASSWORD=<your-db-password>      # <-- replace
ORACLE_DRIVER_CLASS_NAME=oracle.jdbc.OracleDriver

# ==========================================================
# Flowable Database (Oracle)
# ==========================================================
# Flowable workflow engine uses a separate database schema.
# Create this schema before first startup (see Flowable Database Setup).
FLOWABLE_ORACLE_HOST=<your-oracle-host> # <-- replace
FLOWABLE_ORACLE_PORT=1521
FLOWABLE_ORACLE_SERVICE=XEPDB1
FLOWABLE_ORACLE_USER=flowable
FLOWABLE_ORACLE_PASSWORD=<your-flowable-db-password>  # <-- replace
FLOWABLE_ORACLE_DRIVER_CLASS_NAME=oracle.jdbc.OracleDriver

# ==========================================================
# Northbound API
# ==========================================================
# URL of the Northbound API service
# If both services are in the same Docker network, use container name:
#   http://northbound-api:8080/iot-webservice
# If Northbound API runs on a separate host:
#   http://<host>:<port>/iot-webservice
NORTHBOUND_API_URL=http://<your-nbi-host>:8080/iot-webservice  # <-- replace

# ==========================================================
# Configuration Paths
# ==========================================================
# Path to XML configuration files inside the container
XML_PATH=file:/etc/app/

# Path to additional configuration files (e.g., encrypted-parameters.txt)
CONFIG_PATH=file:/etc/app/

# ==========================================================
# Logging
# ==========================================================
# Log level for the application package (com.friendly.provisionportal).
# Values: ERROR, WARN, INFO, DEBUG. In DEBUG mode business errors include stack traces.
# Can also be changed at runtime via Actuator.
LOGGING_LEVEL_COM_FRIENDLY_PROVISIONPORTAL=DEBUG

# ==========================================================
# Timeouts
# ==========================================================
# Default timeout for device operations (seconds)
TIMEOUT=60
# Extra delay (ms) after transaction response (default: 1000, increase under heavy ACS load)
TRANSACTION_DELAY=1000

Pre-configured template files are available on FT_DISK and in the source repository.

Copy the file that matches your database to the working directory:

cp .env.mysql /usr/local/provision-portal/.env.mysql
# or
cp .env.oracle /usr/local/provision-portal/.env.oracle

4.2.3. hazelcast-client.yaml

Basic structure:

hazelcast-client:
  cluster-name: <your-cluster-name>
  network:
    cluster-members:
      - <hazelcast-server-host>:<hazelcast-server-port>
  connection-strategy:
    async-start: false
    reconnect-mode: ON
cluster-name

Name of the Hazelcast cluster to connect to

cluster-members

List of Hazelcast server addresses (format: host:port)

async-start

Whether to start connection asynchronously (recommended: false for startup validation)

reconnect-mode

How to handle connection loss (ON = automatic reconnection)

4.2.4. XML Configuration Files

The XML_PATH directory must contain the following files:

  • Configuration.xml — General system-wide configuration

  • objects.xml — Maps services to TR-069 data objects

  • params.xml — Maps services to TR-069 data parameters

  • CSVSettings.xml — CSV input parsing and formatting settings

  • replaceCPE.xml — CPE replacement procedure config

  • CustomStatus.xml — Custom status code logging config

For detailed descriptions, see the XML Configuration Guide.

4.2.5. encrypted-parameters.txt (optional)

Comma-separated list of parameter names that should be encrypted before storing in the database. By default the application uses a built-in list from the JAR. To override at runtime, place the file in the working directory.

Example content:

Password,WPAPassphrase,KeyPassphrase,PreSharedKey,Secret

4.3. Step 3: Deploy

Create a compose.yml file in your working directory:

version: "3.8"

services:
  provision-portal:
    image: hub.friendly-tech.com/api/provision-portal:latest
    container_name: provision-portal
    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"         # HTTPS port -- change similarly if needed (e.g. "9443:8443")
    volumes:
      - .:/etc/app
    extra_hosts:
      - "host.docker.internal:host-gateway"
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-kf", "https://localhost:8443/prov-portal/actuator/health"]
      interval: 30s
      timeout: 3s
      start_period: 60s
      retries: 3
# Start the application
docker compose up -d

# View logs
docker compose logs -f provision-portal

# Stop
docker compose down

4.3.2. Option 2: docker run

cd /usr/local/provision-portal

docker run -d \
  --name provision-portal \
  --env-file .env.mysql \
  -v $(pwd):/etc/app \
  -p 8080:8080 \
  -p 8443:8443 \
  --restart unless-stopped \
  hub.friendly-tech.com/api/provision-portal:latest

Replace --env-file .env.mysql with --env-file .env.oracle for Oracle. If you retagged the image locally, use provision-portal:latest instead of the full Harbor path.

4.3.3. Docker Networking

If the Provision Portal container must communicate with other containers (database, Hazelcast, Northbound API, etc.), create a shared Docker network:

docker network create app-network

Add --network app-network 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.

To deploy Provision Portal alongside Northbound API and Service API as a single stack, see the Java API Stack Deployment Guide.

4.4. Step 4: Verify Installation

After starting the container, run the following checks:

# 1. Check container status
docker ps -f name=provision-portal
# Expected: container with status "Up" and ports 0.0.0.0:8080->8080/tcp, 0.0.0.0:8443->8443/tcp

# 2. Health check
curl -ks https://localhost:8443/prov-portal/actuator/health
# Expected: {"status":"UP"}

# 3. Check application logs
docker logs provision-portal --tail 50
# Look for: "Started ProvisionPortalApplication in XX.XXX seconds"

# 4. Swagger UI (open in browser)
# https://localhost:8443/prov-portal/swagger-ui/index.html

# 5. Test SOAP endpoint
curl -ks https://localhost:8443/prov-portal/soap/ProvWS?wsdl | head -5
# Expected: beginning of the WSDL XML document

5. Container Management

Command Description

docker compose up -d

Start all services in the background

docker compose down

Stop and remove containers

docker compose logs -f provision-portal

Follow application logs

docker compose ps

Show container status

docker compose restart provision-portal

Restart the application

docker compose pull

Update images to latest versions

docker exec -it provision-portal sh

Enter the application container

docker exec provision-portal ls -la /etc/app

Check mounted configuration files

docker logs --tail 100 provision-portal

View last 100 log lines

docker stats provision-portal --no-stream

View resource usage

docker inspect provision-portal --format '{{json .Config.Env}}'

Check environment variables

6. Updating the Application

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

# 1. Backup configuration
cd /usr/local/provision-portal
tar -czf provision-portal-backup-$(date +%Y%m%d).tar.gz .env.* *.xml *.yaml *.txt

# 2. Pull the latest image
docker compose pull provision-portal

# 3. Recreate the container with the new image
docker compose up -d provision-portal

# 4. Verify
curl -ks https://localhost:8443/prov-portal/actuator/health

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

  1. On a machine with Harbor access, log in and pull the new image for the target server’s architecture:

    docker login hub.friendly-tech.com
    docker pull --platform linux/amd64 \
      hub.friendly-tech.com/api/provision-portal:<new-version>

    An explicit --platform matching the offline target server’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. For example, on an Apple Silicon (arm64) Mac without --platform, the resulting archive will be arm64 and will fail with a platform does not match warning on amd64 servers. The example uses linux/amd64; replace it with the platform of your offline target server (linux/arm64, etc.).

  2. Save the pulled image to a tar archive and compress it:

    docker save hub.friendly-tech.com/api/provision-portal:<new-version> \
      -o provision-portal-<new-version>.tar
    gzip provision-portal-<new-version>.tar
  3. Transfer provision-portal-<new-version>.tar.gz to the offline server (e.g., via scp or removable media).

  4. On the offline server, load the new image and replace the running container:

    # 1. Load the new image from archive
    gzip -dc provision-portal-<new-version>.tar.gz | docker load
    
    # 2. Replace the container
    docker stop provision-portal
    docker rm provision-portal
    
    docker run -d \
      --name provision-portal \
      --env-file .env.mysql \
      -v $(pwd):/etc/app \
      -p 8080:8080 \
      -p 8443:8443 \
      --restart unless-stopped \
      hub.friendly-tech.com/api/provision-portal:<new-version>
    
    # 3. Verify
    curl -ks https://localhost:8443/prov-portal/actuator/health

6.3. Rollback

docker stop provision-portal
docker rm provision-portal

# Re-run with the previous image tag
docker run -d --name provision-portal \
  --env-file .env.mysql \
  -v $(pwd):/etc/app \
  -p 8080:8080 \
  -p 8443:8443 \
  --restart unless-stopped \
  hub.friendly-tech.com/api/provision-portal:<previous-version-tag>

7. Production Checklist

# Item Notes

1

Restart policy

restart: unless-stopped is already configured in the examples

2

Health check

Configured for /prov-portal/actuator/health with a 30s interval

3

Database credentials

Replace default values in the .env file (MYSQL_PASSWORD / ORACLE_PASSWORD)

4

Flowable credentials

Replace FLOWABLE_MYSQL_PASSWORD / FLOWABLE_ORACLE_PASSWORD in the .env file

5

Northbound API URL

Verify NORTHBOUND_API_URL points to the correct instance

6

File permissions

Restrict env files: chmod 600 .env.*

7

Log management

Configure Docker log rotation: --log-opt max-size=50m --log-opt max-file=5

8

Timezone

Set TZ in .env (defaults to Europe/Kiev)

9

Resource limits

Add --memory=2g or deploy.resources.limits in compose.yml for production

8. Runtime Log Level Management

The application supports changing the log level at runtime without restarting the container, using Spring Boot Actuator.

Level What is logged

ERROR

Critical errors only (lost connection to ACS/DB)

WARN

Business errors — without stack trace

INFO

Key operations (startup, authorization, main actions)

DEBUG

Detailed diagnostics + stack traces for business errors

In production use INFO. Switch to DEBUG temporarily when investigating issues — business errors will include full stack traces.

View current log level (GET)
curl http://<host>:8080/prov-portal/actuator/loggers/com.friendly.provisionportal
Response example (HTTP 200)
{
    "configuredLevel": "DEBUG",
    "effectiveLevel": "DEBUG"
}
Switch to INFO (POST)
curl -X POST http://<host>:8080/prov-portal/actuator/loggers/com.friendly.provisionportal \
  -H 'Content-Type: application/json' \
  -d '{"configuredLevel": "INFO"}'
Switch to DEBUG (POST)
curl -X POST http://<host>:8080/prov-portal/actuator/loggers/com.friendly.provisionportal \
  -H 'Content-Type: application/json' \
  -d '{"configuredLevel": "DEBUG"}'
All POST requests return HTTP 204 No Content with an empty body — this is the expected success response. To verify the change, use the GET request above.

To change the log level persistently (survives container restart), set LOGGING_LEVEL_COM_FRIENDLY_PROVISIONPORTAL in the environment file (.env.mysql or .env.oracle):

# Values: ERROR, WARN, INFO, DEBUG
LOGGING_LEVEL_COM_FRIENDLY_PROVISIONPORTAL=INFO

9. Troubleshooting

Start with quick diagnostics:

docker logs provision-portal --tail 100
docker inspect provision-portal --format '{{json .Config.Env}}'
docker exec provision-portal ls -la /etc/app

9.1. Could not resolve placeholder 'XML_PATH'

Symptom: PlaceholderResolutionException: Could not resolve placeholder 'XML_PATH'

Solutions:

  1. Verify --env-file points to the correct .env.mysql or .env.oracle file. Use an absolute path if needed: --env-file /usr/local/provision-portal/.env.mysql.

  2. Confirm the env file contains XML_PATH=file:/etc/app/.

  3. Validate inside the container: docker exec provision-portal env | grep XML_PATH.

9.2. open .env.mysql: no such file or directory

Symptom: Docker fails to start with a "file not found" error.

Solutions:

  1. Run docker run from the directory containing the env file, or use an absolute path: --env-file /usr/local/provision-portal/.env.mysql.

  2. Check file permissions: chmod 640 .env.*.

9.3. FileNotFoundException: XML configuration files

Symptom: Application logs show XML configuration files are missing.

Solutions:

  1. Confirm XML files exist in /usr/local/provision-portal/: ls -la /usr/local/provision-portal/*.xml.

  2. Verify the volume mount: -v $(pwd):/etc/app.

  3. Check inside the container: docker exec provision-portal ls /etc/app.

9.4. Database Connection Failures

Symptom: MySQL Communications link failure or Oracle ORA-12514 in logs.

Solutions:

  1. Verify the database host is reachable from the container:

    docker exec provision-portal nc -zv <db-host> <db-port>
  2. Check MYSQL_HOST / ORACLE_HOST and credentials in your .env.* file.

  3. If using Docker networking, ensure both containers are on the same network:

    docker network inspect app-network

9.5. Flowable Database Not Found

Symptom: Unknown database 'flowable' or ORA-01017: invalid username/password for the Flowable user in logs.

Solutions:

  1. Verify the Flowable database schema exists (see Flowable Database Setup).

  2. Check FLOWABLE_MYSQL_HOST / FLOWABLE_ORACLE_HOST and credentials in the env file.

  3. Confirm the Flowable user has the required privileges.

9.6. Northbound API Connection Failure

Symptom: Connection refused or timeout errors when calling Northbound API.

Solutions:

  1. Verify NORTHBOUND_API_URL in the env file is correct.

  2. Check that the Northbound API container is running: docker ps -f name=northbound-api.

  3. If using Docker networking, ensure both containers are on the same network and use container names instead of localhost.

9.7. Hazelcast Connection Failure

Symptom: Unable to connect to any address in logs.

Solutions:

  1. Verify Hazelcast members are running and on the same Docker network.

  2. Check that cluster-name and cluster-members in hazelcast-client.yaml match the server configuration.

9.8. Port Conflict

Symptom: address already in use error.

Solutions:

  1. Find the process using the port:

    lsof -i :8080
    # or
    docker ps
  2. Stop the conflicting service, or expose different host ports: -p 9080:8080 -p 9443:8443.

9.9. Build Fails: Permission denied: ./gradlew

Symptom: Docker build stage fails on ./gradlew.

Solutions:

  1. On the host: chmod +x gradlew.

  2. Ensure your source checkout preserves execute permissions.

9.10. Build Fails: Secret Errors

Symptom: Could not read script '/run/secrets/github_user' during build.

Solutions:

  1. Provide the required --secret flags when running docker build (see Step 1: Get the Docker Image).

  2. Verify GitHub credentials are valid and have access to private dependencies.

10. Port Reference

Port Protocol Description

8443

HTTPS

Main application port (REST, SOAP, Actuator, Swagger UI)

8080

HTTP

Additional HTTP connector (same endpoints, no TLS)

The primary port is 8443 (HTTPS with self-signed certificate). Port 8080 is an additional HTTP connector for environments where TLS termination is handled externally. Both are configurable via HTTPS_PORT and PORT environment variables. Map them to any host ports using -p <host-https>:8443 -p <host-http>:8080.