Deploy an IOC with ArgoCD#
Warning
DLS users: beamline and accelerator deployments are driven through the internal developer guide at https://dev-guide.diamond.ac.uk/epics-containers/ (the DLS cluster, Argus ArgoCD and webhooks), not these public cluster steps. Follow along on your own test cluster only.
You already deployed t02-services to the cluster with plain Helm
(Deploy an IOC with Helm): the ec K8S backend ran helm upgrade --install for
each service. That works, but nothing keeps the cluster in step with git — if a
resource drifts or a pod is deleted, it stays gone until you redeploy by hand.
This tutorial adds GitOps. You create a deployment repository from a
template, bootstrap a single ArgoCD root Application, and switch the ec
backend from K8S to ARGOCD. From that point on ec deploy only records the
desired version in git, and ArgoCD continuously reconciles the cluster to match.
By the end you will have:
a deployment repo (
t02-deployment) generated fromdeployment-template-argocd;a root ArgoCD Application (
t02) that owns a set of child Applications;the IOC
bl02t-ea-cam-01from Deploy an IOC with Helm running on your cluster, now with its desired version recorded in git and reconciled by ArgoCD.
The worked example continues the domain t02 (beamline bl02t) and the
namespace t02-beamline from the earlier cluster tutorials, tracking the
services repository t02-services you built in Create a Kubernetes Beamline.
Substitute your own names throughout.
Note
This is the GitOps deployment model. To understand how it works (the two-repository split, app-of-apps, auto-sync) before running anything, read How ArgoCD Deploys Your IOCs first.
Prerequisites#
This tutorial assumes the pieces below are already in place; it does not repeat the installation steps.
A Kubernetes cluster with ArgoCD installed, plus the
argocdCLI installed and logged in. Set up Argo CD installs ArgoCD into the cluster from Set up a Kubernetes Cluster and shows how to reach its web UI (for a self-hosted install, by port-forwarding it tohttps://localhost:8081/). Do that first.A workstation with
ec,copier,gitandkubectl— see Set up a Developer Workstation.ecmust already be installed; the generatedenvironment.shonly checks that it is present, it does not install it.A services repository to deploy from, holding the Helm charts and per-service
values.yamlfiles that define each IOC (see services repository). This tutorial uses thet02-servicesrepo you built in Create a Kubernetes Beamline and extended in Deploy an IOC with Helm; substitute your ownEC_SERVICES_REPOURL throughout.An ArgoCD project for your namespace. The
t02-beamlinenamespace already exists from Set up a Kubernetes Cluster. ArgoCD Applications are authorised per-namespace and per-project, so create an ArgoCD project allowed to deploy into it:argocd proj create t02-beamline \ -d https://kubernetes.default.svc,t02-beamline \ -s "*"
Note
The template sets each Application’s
project:field to your namespace name (thecluster_namespaceyou givecopierbelow), so the ArgoCD project is namedt02-beamlineto match.-dwhitelists the destinationcluster,namespace;-s "*"permits any source repo. Adjust the cluster URL/name if your IOCs run on a different cluster from the one hosting ArgoCD.
Scaffold a deployment repo#
Generate the deployment repo from the public
deployment-template-argocd
template with copier (use uvx copier ... if copier is not installed):
copier copy https://github.com/epics-containers/deployment-template-argocd t02-deployment
copier asks a series of questions (defined in the template’s copier.yml).
Answer them as follows for the worked example:
Prompt |
Meaning |
Worked-example answer |
|---|---|---|
|
Short name for this collection of IOCs/services. Becomes the root Application name. |
|
|
One-line repo description. |
(accept default) |
|
DNS name of your ArgoCD server, used by |
your ArgoCD server, e.g. |
|
The cluster where ArgoCD creates the Application objects. |
|
|
The cluster where the IOCs run (child |
|
|
The namespace where IOCs run, and the ArgoCD project. |
|
|
Where this repo will be hosted. |
|
|
The GitHub account/org that will own the repo. |
your GitHub account or org |
|
URL of this deployment repo. |
(accept default — |
|
URL of the services repo to track. |
|
|
Initial branch or tag of the services repo to track. |
|
|
Central log server URL (optional). |
|
Warning
The template defaults are placeholders and will not work unchanged. The two
URLs that matter most are deployment_repo (the repo you are about to push) and
services_repo (here https://github.com/<your-account>/t02-services). Set
both correctly.
Now create the git repository, commit the generated files, and push to the
remote you named in deployment_repo:
git -C t02-deployment init
git -C t02-deployment add .
git -C t02-deployment commit -m "Initial deployment repo from template"
git -C t02-deployment branch -M main
git -C t02-deployment remote add origin https://github.com/<org>/t02-deployment
git -C t02-deployment push -u origin main
Important
ArgoCD must be able to read this repo. The simplest option is a public repo.
If you use a private repo, register the credentials with ArgoCD first —
otherwise the root Application reports
ComparisonError: authentication required.
Tour the generated repo#
The deployment repo is deliberately tiny — it records only which services run and at what version; the service content lives in the services repo.
Path |
Role |
|---|---|
|
The ArgoCD root Application (“app of apps”). |
|
The control surface — the only file you (or CI) normally edit. Declares the project, destination, the services-repo source, and the |
|
A Helm chart whose only dependency is the |
|
A one-line template that expands the |
|
Sourced to set the |
The root apps.yaml (genericized):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: t02
namespace: t02-beamline
finalizers:
- resources-finalizer.argocd.argoproj.io/background
- resources-finalizer.argocd.argoproj.io/foreground
spec:
project: t02-beamline
destination:
name: in-cluster
namespace: t02-beamline
source:
path: apps
repoURL: https://github.com/<org>/t02-deployment # THIS (deployment) repo
targetRevision: main
helm:
version: v3
syncPolicy:
automated:
prune: true
selfHeal: true
And apps/values.yaml — the control surface:
project: t02-beamline
destination:
name: in-cluster
namespace: t02-beamline
source:
repoURL: https://github.com/<your-account>/t02-services # the SERVICES repo
targetRevision: main
services:
t02-epics-pvcs:
t02-epics-opis:
t02-epics-gateways:
Note
source.repoURL here points at the services repo, not this one: every
child Application sources its Helm chart from services/<service> in the
services repo. The template seeds three children — t02-epics-pvcs (shared
storage), t02-epics-opis (auto-generated OPIs) and t02-epics-gateways (a
Channel Access gateway). A bare entry like t02-epics-pvcs: inherits all the
defaults above.
For the full model behind these files — the two-repository split, the
argocd-apps library chart, and how one map becomes many Applications — see
How ArgoCD Deploys Your IOCs.
Configure your environment#
Source the generated environment.sh from the directory that contains the
deployment repo:
source ./t02-deployment/environment.sh
This sets the EC_* environment variables, enables ec shell completion, and
logs you into ArgoCD. The variables it exports are:
export EC_CLI_BACKEND="ARGOCD" # the ArgoCD continuous-deployment backend
export EC_TARGET=t02-beamline/t02 # <namespace>/<root-app-name>
export EC_SERVICES_REPO=https://github.com/<your-account>/t02-services
export EC_LOG_URL='' # central log server (empty — logging_url Skipped)
EC_SERVICES_REPO is the repo that ec deploy validates versions against; for
the full EC_* reference see edge-containers-cli.
Note
Site setup varies. The template’s environment.sh ends with a generic
argocd login <server> --grpc-web --sso. Adapt it to your own server and auth
method. For a self-hosted install reached via port-forward, that is typically
argocd login localhost:8081, logging in as admin with the password from your
ArgoCD install (see Set up Argo CD).
Check that the CLI is configured. Until you bootstrap the root Application (next
section) the target does not exist, so ec ps reports
Target 't02-beamline/t02' not found:
ec ps
That error is expected here; it confirms ec is talking to ArgoCD with the
right target. Once the root Application exists, the same command lists your
services.
Bootstrap the root Application#
This is the single manual step. From the directory that contains the deployment
repo, create the root Application from apps.yaml:
argocd app create --file t02-deployment/apps.yaml
ArgoCD creates the root Application t02, which in turn creates one child
Application per entry in apps/values.yaml. Within a moment you should see the
root plus its three seeded children:
argocd app list --app-namespace t02-beamline
NAME SYNC STATUS HEALTH
t02-beamline/t02 Synced Healthy
t02-beamline/t02-epics-pvcs Synced Healthy
t02-beamline/t02-epics-opis Synced Healthy
t02-beamline/t02-epics-gateways Synced Healthy
Note
kubectl apply -f apps.yaml is the equivalent Kubernetes-native form (apps.yaml
is a valid Application resource). It works, but relies on your direct cluster
RBAC rather than ArgoCD’s project authorisation — prefer the argocd CLI form
above.
Watch it sync in the ArgoCD web UI#
Open your ArgoCD web UI (for a port-forwarded install, https://localhost:8081/)
and filter the Applications view by project t02-beamline. You will see a card
for the root t02 and one per child; as ArgoCD reconciles, the cards turn green
(Synced / Healthy). Click a card to drill into the individual Kubernetes
resources (StatefulSets, Services, ConfigMaps) it manages — the quickest way to
diagnose a service that will not start.
Deploy a service#
Now deploy an IOC. The service bl02t-ea-cam-01 already exists in
t02-services — you added it in Deploy an IOC with Helm — so you can deploy it
directly, this time through ArgoCD. Use a git tag instead of main (for example
2026.7.1) to pin a specific version:
ec deploy bl02t-ea-cam-01 main
Here is exactly what happened, and what did not:
ecchecked thatservices/bl02t-ea-cam-01exists int02-servicesat the requested revision.ecthen committed and pushed an entry underservices.bl02t-ea-cam-01in the deployment repo’sapps/values.yaml, recording the desired version. This commit is the source of truth.ecranargocd app get --refreshto ask ArgoCD to re-read git immediately (otherwise ArgoCD notices on its next poll — every 3 minutes by default, or instantly if you have configured a git webhook).ArgoCD’s auto-sync (the
automated/prune/selfHealpolicy) then reconciles the cluster to match git.
Important
ec deploy does not run argocd app sync. Under the ArgoCD backend it only
records desired state in git and refreshes ArgoCD; the reconciliation is done
by ArgoCD’s auto-sync. To force an immediate sync (e.g. if auto-sync is paused),
use the Sync button in the web UI, or argocd app sync.
Verify with ec ps:
ec ps
name label version ready deployed
t02-epics-pvcs service main True 2026-06-25T09:10:00Z
t02-epics-opis service main True 2026-06-25T09:10:00Z
t02-epics-gateways service main True 2026-06-25T09:10:00Z
bl02t-ea-cam-01 service main True 2026-06-25T09:14:00Z
And confirm the git record — pull the deployment repo and look at
apps/values.yaml:
git -C t02-deployment pull
services:
t02-epics-pvcs:
t02-epics-opis:
t02-epics-gateways:
bl02t-ea-cam-01:
enabled: true
targetRevision: main
labels:
description: ...
A new card for bl02t-ea-cam-01 also appears in the ArgoCD web UI.
Stop, start and remove a service (optional)#
The rest of the service lifecycle is also driven through ec:
ec stop bl02t-ea-cam-01/ec start bl02t-ea-cam-01— pause or resume a service as a live ArgoCD parameter override. Add--committo also record the change inapps/values.yamlas a git audit trail.ec delete bl02t-ea-cam-01— removes the entry fromapps/values.yaml, commits and pushes; auto-sync then prunes the resources from the cluster.ec logs bl02t-ea-cam-01— stream a service’s logs.ec monitor— a terminal UI to browse and manage all services at once.
Clean up#
Because all desired state lives in git, teardown and rebuild are cheap and
reversible. Delete the root Application and, via prune plus finalizers, all of
its children are removed:
argocd app delete t02-beamline/t02 -y
Re-bootstrap any time with argocd app create --file t02-deployment/apps.yaml —
ArgoCD recreates everything from git (the persistent volume claims behind
t02-epics-pvcs are usually the slow part).
Note
The cluster is fully reconstructable from git, with one caveat: IOC autosave files held inside persistent volumes are not in git, so deleting the underlying PVCs discards that state.
Next steps#
How ArgoCD Deploys Your IOCs — the GitOps model in depth: two repositories, app-of-apps, auto-sync, and the
ecCLI’s role.Deploying with Helm (the ec backends) — deploy with pure Helm instead of ArgoCD (the manual, non-GitOps alternative).
Deploy an IOC with Helm — add and configure your own IOC instances in a services repo, ready to deploy here.
Deploy the services — the local /
docker composedeployment path, for contrast with this cluster-based CD flow.