Skip to main content

Docker Compose Deployment (ARM64)

This guide walks you through deploying the complete ALP-CONNEX stack on an ARM64 system using Docker Compose. After completing these steps, you will have:

  • The Management UI for configuring connectors and mappings
  • The Management API backend with a PostgreSQL database
  • The Valkey data store (process image)
  • The IEC 104 Connector serving data to SCADA clients
  • The MQTT Connector ingesting data from IoT devices
  • An Eclipse Mosquitto MQTT broker

Prerequisites

RequirementMinimum
ArchitectureARM64 (e.g. Raspberry Pi 4/5, NVIDIA Jetson, AWS Graviton)
Docker Engine24.0 or newer
Docker ComposeV2 (included with Docker Engine)
RAM2 GB free
Disk5 GB free

Verify your Docker installation:

docker --version
docker compose version

Folder Structure

Create the following directory layout on your host system:

alp-connex/
├── .env # Environment variables
├── docker-compose.yaml # Service definitions
├── mosquitto/
│ └── config/
│ └── mosquitto.conf # MQTT broker configuration
└── mqtt/
└── config/
└── mapping.json # MQTT mapping file (optional)
mkdir -p alp-connex/mosquitto/config alp-connex/mqtt/config
cd alp-connex

Step 1 — Environment Variables (.env)

Create a .env file in the project root. Docker Compose reads this file automatically and substitutes the variables into docker-compose.yaml.

# ──────────────────────────────────────────
# Host Configuration
# ──────────────────────────────────────────
# Replace with the IP address of the machine running this stack.
# This is used by the browser to reach the API — it cannot be "localhost"
# if you access the UI from another machine on the network.
HOST_IP=192.168.1.100

# ──────────────────────────────────────────
# Database
# ──────────────────────────────────────────
POSTGRES_USER=postgres
POSTGRES_PASSWORD=change-me-to-a-secure-password
POSTGRES_DB=alpconnex

# ──────────────────────────────────────────
# Image Versions
# ──────────────────────────────────────────
# Replace with the versions provided to you.
TAG_MANAGEMENT=x.y.z
TAG_IEC104=x.y.z
TAG_MQTT=x.y.z

# ──────────────────────────────────────────
# Connector Configuration IDs
# ──────────────────────────────────────────
# These are the unique identifiers for each connector configuration,
# generated by the Management UI when you create a new configuration.
IEC104_CONFIG_ID=00000000-0000-0000-0000-000000000000
MQTT_CONFIG_ID=00000000-0000-0000-0000-000000000000

Important: Replace HOST_IP with your actual machine IP and POSTGRES_PASSWORD with a secure password before starting the stack.

Step 2 — Docker Compose File

Create the docker-compose.yaml with the following content:

services:

# ═══════════════════════════════════════════
# PostgreSQL — Management Database
# ═══════════════════════════════════════════
database:
image: postgres:15
environment:
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: ${POSTGRES_DB}
# ports:
# - "5432:5432" # Uncomment for external database access
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER}"]
interval: 10s
timeout: 5s
retries: 5
restart: unless-stopped
networks:
- connector-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

# ═══════════════════════════════════════════
# Management UI — Frontend
# ═══════════════════════════════════════════
frontend:
image: registry.alpscale.io/alp-connex/alp-connex-management-frontend:${TAG_MANAGEMENT}
ports:
- "4200:8080"
environment:
# Replace with the IP address of your host machine
API_URL: "http://${HOST_IP}:8080"
depends_on:
backend:
condition: service_started
restart: unless-stopped
networks:
- connector-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

# ═══════════════════════════════════════════
# Management API — Backend
# ═══════════════════════════════════════════
backend:
image: registry.alpscale.io/alp-connex/alp-connex-management-api:${TAG_MANAGEMENT}
environment:
ASPNETCORE_URLS: http://+:8080
ASPNETCORE_ENVIRONMENT: Production
ConnectionStrings__alpconnexdb: >-
Host=database;Port=5432;Database=${POSTGRES_DB};
Username=${POSTGRES_USER};Password=${POSTGRES_PASSWORD}
ConnectionStrings__redis: valkey:6379
# Replace with the IP address of your host machine
Cors__AllowedOrigins: "http://${HOST_IP}:4200"
ports:
- "8080:8080"
depends_on:
database:
condition: service_healthy
valkey:
condition: service_healthy
restart: unless-stopped
networks:
- connector-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

