Installing an Ignition Designer Plugin when spinning up a Docker Container

I am doing some work with Ignition + Docker + Kubernetes and I wanted to know if it is possible to install a plugin module when spinning up an ignition container? I want to use the Tag CICD plugin to have better source control when working in Designer.

Some of this might depend on what version you're using (8.1.x or 8.3.x), but you've got a few options:

  1. Build a derived image that places the module into /usr/local/bin/ignition/user-lib/modules alongside the other built-in modules. On 8.1.x, it will always be loaded if present. On 8.3.x, it will be loaded on a first-launch (fresh data volume) and treated as a built-in module (only able to be disabled, not uninstalled).
  2. Use an init container to retrieve the module and place accordingly prior to launch of the main Ignition container. Again, your approach may vary based on version--Ignition 8.3.x uses a data/modules.json to define which modules should be loaded/enabled on startup.
  3. On Docker only, you can simply bind-mount the module file directly into place.

With Ignition 8.3.x, you also have new environment variables such as ACCEPT_MODULE_LICENSES and ACCEPT_MODULE_CERTS to automate auto-acceptance of third-party modules. If you're needing to integrate third-party modules in 8.1.x, you might find useful information in my ignition-derived-example repo.

Let me know if you'd like more information on any of these options based on what you think you need.

Thank you, the links and this information is really helpful.

Currently I have a docker compose file that is copying the third party module in along with updating the modules.json, so that ignition will be-able to detect it. The issue I am running into now is ignition is not automatically accepting the cert and license even though I am passing an environment variable with the module identifier. What do i need to do to automatically accept the licenses and certs?

services:
  io-gateway:
    image: inductiveautomation/ignition:8.3.1
    container_name: mqtt-gw
    user: "0:0"
    ports:
      - "8088:8088"
      - "8043:8043"
    environment:
      ACCEPT_IGNITION_EULA: "Y"
      IGNITION_EDITION: "standard"
      DISABLE_QUICKSTART: "true"
      ACCEPT_MODULE_LICENSES: "com.cirruslink.mqtt.transmission.gateway"
      ACCEPT_MODULE_CERTS: "com.cirruslink.mqtt.transmission.gateway"

    command: ["-n", "io-gw","-m","2048","--","wrapper.java.initmemory=2048"]

    volumes:
      - ./modules/MQTT-Transmission-signed.modl:/usr/local/bin/ignition/data/local/modl/MQTT-Transmission-signed.modl
      - ./modules/modules.json:/usr/local/bin/ignition/data/modules.json

    restart: unless-stopped

First thing I'll mention is that we @hunterdg observed a documentation error with respect to the location of user-installed modules in the Ignition 8.3 Docker image. The correct location is /usr/local/bin/ignition/data/var/ignition/modl. Modules in this location will be automatically enabled on a fresh launch.

That said, if you're bind-mounting the module into place, you actually probably want to just put it in the standard location under /usr/local/bin/ignition/user-lib/modules since it is by-nature unremovable with that bind-mount.

Nonetheless, I'm guessing that something in the modules.json that you're linking into place is interfering with the auto-accept. Try making the location adjust above and removing the bind-mount of modules.json.

After changing the module path, ignition isn’t detecting the module. I tried composing with both /usr/local/bin/ignition/data/var/ignition/modl and /usr/local/bin/ignition/user-lib/modules and ignition still wouldn’t detect it. I checked the container with docker exec -it mqtt-gw /bin/bash and I can see the file is inside the container. The reason I was copying in the modules.json was to automatically add a reference to the third party module. That worked with getting ignition to detect it, but it didn’t work for automatically accepting the licenses and certs.

Is the compose snippet you posted complete? I didn't see a data volume, which means that it should integrate any of the .modl files from either of those folders on first-launch (since there won't be a pre-existing data/modules.json).

Ahh the compose snippet above is a previous version. this is the snippet I’ve been using:

services:
  io-gateway:
    image: inductiveautomation/ignition:8.3.1
    container_name: mqtt-gw
    user: "0:0"
    ports:
      - "8088:8088"
      - "8043:8043"
    environment:
      ACCEPT_IGNITION_EULA: "Y"
      IGNITION_EDITION: "standard"
      DISABLE_QUICKSTART: "true"
      ACCEPT_MODULE_LICENSES: "com.cirruslink.mqtt.transmission.gateway"
      ACCEPT_MODULE_CERTS: "com.cirruslink.mqtt.transmission.gateway"
    command: ["-n", "io-gw","-m","2048","--","wrapper.java.initmemory=2048"]
    volumes:
      - ./io/mqtt/data:/usr/local/bin/ignition/data
      - ./modules/MQTT-Transmission-signed.modl:/usr/local/bin/ignition/user-lib/modules/MQTT-Transmission-signed.modl

    restart: unless-stopped

