The headless edge computing platform for FactoryTalk Optix — documentation, presentations, and container support.
User Manual
Technical Presentation
Docker Integration
Deploy your first container on your OptixEdge
Follow this tutorial to deploy a MySQL database with phpMyAdmin on OptixEdge using Docker. The procedure is based on the OptixEdge & Docker Containers presentation (page 42 onward).
- Install Docker (or Docker Desktop) on your PC or virtual machine.
- Plug your OptixEdge, open
https://optix_edge_ip_address, sign in, go to Docker, enable Docker Engine under Services, then reboot OptixEdge. - Storage choice: containers can run on the internal eMMC or on an external Micro SD. To change, go to Docker > Container Storage and set Data Root from Internal to Micro SD.
Deploy with Portainer
- In System Manager, go to Docker and enable Embedded Portainer CE under Services.
- Reboot OptixEdge.
- Open
https://optix_edge_ip_address:9443and trust the self-signed certificate. - Set the admin password at first login.
- Go to "Home" and select the local environment, open Stacks, then click Add stack.
- Name the stack (e.g.,
my_sql_stack). - Paste the following content into the web editor and click Deploy the stack:
services: mysql: image: mysql:8 container_name: mysql-retention environment: MYSQL_ROOT_PASSWORD: Password01 # Change to a strong password MYSQL_DATABASE: example_db MYSQL_USER: user MYSQL_PASSWORD: Password01 # Change to a strong password ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql restart: unless-stopped phpmyadmin: image: phpmyadmin:latest container_name: phpmyadmin depends_on: - mysql environment: PMA_HOST: mysql PMA_PORT: 3306 MYSQL_ROOT_PASSWORD: Password01 # Keep in sync with MYSQL_ROOT_PASSWORD ports: - "8090:80" restart: unless-stopped volumes: mysql_data: - Wait for both containers to show as running in the Portainer dashboard.
- Open
http://optix_edge_ip_address:8090in your browser. - Log in with user
rootand passwordPassword01(or with useruserand the same password). - Verify that the
example_dbdatabase is listed in the left panel.
Load from USB key
.tar file and one docker-compose.yaml. For multi-container deployments, include all required images in the same .tar bundle.
MySQL + phpMyAdmin: deploy a MySQL database with a phpMyAdmin web front-end.
- Use a USB key formatted as
FAT32,exFAT, orext4. - On your PC, open a terminal and pull both images for
linux/arm64:docker pull --platform linux/arm64 mysql:8 docker pull --platform linux/arm64 phpmyadmin:latest
- Export both images into a single
.tarfile:docker save -o mysql_phpmyadmin.tar mysql:8 phpmyadmin:latest
- Create a file named
docker-compose.yamlwith this content:services: mysql: image: mysql:8 container_name: mysql-retention environment: MYSQL_ROOT_PASSWORD: Password01 # Change to a strong password MYSQL_DATABASE: example_db MYSQL_USER: user MYSQL_PASSWORD: Password01 # Change to a strong password ports: - "3306:3306" volumes: - mysql_data:/var/lib/mysql restart: unless-stopped phpmyadmin: image: phpmyadmin:latest container_name: phpmyadmin depends_on: - mysql environment: PMA_HOST: mysql PMA_PORT: 3306 MYSQL_ROOT_PASSWORD: Password01 # Keep in sync with MYSQL_ROOT_PASSWORD ports: - "8090:80" restart: unless-stopped volumes: mysql_data: - Copy the two files (
mysql_phpmyadmin.tar,docker-compose.yaml) to the USB key root directory. - Plug the USB key into OptixEdge.
- In System Manager, open Docker and click Import docker image via USB in the Containers area.
- Verify that both containers are running, MySQL is listening on port
3306, and phpMyAdmin is reachable athttp://optix_edge_ip_address:8090.
Mosquitto: deploy an Eclipse Mosquitto MQTT broker for lightweight messaging between devices and services.
- Use a USB key formatted as
FAT32,exFAT, orext4. - On your PC, open a terminal and pull the Mosquitto image for
linux/arm64:docker pull --platform linux/arm64 eclipse-mosquitto:2
- Export the image into a
.tarfile (remember where you save it, as you'll need to copy it to the USB key):docker save -o mosquitto.tar eclipse-mosquitto:2
- Create a file named
docker-compose.yamlwith this content:services: mosquitto: image: eclipse-mosquitto:2 container_name: mosquitto ports: - "1883:1883" - "9001:9001" volumes: - mosquitto_config:/mosquitto/config - mosquitto_data:/mosquitto/data - mosquitto_log:/mosquitto/log restart: unless-stopped volumes: mosquitto_config: mosquitto_data: mosquitto_log: - Copy the two files (
mosquitto.tar,docker-compose.yaml) to the USB key root directory. - Plug the USB key into OptixEdge.
- In System Manager, open Docker and click Import docker image via USB in the Containers area.
- Verify that the container is running and Mosquitto is listening on port
1883(MQTT) and port9001(WebSockets).
Deploy with Docker CLI remote management
- In System Manager, under Docker > Remote Management, choose the connection type and allowed network interfaces.
- On your local machine, point the Docker CLI at OptixEdge.
Linux / macOS
Windows PowerShell
export DOCKER_HOST=tcp://optix_edge_ip:2375
$env:DOCKER_HOST = "tcp://optix_edge_ip:2375"
- Verify the connection by listing running containers:
docker ps
- Run any Docker command as if you were on the OptixEdge device (e.g.,
docker compose up -d). - When finished, restore your local Docker context:
Linux / macOS
Windows PowerShell
unset DOCKER_HOST
Remove-Item Env:DOCKER_HOST
Go Beyond: more exercise ideas
Kuma container: run Uptime Kuma to monitor service uptime with a simple web dashboard.
Portainer stackversion: '3.8'
services:
kuma:
image: docker.io/louislam/uptime-kuma:1
container_name: kuma
ports:
- "3001:3001"
volumes:
- kuma_data:/app/data
restart: unless-stopped
volumes:
kuma_data:
Mosquitto: deploy an MQTT broker for lightweight messaging between devices and services.
Portainer stackversion: '3.8'
services:
mosquitto:
image: eclipse-mosquitto:2
container_name: mosquitto
ports:
- "1883:1883"
- "9001:9001"
restart: unless-stopped
Prometheus + Node Exporter: monitor CPU and RAM usage of the OptixEdge device.
Portainer stackversion: '3.8'
services:
# --- INIT CONTAINER ---
# This tiny container runs once at startup to create the Prometheus
# configuration file. It writes the config into a shared volume,
# then exits. This is needed because Prometheus needs a config file
# to know which services to monitor (called "scrape targets").
prometheus-init:
image: busybox:latest # Minimal Linux image (~1MB), used only to write a file
container_name: prometheus-init
volumes:
- prometheus_config:/config # Shared volume where the config file will be written
entrypoint: ["/bin/sh", "-c"] # Run a shell command
command:
- |
cat > /config/prometheus.yml << 'EOF'
global:
scrape_interval: 5s # How often Prometheus collects metrics (every 5 seconds)
scrape_configs:
- job_name: prometheus # Prometheus monitors itself
static_configs:
- targets: ['localhost:9090']
- job_name: node_exporter # Node Exporter provides device metrics (CPU, RAM, disk, etc.)
static_configs:
- targets: ['node-exporter:9100']
EOF
restart: "no" # Run only once, do not restart after exiting
# --- PROMETHEUS ---
# The metrics database. It periodically collects (scrapes) metrics
# from the targets defined in the config file, stores them, and
# provides a web UI to query and graph them.
# Web UI available at: http://<device-ip>:9090
prometheus:
image: prom/prometheus:latest
container_name: prometheus
depends_on:
prometheus-init:
condition: service_completed_successfully # Wait for the config file to be created first
ports:
- "9090:9090" # Expose Prometheus web UI on port 9090
command:
- --config.file=/config/prometheus.yml # Path to the config created by the init container
- --storage.tsdb.path=/prometheus # Where to store collected metrics data
- --web.listen-address=0.0.0.0:9090 # Listen on all network interfaces
volumes:
- prometheus_data:/prometheus # Persistent storage for metrics history
- prometheus_config:/config:ro # Read the config file (read-only)
restart: unless-stopped # Auto-restart unless manually stopped
# --- NODE EXPORTER ---
# A lightweight agent that reads system metrics from the device
# (CPU usage, RAM, disk space, network traffic, etc.) and exposes
# them on port 9100 for Prometheus to collect.
# Raw metrics visible at: http://<device-ip>:9100/metrics
node-exporter:
image: prom/node-exporter:latest
container_name: node-exporter
ports:
- "9100:9100" # Expose metrics endpoint on port 9100
restart: unless-stopped
# --- VOLUMES ---
# Named volumes persist data across container restarts and recreations.
volumes:
prometheus_data: # Stores Prometheus metrics history
prometheus_config: # Stores the generated config file
Check RAM and CPU in Prometheus
- Open
http://optix_edge_ip_address:9090. - In the Expression field, paste one query and click Execute.
- Use Graph view to see trends over time.
100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100)
(1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100
Apache Guacamole: deploy a clientless remote desktop gateway that supports VNC, RDP, and SSH protocols. Access remote desktops from any device using only a web browser — no client software required.
Portainer stackservices:
postgres:
image: docker.io/postgres:16-alpine
container_name: guac-postgres
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init:/docker-entrypoint-initdb.d:ro
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER} -d ${POSTGRES_DB}"]
interval: 10s
timeout: 5s
retries: 10
guacd:
image: docker.io/guacamole/guacd:1.6.0
container_name: guac-guacd
restart: unless-stopped
guacamole:
image: docker.io/guacamole/guacamole:1.6.0
container_name: guac-web
restart: unless-stopped
depends_on:
postgres:
condition: service_healthy
guacd:
condition: service_started
environment:
GUACD_HOSTNAME: guacd
POSTGRESQL_HOSTNAME: postgres
POSTGRESQL_PORT: 5432
POSTGRESQL_DATABASE: ${POSTGRES_DB}
POSTGRESQL_USER: ${POSTGRES_USER}
POSTGRESQL_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRESQL_AUTO_CREATE_ACCOUNTS: "true"
ports:
- "8080:8080"
volumes:
postgres_data:
Environment Variables
In Portainer, add these environment variables when deploying the stack:
POSTGRES_DB=guacamole_db POSTGRES_USER=guacamole_user POSTGRES_PASSWORD=YourSecurePassword
Access the Web UI
- Open
http://optix_edge_ip_address:8080/guacamolein your browser. - Log in with the default credentials:
guacadmin/guacadmin. - Change the admin password immediately after first login.
- Add connections to your VNC, RDP, or SSH targets via Settings > Connections.
Tips for production-ready compose files
CPU and memory limits
Add a deploy.resources.limits block to each service to prevent a container from consuming all device resources and affecting FTOptix performance.
services:
mysql:
image: mysql:8
container_name: mysql-retention
ports:
- "3306:3306"
restart: unless-stopped
deploy:
resources:
limits:
cpus: "0.50"
memory: "256M" // RAM
Log rotation
Add a logging block to each service to cap log file size and count, preventing local storage from filling up.
services:
mysql:
image: mysql:8
container_name: mysql-retention
ports:
- "3306:3306"
restart: unless-stopped
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
Both combined
A complete service definition with both resource limits and log rotation applied.
Full exampleservices:
mysql:
image: mysql:8
container_name: mysql-retention
ports:
- "3306:3306"
restart: unless-stopped
deploy:
resources:
limits:
cpus: "0.50"
memory: "256M"
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"