Docker Deployment

November 22, 2025 ยท View on GitHub

Overview

OpenPLC Runtime v4 provides official Docker images for easy deployment across multiple platforms. The containerized runtime includes all dependencies and can be deployed with a single command.

Official Image

Registry: GitHub Container Registry (GHCR)

Image: ghcr.io/autonomy-logic/openplc-runtime:latest

Supported Architectures:

  • linux/amd64 - x86_64 systems
  • linux/arm64 - ARM 64-bit (Raspberry Pi 4, etc.)
  • linux/arm/v7 - ARM 32-bit (Raspberry Pi 3, etc.)

Quick Start

Pull and Run

docker pull ghcr.io/autonomy-logic/openplc-runtime:latest

docker run -d \
  --name openplc-runtime \
  -p 8443:8443 \
  --cap-add=SYS_NICE \
  --cap-add=SYS_RESOURCE \
  -v openplc-runtime-data:/var/run/runtime \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Note: The runtime will listen on port 8443 for connections from the OpenPLC Editor. Do not open https://localhost:8443 in a browser - there is no web interface. Connect to it from the OpenPLC Editor desktop application.

Stop and Remove

docker stop openplc-runtime
docker rm openplc-runtime

Volume Management

Persistent Data

The runtime stores important data in /var/run/runtime/:

Contents:

  • .env - Environment variables (JWT secret, database URI, pepper)
  • restapi.db - SQLite database with user accounts
  • Socket files (created at runtime, ephemeral)

Volume Mount:

-v openplc-runtime-data:/var/run/runtime

Using a named volume provides persistence and easy management:

# Create named volume
docker volume create openplc-runtime-data

# Run with named volume
docker run -d \
  --name openplc-runtime \
  -p 8443:8443 \
  -v openplc-runtime-data:/var/run/runtime \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Benefits:

  • Data persists across container restarts
  • Easy backup and restore
  • Docker manages volume location

Bind Mount (Alternative)

For direct access to runtime data:

mkdir -p /path/to/runtime-data

docker run -d \
  --name openplc-runtime \
  -p 8443:8443 \
  -v /path/to/runtime-data:/var/run/runtime \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Benefits:

  • Direct file system access
  • Easy inspection and backup
  • Can use existing directory

Considerations:

  • Requires proper permissions
  • Host path must exist
  • Less portable across systems

Port Mapping

Default Port

The runtime exposes port 8443 for REST API access from the OpenPLC Editor:

-p 8443:8443

Custom Port

To use a different host port:

-p 9443:8443  # Editor connects to https://localhost:9443

Localhost Only

To restrict access to localhost:

-p 127.0.0.1:8443:8443

Multiple Interfaces

To bind to specific interface:

-p 192.168.1.100:8443:8443

Environment Variables

Database URI

Override the default database location:

docker run -d \
  --name openplc-runtime \
  -p 8443:8443 \
  -e SQLALCHEMY_DATABASE_URI=sqlite:////var/run/runtime/custom.db \
  -v openplc-runtime-data:/var/run/runtime \
  ghcr.io/autonomy-logic/openplc-runtime:latest

JWT Secret

Provide a custom JWT secret (not recommended, auto-generated is secure):

-e JWT_SECRET_KEY=your_64_character_hex_string_here

Pepper

Provide a custom pepper for password hashing:

-e PEPPER=your_64_character_hex_string_here

Real-Time Scheduling

For real-time performance, the container may need privileged mode:

docker run -d \
  --name openplc-runtime \
  --privileged \
  -p 8443:8443 \
  -v openplc-runtime-data:/var/run/runtime \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Alternative (More Secure):

Grant specific capabilities instead of full privileged mode:

docker run -d \
  --name openplc-runtime \
  --cap-add=SYS_NICE \
  -p 8443:8443 \
  -v openplc-runtime-data:/var/run/runtime \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Capabilities:

  • SYS_NICE - Required for SCHED_FIFO real-time scheduling

Docker Compose

Basic Configuration

Create docker-compose.yml:

version: '3.8'

services:
  openplc-runtime:
    image: ghcr.io/autonomy-logic/openplc-runtime:latest
    container_name: openplc-runtime
    ports:
      - "8443:8443"
    volumes:
      - openplc-runtime-data:/var/run/runtime
    restart: unless-stopped

volumes:
  openplc-runtime-data:

Start:

docker-compose up -d

Stop:

docker-compose down

Advanced Configuration

version: '3.8'

services:
  openplc-runtime:
    image: ghcr.io/autonomy-logic/openplc-runtime:latest
    container_name: openplc-runtime
    cap_add:
      - SYS_NICE
    ports:
      - "127.0.0.1:8443:8443"
    volumes:
      - openplc-runtime-data:/var/run/runtime
      - ./plugins.conf:/workdir/plugins.conf:ro
    environment:
      - TZ=America/New_York
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-k", "-f", "https://localhost:8443/api/ping"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 10s

volumes:
  openplc-runtime-data:

Features:

  • Localhost-only access
  • Real-time scheduling capability
  • Custom plugin configuration
  • Timezone setting
  • Health check monitoring
  • Auto-restart on failure

Building Custom Images

From Source

Clone the repository and build:

git clone https://github.com/Autonomy-Logic/openplc-runtime.git
cd openplc-runtime
git checkout development

