DigitalOcean
DigitalOceanβ
This guide walks through deploying Styrmin on a DigitalOcean Kubernetes cluster (DOKS), wiring it up with two DigitalOcean Load Balancers (one for the Styrmin control plane, one for application traffic) and a DigitalOcean Spaces bucket for Velero backups. At the end you'll deploy Infrahub and Semaphore through Styrmin and reach them via hostname-based ingress.
The guide assumes you're installing the published Styrmin
container image and Helm chart from
registry.opsmill.io β there's no source checkout, no docker build,
and no helm dependency update step. If you need to run Styrmin from
a local source checkout (e.g. while contributing), see the
Quickstart instead.
Architectureβ
ββββββββββββββββββββββββββββββββββββββββββ
β DigitalOcean Kubernetes (DOKS) β
β β
demo.styrmin.app β ββββββββββββ ββββββββββββββββββββ β
ββFRONT_LBβββββββΊ β β Traefik β ββΊ β styrmin-server β β
β β (front) β β + agent + prefectβ β
β ββββββββββββ ββββββββββββββββββββ β
β β
*.styrmin.io β ββββββββββββ ββββββββββββββββββββ β
ββAPPS_LBββββββββΊ β β Traefik β ββΊ β Infrahub / β β
β β (apps, β β Semaphore / β¦ β β
β β per-env) β β deployments β β
β ββββββββββββ ββββββββββββββββββββ β
β β
β DO Spaces (Velero backups) β
ββββββββββββββββββββββββββββββββββββββββββ
- The front load balancer terminates traffic to
demo.styrmin.appand routes it to the Styrmin GraphQL API and frontend. - The apps load balancer terminates wildcard traffic to
*.styrmin.ioand routes it to each deployment's own Traefik ingress controller. Styrmin provisions one dedicated Traefik per environment. - Velero backs every deployment in the environment up to a single DigitalOcean Spaces bucket.
Prerequisitesβ
You need the following on your PATH:
doctlβ DigitalOcean CLIhelmβ₯ 3.8 (OCI support)kubectlstyrminctlβ the Styrmin CLI
You also need:
- A DigitalOcean account with billing enabled.
- A registered domain (this guide uses
demo.styrmin.appfor the control plane and*.styrmin.iofor applications β substitute your own).
The Styrmin container image and Helm chart are pulled from the public
OCI registry at registry.opsmill.io β no DigitalOcean-side container
registry setup is required.
1. Authenticate with DigitalOceanβ
doctl auth init
2. Create a Spaces bucket for Velero backupsβ
Velero stores backup data in an S3-compatible object store. Create a
Spaces bucket in the same region as your cluster (this guide uses
ams3).
-
Create the bucket β for example named
styrmin-veleroβ from the DigitalOcean control panel. -
Create scoped Spaces access keys with
doctl:doctl spaces keys create styrmin-velero-key \
--grants 'bucket=styrmin-velero;permission=readwrite'
Record the returned Access Key and Secret Key. You'll need them in step 10.
3. One-time infrastructure setupβ
You'll provision two load balancers and one Kubernetes cluster. The load
balancers are created before the cluster so their IDs can be passed in
when Traefik is installed β DOKS will then adopt them via the
kubernetes.digitalocean.com/load-balancer-id annotation rather than
provisioning a new one per Service.
3a. Front load balancer (Styrmin control plane)β
doctl compute load-balancer create \
--name ams3-load-balancer-styrmin-front \
--region ams3 \
--size lb-small \
--forwarding-rules entry_protocol:tcp,entry_port:80,target_protocol:tcp,target_port:80 \
--health-check protocol:http,port:80,path:/,check_interval_seconds:10,response_timeout_seconds:5,healthy_threshold:5,unhealthy_threshold:3
Record the returned ID and IP β referred to as <FRONT_LB_ID>
and <FRONT_LB_IP> below.
3b. Apps load balancer (application ingress)β
doctl compute load-balancer create \
--name ams3-load-balancer-styrmin-apps \
--region ams3 \
--size lb-small \
--forwarding-rules entry_protocol:tcp,entry_port:80,target_protocol:tcp,target_port:80 \
--health-check protocol:http,port:80,path:/,check_interval_seconds:10,response_timeout_seconds:5,healthy_threshold:5,unhealthy_threshold:3
Record the returned ID and IP β referred to as <APPS_LB_ID>
and <APPS_LB_IP> below.
3c. DNSβ
Configure the following DNS records at your registrar:
| Record | Type | Target |
|---|---|---|
demo.styrmin.app | A | <FRONT_LB_IP> |
*.styrmin.io | A | <APPS_LB_IP> |
If you use your own hostnames, substitute them in every subsequent step (including step 8 where the FQDN suffix is recorded on the Cluster).
3d. Kubernetes clusterβ
doctl kubernetes cluster create styrmin-demo \
--region ams3 \
--version 1.35.1-do.0 \
--node-pool "name=worker-pool;size=s-2vcpu-4gb;count=2"
doctl automatically writes the kubeconfig and switches your context to
the new cluster.
4. Connect to the clusterβ
kubectl get nodes # verify connectivity
You should see two Ready nodes.
5. Install Traefik (front ingress controller)β
Install Traefik as the cluster-wide ingress controller, reusing the front load balancer you created in step 3a:
helm repo add traefik https://traefik.github.io/charts
helm repo update
helm upgrade --install traefik traefik/traefik \
--create-namespace \
-n styrmin \
--set service.annotations."kubernetes\.digitalocean\.com/load-balancer-id"=<FRONT_LB_ID> \
--set ingressClass.enabled=false \
--set rbac.namespaced=true \
--wait \
--timeout 3m
The load-balancer-id annotation tells the DigitalOcean cloud
controller to attach this Service to the existing load balancer rather
than provisioning a new one.
6. Install Styrmin from the published Helm chartβ
Create a small values file with the public hostname and ingress class
for your install. Save it as do-values.yaml:
# do-values.yaml
ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: traefik
hosts:
- host: demo.styrmin.app
paths:
- path: /
pathType: Prefix
Then install Styrmin from the OCI registry. The chart already pins its
agent subchart and Bitnami dependencies, so no helm dependency update
is needed. Pin to a specific version β 0.1.24 at time of writing β or
omit --version to take the latest published release.
helm upgrade --install styrmin \
oci://registry.opsmill.io/opsmill/chart/styrmin \
--version 0.1.24 \
--create-namespace \
-n styrmin \
-f do-values.yaml \
--wait \
--timeout 5m
The chart defaults image.registry to registry.opsmill.io/opsmill,
so the cluster pulls the matching server and agent images directly from
the public registry β no image.repository override and no
imagePullSecret are required.
7. Access the Styrmin APIβ
Once Helm finishes, the GraphQL API is available through the front load balancer at:
https://demo.styrmin.app/graphql
For local debugging you can still port-forward:
kubectl port-forward svc/styrmin-server 8000:8000 -n styrmin &
From here on, every step uses styrminctl against
the public URL. Export it once:
export STYRMIN_SERVER_ADDRESS=https://demo.styrmin.app
8. Create a Cluster recordβ
Register the DOKS cluster with Styrmin. The fqdn_suffix is the wildcard
domain you configured in step 3c, and standard_storage selects the
DigitalOcean block-storage CSI driver as the default StorageClass:
styrminctl clusters create do-demo \
-c '{"fqdn_suffix": "styrmin.io", "standard_storage": {"storage_class": "do-block-storage"}}'
Record the returned id β referred to as <CLUSTER_ID> below.
9. Create an Environmentβ
The environment carries the per-environment ingress configuration. The
dedicated_ingress.service.annotations block tells the per-environment
Traefik controller (which Styrmin provisions for you) to attach to the
apps load balancer from step 3b:
styrminctl environments create demo <CLUSTER_ID> \
-c '{"ip_whitelist": ["0.0.0.0/0"], "dedicated_ingress": {"service": {"annotations": {"kubernetes.digitalocean.com/load-balancer-id": "<APPS_LB_ID>"}}}}'
Record the returned id β referred to as <ENVIRONMENT_ID> below.
Tighten the IP whitelist before production.
0.0.0.0/0is open to the public internet β use a real allowlist for non-demo workloads.
10. Create a backup storage locationβ
Register the Spaces bucket from step 2 as a backup storage location. Substitute your Spaces credentials:
styrminctl backup-locations create do-spaces \
--endpoint https://ams3.digitaloceanspaces.com \
--bucket styrmin-velero \
--region ams3 \
--access-key-id <DO_SPACES_ACCESS_KEY> \
--secret-access-key <DO_SPACES_SECRET_KEY>
Record the returned id β referred to as <BSL_ID> below.
11. Assign the backup storage location to the environmentβ
Link the BSL to the environment so every deployment in that environment backs up to the same Spaces bucket:
styrminctl backup-locations assign <ENVIRONMENT_ID> <BSL_ID>
12. Load application driversβ
The bundled driver examples are baked into the published Styrmin image
at /styrmin/drivers/. Load the ones you want to deploy:
styrminctl drivers load-local-version /styrmin/drivers/infrahub
styrminctl drivers load-local-version /styrmin/drivers/semaphore
To load a driver from a git repository (public or private) instead, see Loading a Driver.
13. Deploy Infrahubβ
styrminctl deployments create infrahub 1.6.0 <ENVIRONMENT_ID>
14. Deploy Semaphoreβ
styrminctl deployments create semaphore 2.16.47 <ENVIRONMENT_ID>
15. Monitor deploymentsβ
styrminctl deployments get <DEPLOYMENT_ID>
You can also forward the Prefect UI for a richer view of async tasks:
kubectl port-forward svc/prefect-server 4200:4200 -n styrmin &
Then open http://localhost:4200.
16. Access applications via Ingressβ
Once the deployments are running, Styrmin's per-environment Traefik controller publishes them via hostname-based routing through the apps load balancer:
- Infrahub:
http://infrahub-<DEPLOYMENT_ID>.styrmin.io - Semaphore:
http://semaphore-<DEPLOYMENT_ID>.styrmin.io
Because the apps load balancer is already wired to *.styrmin.io, no
extra DNS work is needed per deployment.
Teardownβ
Both load balancers were created outside the Kubernetes lifecycle and
adopted by annotation, so it's safe to delete the cluster without
deleting them β but only if you tell DigitalOcean explicitly. The
service.kubernetes.io/do-loadbalancer-disown annotation does that:
# Disown the load balancers so they survive the cluster delete
kubectl annotate svc --all-namespaces -l app.kubernetes.io/name=traefik \
service.kubernetes.io/do-loadbalancer-disown="true"
Once the LBs are disowned, delete the cluster:
doctl kubernetes cluster delete styrmin-demo
To fully clean up, also delete:
- the front and apps load balancers (
doctl compute load-balancer delete <id>), - the Spaces bucket and access keys,
- the DNS records.
Troubleshootingβ
Helm install hangs on Traefik. The <FRONT_LB_ID> annotation must
match a load balancer that already exists in the same region as the
cluster. Run doctl compute load-balancer list to confirm.
helm pull / helm install fails with not found on the OCI URL.
The published chart lives at
oci://registry.opsmill.io/opsmill/chart/styrmin. Confirm your Helm
version is β₯ 3.8 (helm version) β earlier releases don't speak OCI.
If you pinned --version to a release that doesn't exist, drop the
flag temporarily to take the latest, then re-pin to a real version.
do-block-storage PVCs stay Pending. Make sure the cluster has the
DigitalOcean block-storage CSI driver enabled (it is on by default for
DOKS β₯ 1.20). kubectl get sc should list do-block-storage as default.
Applications return 404 from *.styrmin.io. Confirm that DNS
resolution actually returns <APPS_LB_IP> (a stale *.styrmin.io record
will silently break routing) and that the per-environment Traefik
deployment is Running in the environment's namespace.
Velero backups fail with 403 SignatureDoesNotMatch. Re-check the
endpoint (https://<region>.digitaloceanspaces.com), the region
(ams3, nyc3, β¦), and that the access key has readwrite on the
exact bucket name.
Next stepsβ
- Platform Overview β how the pieces fit together once Styrmin is up.
- What is an Application Driver? β understand the unit you're deploying.
- Creating a Driver β write your own driver and load it onto the cluster.