# ═══════════════════════════════════════════
# Valkey — Process Image Data Store
# ═══════════════════════════════════════════
valkey:
image: valkey/valkey:8.0
# ports:
# - "6379:6379" # Uncomment for external Valkey access
volumes:
- valkey-data:/data
command: valkey-server --appendonly yes --appendfsync everysec
healthcheck:
test: ["CMD", "valkey-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
start_period: 5s
restart: unless-stopped
networks:
- connector-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

# ═══════════════════════════════════════════
# IEC 104 Connector
# ═══════════════════════════════════════════
iec104-server:
image: registry.alpscale.io/alp-connex/alp-connex-iec104:${TAG_IEC104}
ports:
- "2404:2404"
command: >-
--id ${IEC104_CONFIG_ID}
--log-level info
redis --host valkey --port 6379
depends_on:
valkey:
condition: service_healthy
restart: unless-stopped
networks:
- connector-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

# ═══════════════════════════════════════════
# MQTT Connector
# ═══════════════════════════════════════════
mqtt-client:
image: registry.alpscale.io/alp-connex/alp-connex-mqtt:${TAG_MQTT}
command: >-
--id ${MQTT_CONFIG_ID}
--log-level info
--mapping mapping.json
mqtt --broker mosquitto --port 1883
redis --host valkey --port 6379
volumes:
- ./mqtt/config/mapping.json:/app/mapping.json:ro
depends_on:
valkey:
condition: service_healthy
mosquitto:
condition: service_healthy
restart: unless-stopped
networks:
- connector-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

# ═══════════════════════════════════════════
# Eclipse Mosquitto — MQTT Broker
# ═══════════════════════════════════════════
mosquitto:
image: eclipse-mosquitto:2
ports:
- "1883:1883"
volumes:
- ./mosquitto/config:/mosquitto/config:ro
- mosquitto-data:/mosquitto/data
healthcheck:
test: ["CMD-SHELL", "mosquitto_sub -t '$$SYS/#' -C 1 -W 3"]
interval: 10s
timeout: 5s
retries: 3
start_period: 5s
restart: unless-stopped
networks:
- connector-net
logging:
driver: json-file
options:
max-size: "10m"
max-file: "3"

# ═══════════════════════════════════════════
# Networks & Volumes
# ═══════════════════════════════════════════
networks:
connector-net:
driver: bridge

volumes:
db-data:
valkey-data:
mosquitto-data:

Step 3 — Mosquitto Configuration

Create the Mosquitto configuration file at mosquitto/config/mosquitto.conf:

listener 1883
allow_anonymous true
persistence true
persistence_location /mosquitto/data/

This is a minimal configuration for getting started. For production, consider enabling authentication and TLS. See the Eclipse Mosquitto documentation.

Step 4 — Mapping File (Optional)

If you want to use a file-based mapping instead of (or in addition to) the Management UI, create a mapping file at mqtt/config/mapping.json. See the MQTT Connector documentation for the file format and examples.

If you are using the Management UI to manage mappings, you can create an empty mapping file as a placeholder:

{
"version": "1.0.0",
"mappings": []
}

Starting the Stack

From the alp-connex/ directory:

# Start all services in the background
docker compose up -d

# Watch the startup logs
docker compose logs -f

The first start will pull all images, which may take a few minutes depending on your internet connection.

Verifying the Deployment

Check Service Health

docker compose ps

All services should show Up and healthy (where applicable):

NAME              STATUS                  PORTS
database Up (healthy)
frontend Up 0.0.0.0:4200->8080/tcp
backend Up 0.0.0.0:8080->8080/tcp
valkey Up (healthy)
iec104-server Up 0.0.0.0:2404->2404/tcp
mqtt-client Up
mosquitto Up (healthy) 0.0.0.0:1883->1883/tcp

Access the Management UI

Open a browser and navigate to:

http://<HOST_IP>:4200

Test the MQTT Broker

From the host or another machine on the network, publish a test message:

mosquitto_pub -h <HOST_IP> -p 1883 -t "test/topic" -m '{"value": 42}'

Check Connector Logs

# IEC 104 Connector
docker compose logs iec104-server

# MQTT Connector
docker compose logs mqtt-client

Stopping the Stack

# Stop all services (data is preserved in volumes)
docker compose down

# Stop and remove all data (fresh start)
docker compose down -v

Troubleshooting

Services fail to start

SymptomCauseSolution
image not foundWrong image tag or not logged in to registryVerify TAG_* values in .env and run docker login registry.alpscale.io
Port already in useAnother service occupies the portCheck with ss -tlnp (Linux) or netstat -an and stop the conflicting service
Database health check failsPostgreSQL not readyWait 30 seconds and check again; verify POSTGRES_* variables

MQTT Connector not receiving data

  1. Check that the Mosquitto broker is healthy: docker compose ps mosquitto
  2. Verify the mapping file is correctly mounted: docker compose exec mqtt-client ls /app/mapping.json
  3. Check connector logs for errors: docker compose logs mqtt-client
  4. Ensure the IoT device publishes to a topic that matches a mapping entry

IEC 104 Connector not reachable

  1. Verify port 2404 is published: docker compose ps iec104-server
  2. Check firewall rules on the host machine
  3. Ensure the SCADA client is configured to connect to <HOST_IP>:2404
  4. Check connector logs: docker compose logs iec104-server

Frontend cannot reach the API

  1. Verify HOST_IP in .env is correct and reachable from your browser
  2. Ensure API_URL and Cors__AllowedOrigins both use the same IP
  3. Check that port 8080 is not blocked by a firewall

For connector-specific configuration, see the IEC 104 Connector and MQTT Connector documentation.