docker build -t my-openplc-runtime .

Using Build Script

bash scripts/build-docker-image.sh

Multi-Architecture Build

For building images that support multiple architectures:

docker buildx create --use
docker buildx build \
  --platform linux/amd64,linux/arm64,linux/arm/v7 \
  -t my-openplc-runtime:latest \
  --push \
  .

Development Container

For development and testing, use the development image:

bash scripts/build-docker-image-dev.sh
bash scripts/run-image-dev.sh

Features:

  • Includes test dependencies
  • Development tools installed
  • Source code mounted as volume
  • Interactive shell access

Networking

Bridge Network (Default)

Containers use Docker's default bridge network:

docker run -d \
  --name openplc-runtime \
  -p 8443:8443 \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Host Network

For direct host network access (Linux only):

docker run -d \
  --name openplc-runtime \
  --network host \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Note: Port mapping is not needed with host network.

Custom Network

Create and use a custom network:

docker network create openplc-net

docker run -d \
  --name openplc-runtime \
  --network openplc-net \
  -p 8443:8443 \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Logging

View Logs

docker logs openplc-runtime

Follow Logs

docker logs -f openplc-runtime

Limit Log Output

docker logs --tail 100 openplc-runtime

Log Drivers

Configure log driver in docker-compose.yml:

services:
  openplc-runtime:
    image: ghcr.io/autonomy-logic/openplc-runtime:latest
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

Backup and Restore

Backup Volume

# Create backup
docker run --rm \
  -v openplc-runtime-data:/data \
  -v $(pwd):/backup \
  alpine tar czf /backup/openplc-backup.tar.gz -C /data .

Restore Volume

# Restore backup
docker run --rm \
  -v openplc-runtime-data:/data \
  -v $(pwd):/backup \
  alpine tar xzf /backup/openplc-backup.tar.gz -C /data

Export Container

docker export openplc-runtime > openplc-container.tar

Import Container

docker import openplc-container.tar my-openplc-runtime:backup

Security Considerations

Container Isolation

Benefits:

  • Process isolation from host
  • File system isolation
  • Network isolation (unless host network used)

Limitations:

  • Privileged mode reduces isolation
  • Volume mounts expose host directories
  • Port exposure creates network access

Image Verification

Verify image authenticity:

docker pull ghcr.io/autonomy-logic/openplc-runtime:latest
docker inspect ghcr.io/autonomy-logic/openplc-runtime:latest

Secrets Management

Do Not:

  • Hard-code secrets in docker-compose.yml
  • Commit .env files to version control
  • Use default passwords in production

Do:

  • Use Docker secrets (Swarm mode)
  • Use environment files (.env) with proper permissions
  • Rotate secrets regularly

Network Security

Recommendations:

  • Use firewall rules to restrict access
  • Bind to localhost for local-only access
  • Use reverse proxy for additional security
  • Enable TLS certificate validation

Troubleshooting

Container Won't Start

Check logs:

docker logs openplc-runtime

Common issues:

  • Port already in use
  • Volume permission errors
  • Insufficient resources

Cannot Connect from OpenPLC Editor

Verify container is running:

docker ps | grep openplc-runtime

Check port mapping:

docker port openplc-runtime

Test connectivity:

curl -k https://localhost:8443/api/ping

Real-Time Performance Issues

Solutions:

  • Use --privileged or --cap-add=SYS_NICE
  • Increase container resources
  • Use host network mode
  • Check host system load

Volume Permission Errors

Fix permissions:

docker run --rm \
  -v openplc-runtime-data:/data \
  alpine chown -R 1000:1000 /data

Out of Disk Space

Clean up:

docker system prune -a
docker volume prune

Performance Optimization

Resource Limits

Limit container resources:

services:
  openplc-runtime:
    image: ghcr.io/autonomy-logic/openplc-runtime:latest
    deploy:
      resources:
        limits:
          cpus: '2'
          memory: 1G
        reservations:
          cpus: '1'
          memory: 512M

CPU Pinning

Pin container to specific CPU cores:

docker run -d \
  --name openplc-runtime \
  --cpuset-cpus="0,1" \
  -p 8443:8443 \
  ghcr.io/autonomy-logic/openplc-runtime:latest

Memory Optimization

docker run -d \
  --name openplc-runtime \
  --memory="1g" \
  --memory-swap="2g" \
  -p 8443:8443 \
  ghcr.io/autonomy-logic/openplc-runtime:latest

CI/CD Integration

GitHub Actions

The official images are built automatically via GitHub Actions:

Workflow: .github/workflows/docker.yml

Triggers:

  • Push to development branch
  • Pull request to development
  • Manual workflow dispatch

Process:

  1. Build multi-architecture images
  2. Run tests
  3. Push to GHCR
  4. Tag with commit SHA and latest

Using in CI/CD

Example GitHub Actions workflow:

name: Test with OpenPLC Runtime

on: [push]

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      openplc:
        image: ghcr.io/autonomy-logic/openplc-runtime:latest
        ports:
          - 8443:8443
        volumes:
          - openplc-data:/var/run/runtime
    steps:
      - uses: actions/checkout@v2
      - name: Test API
        run: |
          curl -k https://localhost:8443/api/ping