When I removed the data volume reference from the compose and ignition did detect and verify the custom module.

For anyone else that is looking for a solution to adding Third-Party modules, here is the docker-compose that worked for me.

services:
  io-gateway:
    image: inductiveautomation/ignition:8.3.1
    container_name: mqtt-gw
    user: "0:0"
    ports:
      - "8088:8088"
      - "8043:8043"
    environment:
      ACCEPT_IGNITION_EULA: "Y"
      IGNITION_EDITION: "standard"
      DISABLE_QUICKSTART: "true"
      ACCEPT_MODULE_LICENSES: "com.cirruslink.mqtt.transmission.gateway"
      ACCEPT_MODULE_CERTS: "com.cirruslink.mqtt.transmission.gateway"
    command: ["-n", "io-gw","-m","2048","--","wrapper.java.initmemory=2048"]
    volumes:
      - ./io/mqtt/data:/usr/local/bin/ignition/data
      - ./modules/MQTT-Transmission-signed.modl:/usr/local/bin/ignition/user-lib/modules/MQTT-Transmission-signed.modl

    restart: unless-stopped

in OrbStack on mac, bind-mounting to this errors with the following. I get it, because the bind is creating the path with different permissions than the gateway init, but is that a bug?

The Ignition Gateway has failed to successfully start.
Reason: java.nio.file.AccessDeniedException: /usr/local/bin/ignition/data/var/ignition/designer. Check the logs for more details.
  volumes:
    - ./modules:/usr/local/bin/ignition/data/var/ignition/modl
    - ignition-data:/usr/local/bin/ignition/data

In your docker-compose do you set your user to root? user: "0:0"

well that "fixes" the issue, but no, I run it unprivileged and have sidecar containers that handle the unprivileged nature via this:

  user: "2003:2003" # match ignition container
  group_add:
    - 65533 # allow writing to /git

this wouldn't be the first time I've seen behavior differ between OrbStack and Linux (specifically with permission mapping), but in this case I think it's not the culprit. I've always been curious why only parts of the container file system are in the image vs generated on init, so leaning toward this just not being fully "baked" :laughing: (e.g. data/config/resources exists at boot (understandably) but my sidecar must sleep while waiting for data/projects)

FYI, you don't need user: "0:0" if you mount - ./modules/MQTT-Transmission-signed.modl:/usr/local/bin/ignition/user-lib/modules/MQTT-Transmission-signed.modl like in your last example.

I just want to make use of the other path

I tried this simple Compose stack on both Docker Desktop and OrbStack and didn't encounter any issues:

services:
  gateway:
   image: inductiveautomation/ignition:8.3.6
   ports:
     - 9088:8088
   env_file: ~/Docker/basic.env
   command: >
     -n forum-113547
   volumes:
     - ./modules:/usr/local/bin/ignition/data/var/ignition/modl
     - ignition-data:/usr/local/bin/ignition/data

volumes:
  ignition-data:

As to why certain paths exist within the image, they're there to enable bind-mounting to direct subfolders under those paths. If you bind-mount to a folder whose parent folders don't exist in the image, those interim folders are [unfortunately] created with root ownership and thus cause permissions issues.

heh. looks like 8.3.6 fixed it, but doesn't seem to be explcitly called out in the release notes.

regarding file system - I'm more curious why some paths (like /data/projects) aren't in the image. I feel like I may have answered that question myself at some point when poking around in docker-entrypoint.sh, but maybe_seed_data_volume() doesn't seem to be the culprit after another quick look

(I can't mount two named volumes separately to data/config and data/projects)

also FYI the 8.3 "Warning" blurb has changed since 8.3 - it now points to the 8.1 upgrade guide

8.3:
WARNING: If you are upgrading to Ignition 8.3, you must read the upgrade guide first before installing Ignition 8.3. Visit the following URL for more information before installing: Ignition 8.1 to 8.3 Upgrade Guide

8.4+:
WARNING: If you are upgrading to Ignition 8 from any previous version of Ignition you must read the upgrade guide first before installing Ignition 8. Visit the following URL for more information before installing: Ignition 8 Upgrade Guide

:grin: - Merging wrapper arguments in Docker - #5 by hunterdg

Yeah, so somehow I completed missed your reply in that thread.. :sorry: .. Glad it got sorted in the end though :person_shrugging:. I fixed my comment above :smiley: