sha256:c61289169e2e98b31a2d018e2b5f9015d3d3320d60ad6a49e370e6bbd397a66d
Last pushed
4 days by peckaales
Type
Compose
Manifest digest
sha256:c61289169e2e98b31a2d018e2b5f9015d3d3320d60ad6a49e370e6bbd397a66d
# Local test stack: the monolith, SQL Server, and the one-shot service that creates its databases.
# Setup needs three files in this directory: docker-compose.yml, .env (see .env.example) and license/SignedLicense.jws.
# docker compose up -d the server and its database
# docker compose --profile hmi up -d with WebHMI on http://localhost:3000
# docker compose --profile tools up -d with CloudBeaver on http://localhost:8978
# Extras live in optional/: docker compose -f docker-compose.yml -f optional/<name>.yml up -d
name: edge
# The database the server and configure-system both use. `mssql` is the service below; ICOWB_SqlPassword
# also has to reach that service as MSSQL_SA_PASSWORD, so the two stay in step.
x-database: &database
ICOWB_SqlInstance: mssql,1433
ICOWB_SqlUsername: sa
ICOWB_SqlCatalog: IcoUnifiedConfig
ICOWB_SqlSetupCatalog: IcoSetup
ICOWB_SqlPassword: ${ICOWB_SqlPassword:?set ICOWB_SqlPassword in .env}
services:
edge:
image: icodevtest/edge:11.6.19-main
container_name: edge
# Fixed, because the OPC UA certificate in the pki volume is stamped with the hostname. A random one
# on each recreate invalidates it and `ua:` stops resolving.
hostname: edge
# All three let licensing read /sys/firmware/dmi/tables/DMI for the machine identifiers; without them
# the server runs unlicensed. docs/features/edge/29036-licensing-machine-id.md.
security_opt:
- systempaths=unconfined
- apparmor=unconfined
cap_add:
- DAC_READ_SEARCH
restart: unless-stopped
ports:
- "8080:8080"
- "8443:8443"
# OPC UA server interface, opc.tcp and https.
- "5011:5011"
- "5013:5013"
# Inbound from devices on the host's LAN: BACnet/IP (I-Am replies, COV, foreign device via a BBMD) and
# SNMP traps. Host and container port must match. docs/edge-container.md, "Devices on the host's LAN".
# 47900 TCP
- "47808:47808/udp"
- "162:162/udp"
environment:
<<: *database
# data connectors
ICO_PM__BACNETSC__ENABLED: true
ICO_PM__IOTPUBLISHER__ENABLED: true
ICO_PM__IOTSUBSCRIBER__ENABLED: true
ICO_PM__MODBUS__ENABLED: true
ICO_PM__OPCUA__ENABLED: true
ICO_PM__SNMP__ENABLED: true
# assets
ICO_PM__ASSETCATALOG__ENABLED: true
ICO_PM__ASSETCATALOGINPROC__ENABLED: true
# alarm server
ICO_PM__ALMS__ENABLED: true
# alarm collector
ICO_PM__ALMC__ENABLED: true
# historian collector
ICO_PM__DHCOLLECTOR__ENABLED: true
ICO_PM__DHCOLLECTORNOSESSION__ENABLED: true
# ICO_TRACE_OVERRIDE: stdout@Debug
# Ports INSIDE the container; change the `ports:` mapping above too:
# ICO_Communication__Endpoints__Http__Port: 8080
# ICO_Communication__Endpoints__Https__Port: 8443
# The address clients use from outside, so the PUBLISHED port ("8888:8080" -> 8888):
# ICO_Communication__WebServerPublicOrigin: http://my-host:8080
# HTTPS needs both of these AND the ./certs mount; without a certificate Kestrel skips port 8443.
# ICO_FWX_TLS_CERT_PATH: /certs/server.pem
# ICO_FWX_TLS_CERT_KEY_PATH: /certs/server.key
# ICO_FWX_TLS_CERT_PASSWORD: <only for a password-protected key or .pfx>
healthcheck:
test: ["CMD-SHELL", "wget -q -O /dev/null http://localhost:8080/wb/ || exit 1"]
interval: 5s
start_interval: 1s
start_period: 15s
timeout: 3s
retries: 6
depends_on:
mssql:
condition: service_healthy
configure-system:
condition: service_completed_successfully
volumes:
- pki:/usr/share/ICONICS/11/pki
# Runtime state only, so a new image can change what it ships.
- traces:/usr/share/ICONICS/11/Logs/Traces
- license_log:/usr/share/ICONICS/11/Logs/License
- data:/usr/share/ICONICS/11/Data
# The folder, not the file: a file mount keeps the replaced file, so a new licence would never be seen.
- ./license:/usr/share/ICONICS/11/License:ro
# - ./certs:/certs:ro
# Creates the configuration databases when they are missing and upgrades what is behind; `edge` waits for it.
configure-system:
image: icodevtest/edge:11.6.19-main
container_name: configure-system
command: configure-system
restart: "no"
depends_on:
mssql:
condition: service_healthy
environment:
<<: *database
# Leaves the new system unsecured. "true" needs ICOWB_SecurityUser and ICOWB_SecurityPassword and
# creates the first login user instead.
ICOWB_SecureSystem: "false"
# ICOWB_Providers: "SEC,FWX"
mssql:
image: mcr.microsoft.com/mssql/server:2025-latest
container_name: mssql
restart: unless-stopped
environment:
ACCEPT_EULA: "Y"
# Read only while the mssql volume is empty, and the image enforces its own policy (>= 8 characters).
MSSQL_SA_PASSWORD: ${ICOWB_SqlPassword:?set ICOWB_SqlPassword in .env}
ports:
- "1433:1433"
healthcheck:
test: ["CMD-SHELL", "/opt/mssql-tools18/bin/sqlcmd -S localhost -U sa -P \"$$MSSQL_SA_PASSWORD\" -C -Q 'SELECT 1' >/dev/null || exit 1"]
interval: 5s
start_interval: 1s
start_period: 15s
timeout: 3s
retries: 5
volumes:
- mssql:/var/opt/mssql
# Database UI on http://localhost:8978 — register the server as host `mssql`, port 1433.
cloudbeaver:
image: dbeaver/cloudbeaver:latest
container_name: cloudbeaver
profiles: [tools]
restart: unless-stopped
ports:
- "8978:8978"
volumes:
- cloudbeaver_data:/opt/cloudbeaver/workspace
depends_on:
- mssql
webhmi:
image: icodevtest/webhmi:latest
container_name: webhmi
profiles: [hmi]
restart: unless-stopped
ports:
- 3000:8080
environment:
ASPNETCORE_URLS: http://+:8080
COMPlus_gcServer: 0
ICO_OIDC_PROMPT: Empty
ICO_Client__DefaultConnection__Url: "ws://edge:8080/fwxserverws/ws_anon/"
ICO_TRACE_OVERRIDE: stdout@info
volumes:
- ./displays:/usr/share/ICONICS/11/PubDisplay/displays
volumes:
pki:
traces:
license_log:
data:
mssql:
cloudbeaver_data:
# Runs `edge` in the host's network namespace instead of on the compose bridge, so BACnet and SNMP broadcasts
# reach every network the host is on, with no BBMD and no field network to configure:
# docker compose -f docker-compose.yml -f optional/host-network.yml up -d
# The server binds 8080, 8443, 5011, 5013, 47808/udp and 162/udp on the host itself; anything else on the
# host using one of them stops it. It has no network isolation from the host.
services:
edge:
network_mode: host
# The container takes the host's name, which is just as stable for the OPC UA certificate.
hostname: !reset null
ports: !reset []
# No Docker DNS on the host network. `mssql` is also the server name configure-system stores in IcoSetup,
# so it has to resolve; it reaches SQL Server through its published port.
extra_hosts:
- "mssql:127.0.0.1"
volumes:
# From the directory compose runs in: run from the OCI artifact, ./ would mean compose's download cache.
- ${PWD}/license:/usr/share/ICONICS/11/License:ro
# On the host network too: from the bridge it would reach the server through the host's firewall, which may
# block it. It keeps port 3000, since the server holds 8080.
webhmi:
network_mode: host
ports: !reset []
environment:
ASPNETCORE_URLS: http://+:3000
ICO_Client__DefaultConnection__Url: "ws://localhost:8080/fwxserverws/ws_anon/"
docker compose -f oci://icodevtest/edge-stack:latest upUse the above command to pull and run the Compose file. Learn more.