Installation & Deployment

Overview

This guide walks you through installing and running the TR-069 Emulator — a Spring Boot application (Java 25) that simulates TR-069/CWMP CPE devices for testing ACS implementations. Docker is the recommended deployment method: it ships a self-contained image that runs the emulator in headless web mode and exposes the browser-based control panel and REST API.

The deployment consists of:

  • TR-069 Emulator — Spring Boot service started with --web-mode (no desktop Swing GUI), serving the Web UI, the REST API and Swagger UI on port 8555.

  • Connection Request listener — an HTTP listener on port 9999 that lets the ACS initiate sessions with emulated devices.

  • Docker container — pre-built image from the Harbor registry or loaded from an archive.

  • Parameter trees — XML device-model files under /app/parameterstree inside the container, baked into the image and optionally mounted as a volume for customization.

The emulator runs as a single container and needs no database of its own to start. It connects outbound to your ACS (the host/port you configure in the UI), and — when Connection Request is enabled — accepts inbound requests from the ACS on port 9999.

Prerequisites

System Requirements

  • Docker Engine 20.10+

  • Docker Compose 2.0+ (optional — only for the Compose deployment option)

  • Minimum 1 GB RAM (2+ GB recommended; significantly more for large device counts — see Production / Load-Test Checklist)

  • 500 MB free disk space for the image and logs

JDK 25 is only required for local Maven builds and for running the desktop GUI without Docker. Docker deployment does not need a JDK on the host machine.

Supported Operating Systems

  • Linux (recommended)

  • macOS

  • Windows (Docker Desktop, or native Java for the desktop GUI)

Before starting, make sure:

  • Docker Engine 20.10+ is installed. If Docker is not installed, follow the Docker Installation Guide.

  • You have network access from the emulator to your ACS (HTTP/HTTPS host and port).

  • If you will use Connection Request, the ACS can reach the emulator host on port 9999 — see Connection Request Host.

Quick Start

For experienced users who already have Docker installed.

# 1. Authenticate with the registry (first time only)
docker login hub.friendly-tech.com

# 2. Run the emulator (the image is pulled automatically if missing)
docker run -d \
  --name tr069-emulator \
  -p 8555:8555 \
  -p 9999:9999 \
  hub.friendly-tech.com/emulator/tr069:latest

# 3. Verify (returns 200 with "Running" or "Stopped")
curl -s http://localhost:8555/api/status

After startup the Web UI is available at http://localhost:8555.

Port 8555 serves the Web UI and REST API. Port 9999 is the HTTP Connection Request listener the ACS uses to initiate sessions with emulated devices. If your ACS will send Connection Requests, also set CONNECTION_REQUEST_HOST — see Connection Request Host.

Getting the Docker Image

Option A: Pull from Harbor Registry (recommended)

# Authenticate with the registry
docker login hub.friendly-tech.com

# Pull the latest image (optional -- "docker run" / "docker compose up"
# will pull it automatically on first use)
docker pull hub.friendly-tech.com/emulator/tr069:latest

To pin to a specific release tag instead of latest:

docker pull hub.friendly-tech.com/emulator/tr069:<version>

<version> is an example placeholder. Always use the tag that corresponds to your deployment. To request Harbor access, contact the DevOps team for a user account or robot token.

Option B: Transfer Image to an Offline Server

Use this option when the target server has no internet access. 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/emulator/tr069:latest

    An explicit --platform matching the offline target server’s architecture is required if the image is multi-arch. Without --platform, docker pull selects the host architecture, which may not match the target. Replace linux/amd64 with the platform of your offline target (linux/arm64, etc.).

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

    docker save hub.friendly-tech.com/emulator/tr069:latest \
      -o tr069-emulator.tar
    gzip tr069-emulator.tar
  3. Transfer tr069-emulator.tar.gz to the offline server (e.g., via scp or removable media).

  4. On the offline server, load the image:

    gzip -dc tr069-emulator.tar.gz | docker load

Option C: Build from Source (developers only)

# Build the application JAR (Java 25 + Maven)
mvn clean package -DskipTests

# Build the Docker image (uses the bundled Dockerfile)
docker build -t tr069-emulator:latest .

The Dockerfile copies target/.jar, the parameterstree/ directory and the root configuration files into the image, so mvn package must run *before docker build.

After loading, pulling or building, verify the image is available:

docker images | grep tr069

Parameter Trees & Configuration

Parameter Trees Directory

