Deploy an IOC with Helm#

Warning

DLS users: beamline and accelerator IOCs are deployed through the internal developer guide at https://dev-guide.diamond.ac.uk/epics-containers/, not these public cluster steps. Follow along on your own test cluster only.

This is your first deployment of your own IOC onto the cluster, using plain Helm. In Create a Kubernetes Beamline you created the services repo t02-services and deployed its shared services with the ec K8S backend (which runs helm upgrade --install under the hood). Now you add your own IOC — a simulated area detector, much like the one built in Create an IOC Instance, but defined for a Helm services repo — and deploy it the same way, with ec deploy. Substitute your own names throughout.

By the end you will have a new IOC instance folder in your services repo, committed, pushed and running on the cluster. Later, Deploy an IOC with ArgoCD puts this same repo under GitOps control with ArgoCD.

Add a new instance folder#

An IOC instance is a folder under services/. Its folder name is the IOC name, and it holds two things:

Item

Purpose

values.yaml

Helm values for the ioc-instance chart — chiefly which container image to run.

config/

The IOC configuration, mounted into the container as a Kubernetes ConfigMap. Normally a single ibek file, ioc.yaml.

Your services repo ships a .ioc_template skeleton to copy. From the repo root:

cd t02-services
cp -r services/.ioc_template services/bl02t-ea-cam-01
code .

Note

DLS users: module load vscode first, then code ..

The skeleton’s Chart.yaml and templates/ are symlinks into the shared .helm-shared/ chart, so every instance reuses the same ioc-instance chart — you never edit them.

Set the image — values.yaml#

Edit services/bl02t-ea-cam-01/values.yaml and replace the placeholder image with the SimDetector Generic IOC:

# yaml-language-server: $schema=../../.helm-shared/values.schema.json

ioc-instance:
  image: ghcr.io/epics-containers/ioc-adsimdetector-runtime:2.11ec3

ioc-instance: is the key the chart imports its values under. Shared defaults (such as hostNetwork: true) come from the repo-root services/values.yaml, so a per-instance file normally only overrides what differs — usually just image.

Configure the IOC — config/ioc.yaml#

An ibek ioc.yaml is a list of entities; each instantiates a model contributed by a support module. The Generic IOC bakes in the support it can instantiate, so nothing extra is downloaded here. Open services/bl02t-ea-cam-01/config/ioc.yaml and replace its body:

# yaml-language-server: $schema=../ioc.schema.json

ioc_name: "{{ _global.get_env('IOC_NAME') }}"

description: An IOC that simulates an area detector

entities:
  - type: epics.EpicsEnvSet
    name: EPICS_TZ
    value: GMT0BST

  - type: devIocStats.iocAdminSoft
    IOC: "{{ ioc_name | upper }}"

  - type: ADSimDetector.simDetector
    PORT: DET.DET
    P: BL02T-EA-CAM-01
    R: ":DET:"

  - type: ADCore.NDStdArrays
    PORT: DET.ARR
    P: BL02T-EA-CAM-01
    R: ":ARR:"
    NDARRAY_PORT: DET.DET
    TYPE: Int8
    FTVL: CHAR
    NELEMENTS: 1048576

This makes a simulation detector with PV prefix BL02T-EA-CAM-01:DET: and a Standard Arrays plugin that publishes its image over Channel Access.

Note

YAML indentation is significant: each - type: starts a new entity, and a value that begins with : must be quoted (as in R: ":DET:").

The schema line points at ../ioc.schema.json. Generate it once so VSCode (with the Red Hat YAML extension) offers completion and validation for exactly the entities this image provides:

ibek pattern schema services/bl02t-ea-cam-01

ibek fetches the published schema for your pinned image and writes services/bl02t-ea-cam-01/ioc.schema.json. To learn where these entity models come from and build your own, see Create a Generic IOC.

Vendor extra runtime support (optional)#

Some IOCs need runtime inputs that are not compiled into the image — StreamDevice protocol files, EPICS DB templates, and the ibek models that describe them. Vendor them into the instance’s config/ from a central pattern library:

ibek pattern add <library>:<pattern>@<tag> services/bl02t-ea-cam-01

This copies the files into config/, records each one with a pinned version and sha256 hash in services/bl02t-ea-cam-01/runtime-lock.yaml, and refreshes ioc.schema.json so the new entities validate too. Because config/ is mounted as the ConfigMap, keep its total content under 1 MiB. ibek pattern check verifies the vendored files still match the lock. The simulated detector above needs none of this, so you can skip the section.

Deploy it#

Make sure your environment is configured for the K8S backend — source the repo’s environment.sh (see Create a Kubernetes Beamline), which sets EC_CLI_BACKEND=K8S. Then commit your new instance and push it to the services repo that ec deploys from (EC_SERVICES_REPO):

git add services/bl02t-ea-cam-01
git commit -m "Add bl02t-ea-cam-01 IOC"
git push

Now deploy it (-v prints the underlying helm/kubectl commands):

ec -v deploy bl02t-ea-cam-01 main

Under the K8S backend ec deploy does not go through ArgoCD or a deployment repo. Instead it:

  • clones t02-services at the requested revision (main here);

  • packages services/bl02t-ea-cam-01 as a Helm chart, applying the shared services/values.yaml and the instance values.yaml;

  • runs helm upgrade --install bl02t-ea-cam-01 ... --namespace t02-beamline directly against the cluster.

Use a git tag instead of main (for example ec deploy bl02t-ea-cam-01 2026.7.1) to pin a specific version, exactly as you tagged and deployed the shared services in Create a Kubernetes Beamline.

Confirm it is running, then inspect it:

ec ps                          # list services in your namespace
ec logs bl02t-ea-cam-01        # stream the IOC's container logs
 name                version   ready   deployed
 t02-epics-pvcs      2026.7.1  True    2026-07-01T09:10:00Z
 t02-epics-opis      2026.7.1  True    2026-07-01T09:11:00Z
 bl02t-ea-cam-01     main      True    2026-07-01T09:20:00Z

Pause and resume the IOC without removing it:

ec stop bl02t-ea-cam-01
ec start bl02t-ea-cam-01

See edge-containers-cli for the full ec command and environment-variable reference.

Next steps#