Device data models are defined by XML files in the parameterstree/ directory (/app/parameterstree inside the container). These files are listed, selected, uploaded and deleted from the Web UI and the REST API.

parameterstree/
├── cpe_params.xml            # default tree (EmulatorParameter PARAMS_XML)
├── cpe_params_big_tree.xml
├── device_Technicolor.xml
├── Huawei_HG8245Q2.xml
├── base/
│   ├── tr-098.xml            # InternetGatewayDevice. root model
│   └── tr-181.xml            # Device. root model
└── ... (other device XML files)

The base/ subdirectory holds canonical TR-098 (InternetGatewayDevice.) and TR-181 (Device.) trees used by the "start by protocol" flow (REST POST /apiV2/startProtocol).

Each file defines the TR-069 data model — parameter paths, values, types and writability:

<?xml version="1.0" encoding="UTF-8"?>
<device>
    <parameter>
        <name>Device.DeviceInfo.Manufacturer</name>
        <value>MyCompany</value>
        <type>string</type>
        <writable>false</writable>
    </parameter>
    <!-- More parameters... -->
</device>

A bind mount on /app/parameterstree replaces the directory baked into the image. If you mount your own directory, make sure it contains the XML files you intend to use; otherwise the dropdown will be empty. To customize safely, first extract the defaults from the image, edit them, then mount your copy — see Deployment.

Runtime Configuration Files

The emulator persists runtime state to property files in its working directory (/app), not to a separate configuration directory:

File Description

start_params.properties

Persisted emulator start parameters — ACS protocol/host/port/URL path, group/concurrency/delay counts, and option toggles. Written when you click Save / Save Config in the UI.

cpe_self_updated_params.properties

Definitions of auto-updating parameters (the Updatable Parameters feature).

To keep this state across container re-creation, mount the working directory (or the individual files) as a volume — see Deployment.

Connection Request Host

When running in Docker, the emulator must report an externally reachable address to the ACS so the ACS can send Connection Requests back to the emulated device. Set the CONNECTION_REQUEST_HOST environment variable to the host IP or hostname the ACS can reach:

docker run -d \
  --name tr069-emulator \
  -p 8555:8555 \
  -p 9999:9999 \
  -e CONNECTION_REQUEST_HOST=192.168.1.100 \
  hub.friendly-tech.com/emulator/tr069:latest

This sets the Connection Request URL reported to the ACS to http://192.168.1.100:9999/{serial} (the variable maps to the JVM property -Dconnection.request.address).

If CONNECTION_REQUEST_HOST is not set, the emulator auto-detects the container’s local address, which is usually not reachable from the ACS. Set it explicitly whenever the ACS needs to initiate sessions.

Deployment

Option 1: docker run

docker run -d \
  --name tr069-emulator \
  -p 8555:8555 \
  -p 9999:9999 \
  -e CONNECTION_REQUEST_HOST=192.168.1.100 \
  --restart unless-stopped \
  hub.friendly-tech.com/emulator/tr069:latest

To customize parameter trees and persist runtime state, extract the defaults first and mount volumes:

# Extract the default parameter trees from the image
docker create --name tmp-tr069 hub.friendly-tech.com/emulator/tr069:latest
docker cp tmp-tr069:/app/parameterstree ./parameterstree
docker rm tmp-tr069

# Run with the parameter trees mounted (and persist runtime state)
docker run -d \
  --name tr069-emulator \
  -p 8555:8555 \
  -p 9999:9999 \
  -e CONNECTION_REQUEST_HOST=192.168.1.100 \
  -v "$(pwd)/parameterstree:/app/parameterstree" \
  --restart unless-stopped \
  hub.friendly-tech.com/emulator/tr069:latest

Option 2: Docker Compose

Create a docker-compose.yml file:

services:
  tr069-emulator:
    image: hub.friendly-tech.com/emulator/tr069:latest
    container_name: tr069-emulator
    ports:
      - "8555:8555"          # Web UI + REST API
      - "9999:9999"          # Connection Request listener
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - JAVA_OPTS=-Xms512m -Xmx1g
      - CONNECTION_REQUEST_HOST=192.168.1.100   (1)
    volumes:
      - ./parameterstree:/app/parameterstree    (2)
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8555/api/status"]
      interval: 30s
      timeout: 3s
      start_period: 30s
      retries: 3
1 Replace with the IP address reachable by your ACS. Remove the line to use auto-detection.
2 Optional. Mounting replaces the built-in trees — populate the directory first (see Option 1).

Start with:

docker compose up -d
docker compose logs -f tr069-emulator

Verify Installation

After starting the container, run the following checks:

# 1. Check container status
docker ps -f name=tr069-emulator
# Expected: container "Up" with 0.0.0.0:8555->8555/tcp and 0.0.0.0:9999->9999/tcp

# 2. Liveness check (returns 200 once the service is up)
curl -s http://localhost:8555/api/status
# Expected: "Running" or "Stopped"

# 3. Emulator status (Load Test) / active devices (Single Mode)
curl -s http://localhost:8555/api/status            # "Running" or "Stopped"
curl -s http://localhost:8555/apiV2/activeEmulators # [] on a fresh start

# 4. Application logs
docker logs tr069-emulator --tail 50
# Look for the Spring Boot "Started ..." line

# 5. Web UI and Swagger UI (open in a browser)
# http://localhost:8555                       # landing page (choose a mode)
# http://localhost:8555/load                  # Load Test Mode
# http://localhost:8555/single                # Single Emulator Mode
# http://localhost:8555/swagger-ui.html       # interactive REST API
# http://localhost:8555/v3/api-docs           # OpenAPI spec

Use /api/status (Load Test run state) or /apiV2/activeEmulators / /apiV2/statistics (Single Mode) as liveness/monitoring endpoints — each returns 200 once the service is up. The Spring Boot Actuator endpoints (/actuator/*) are *not exposed by the running service, so do not rely on /actuator/health for health checks.

Environment Variables

Variable Description Default Required

SERVER_PORT

Web UI / REST API port inside the container

8555

No

SPRING_PROFILES_ACTIVE

Active Spring profile

prod

No

JAVA_OPTS

JVM options (heap sizing, GC, heap-dump on OOM). Increase the heap for large device counts.

-Xms512m -Xmx1g -XX:+UseG1GC …​

No

CONNECTION_REQUEST_HOST

External IP/hostname reported to the ACS for Connection Requests. Maps to -Dconnection.request.address=http://<host>:9999. See Connection Request Host.

(auto-detect)

No

DEFAULT_ACS_URL

Default ACS URL pre-filled in the UI start forms

http://ftacs:8080/ftacs/ACS

No

TZ

Container timezone

UTC

No

Built on Spring Boot 3.5.6 / Java 25. The Connection Request listener defaults to HTTP on port 9999. The Docker image maps CONNECTION_REQUEST_HOST to -Dconnection.request.address=http://<host>:9999 (HTTP only). To serve Connection Requests over HTTPS, pass an https:// address through the JVM property directly (-Dconnection.request.address=https://<host>:<port>) — for example by running the JAR manually or overriding the container entrypoint; an additional TLS listener then starts using the bundled certificate (certs/server-cert.pem / server-key.pem). The listener port can be overridden with -Dconnection.request.port. See Protocol Support — Connection Request Transports for the XMPP and UDP/STUN transports.

SERVER_PORT changes the port inside the container. To reach it on a different host port, change only the left side of the Docker port mapping (e.g. -p 9555:8555); the container port stays 8555 unless you also change SERVER_PORT.

Manual Installation (without Docker)

Use this when you want to run the emulator directly on a host with Java 25 installed — either in headless web mode or as the desktop GUI.

Build from Source

  1. Clone the repository:

    git clone https://github.com/Friendly-Technologies/TR069_Emulator.git
    cd TR069_Emulator
  2. Build with Maven:

    mvn clean package -DskipTests

Run in Web Mode (headless)

Web mode serves the same browser UI and REST API as the Docker container.

java -jar target/FTCpeEmulator.jar --web-mode

Run from the project root so the parameterstree/ directory and the root configuration files are found relative to the working directory. To set the Connection Request address, pass -Dconnection.request.address=http://<host>:9999.

Run the Desktop GUI (Swing)

Launching the JAR without --web-mode starts the legacy Swing desktop application. The bundled run.bat does this with a larger heap; on Linux use linux/run_32gb_heap.sh for high-volume load tests.

# Windows
run.bat

# Linux (large heap)
./linux/run_32gb_heap.sh

Container Management

Command Description

docker run -d --name tr069-emulator -p 8555:8555 -p 9999:9999 <image>

Start the emulator in the background

docker stop tr069-emulator / docker start tr069-emulator

Stop / start the container

docker restart tr069-emulator

Restart the application

docker logs -f tr069-emulator

Follow application logs

docker exec -it tr069-emulator sh

Open a shell inside the container

docker exec tr069-emulator ls -la /app/parameterstree

List the available parameter-tree files

docker stats tr069-emulator --no-stream

View CPU / memory usage

docker inspect tr069-emulator --format '{{json .Config.Env}}'

Show the container’s environment variables

Updating

# 1. (Optional) back up parameter trees and runtime state
docker cp tr069-emulator:/app/parameterstree ./parameterstree-backup
docker cp tr069-emulator:/app/start_params.properties ./start_params.properties.bak

# 2. Pull the new image
docker pull hub.friendly-tech.com/emulator/tr069:latest

# 3. Recreate the container
docker rm -f tr069-emulator
docker run -d \
  --name tr069-emulator \
  -p 8555:8555 \
  -p 9999:9999 \
  -e CONNECTION_REQUEST_HOST=192.168.1.100 \
  -v "$(pwd)/parameterstree:/app/parameterstree" \
  --restart unless-stopped \
  hub.friendly-tech.com/emulator/tr069:latest

# 4. Verify (returns 200 with "Running" or "Stopped")
curl -s http://localhost:8555/api/status

If you are not mounting /app/parameterstree (and the runtime property files) as volumes, recreating the container resets parameter trees and saved configuration to the image defaults. Mount volumes to keep custom trees, uploaded XML and persisted settings across updates.

Rollback

docker rm -f tr069-emulator

# Re-run with the previous image tag
docker run -d \
  --name tr069-emulator \
  -p 8555:8555 \
  -p 9999:9999 \
  -v "$(pwd)/parameterstree:/app/parameterstree" \
  --restart unless-stopped \
  hub.friendly-tech.com/emulator/tr069:<previous-version-tag>

Production / Load-Test Checklist

# Item Notes

1

Restart policy

Use --restart unless-stopped (or restart: unless-stopped in Compose)

2

Connection Request host

Set CONNECTION_REQUEST_HOST to an ACS-reachable address; publish port 9999

3

Heap sizing

Raise JAVA_OPTS (-Xmx) for large device counts; for very high volume run the desktop mode with linux/run_32gb_heap.sh

4

Persistence

Mount /app/parameterstree and the runtime property files so custom trees and settings survive updates

5

Health check

Configure a healthcheck against /api/status (already in the Compose example)

6

Timezone

Set TZ if logs must use a specific timezone (defaults to UTC)

Troubleshooting

Start with quick diagnostics:

docker logs tr069-emulator --tail 100
docker inspect tr069-emulator --format '{{json .Config.Env}}'
docker exec tr069-emulator ls -la /app/parameterstree

Common Issues

ACS cannot reach the device (Connection Request fails)

Symptom: The ACS reports Connection Request timeouts; the device only informs on its own schedule.

Solution:

  • Set CONNECTION_REQUEST_HOST to an address the ACS can reach (not the container’s internal IP). See Connection Request Host.

  • Publish port 9999 (-p 9999:9999) and ensure no firewall blocks it.

Parameter-tree dropdown is empty / "file not found"

Symptom: No XML files in the dropdown, or a device fails to start with a missing-tree error.

Check:

docker exec tr069-emulator ls /app/parameterstree

Solution:

  • If you mounted /app/parameterstree, ensure the directory actually contains XML files (a mount replaces the built-in trees).

  • Re-extract the defaults (see Deployment, Option 1) or upload a tree from the UI.

Web UI not reachable on port 8555

Symptom: The browser cannot connect to http://localhost:8555.

Check:

docker ps -f name=tr069-emulator
curl -s http://localhost:8555/api/status

Solution:

  • Confirm the port mapping (-p 8555:8555) and that the container is Up.

Port conflict (address already in use)

Symptom: The container fails to start because port 8555 or 9999 is taken.

Solution:

  • Publish different host ports: -p 9555:8555 and/or -p 19999:9999 (keep the right side matching the container port).

OutOfMemoryError with many devices

Symptom: A heap dump appears at /app/logs/heapdump.hprof; logs show OutOfMemoryError.

Solution:

  • Increase the JVM heap via JAVA_OPTS, e.g. -e JAVA_OPTS="-Xms1g -Xmx4g".

  • For very large load tests, run the desktop mode with linux/run_32gb_heap.sh.

Port Reference

Port Protocol Description

8555

HTTP

Web UI, REST API, and Swagger UI

9999

HTTP

Connection Request listener (ACS-initiated sessions)

Outbound traffic to the ACS (HTTP/HTTPS on the host/port you configure in the UI) does not need to be published from the container.