Licence and images
Swarmd’s container images live in a private ECR registry. Your licence key is what lets your cluster pull them — and the same key is what billing-service checks to establish your subscription tier. Worth understanding before you install, because it is the one part of the chart that reaches outside your cluster, and the one failure mode that stops everything.The loop
- A pre-install Job runs before anything else. It calls
POST {licence.serverUrl}/v1/ecr-credentialswith your key in anAuthorization: Licence <key>header. - The response carries a base64
dockerConfigJson. The Job decodes it and writes akubernetes.io/dockerconfigjsonSecret —swarmd-ecr-pullby default. - That Secret is appended to
global.image.pullSecretsautomatically. You don’t wire it up; every pod gets it. - A CronJob refreshes it every 8 hours. AWS ECR tokens expire after 12, so this leaves four hours of headroom.
alpine/k8s — deliberately a public image, because it has to run before the
pull secret exists.
Failure is loud by design. If the licence server is unreachable during
pre-install, the script exits non-zero, the hook fails, and Helm rolls the
install back. You get an error instead of a namespace full of
ImagePullBackOff.Supplying the key
- From a Secret (recommended)
- Inline
What you can tune
Why the renew interval matters
Why the renew interval matters
Raising
renewIntervalHours past 12 means the token expires before the next
refresh, and every pull fails until the CronJob next runs. Existing pods keep
running — kubelet only pulls on start — so the symptom is delayed and
confusing: everything is fine until a node reboots or you scale up.Leave it at 8 unless you have a specific reason.Verification cadence and revocation
Verification cadence and revocation
verifyIntervalMinutes and cacheTtlMinutes trade licence-server load
against how long a revoked licence keeps working. With the defaults, a
revocation takes effect within an hour, and at most two.Loosen them for an air-gapped-ish deployment that can only reach the licence
server intermittently; tighten them if you need revocation to bite quickly.Mirroring into your own registry
If your cluster cannot reach ECR — or policy forbids pulling from a vendor registry — mirror the images and point the chart at your mirror:global.image.registry is prepended to every image reference that doesn’t
already carry a registry.
You still need a valid licence key: billing-service verifies it at runtime
independently of image pulls. The ECR bootstrap Job will keep trying and
failing harmlessly if it can’t reach the licence server — but plan for
offline validation with us rather than relying on that.
When it goes wrong
Every pod is ImagePullBackOff
Every pod is ImagePullBackOff
The pull Secret is missing or stale. Look at the bootstrap Job first — it’s
the only thing that creates it:If the Secret doesn’t exist, the Job never succeeded. If it exists but pulls
still fail, the token has expired — check the CronJob:Force a refresh:
'licence server returned empty dockerConfigJson'
'licence server returned empty dockerConfigJson'
The server answered, but without credentials. That means the key was accepted
as a header but rejected as a licence — expired, revoked, or not entitled to
image pulls. Check the response body in the Job logs, then talk to Swarmd.
curl: (28) Operation timed out
curl: (28) Operation timed out
No egress. The script allows 30 seconds. Your cluster needs outbound HTTPS to
api.dev.swarmd.ai — check egress policy, proxy configuration, and whether
your CNI’s NetworkPolicy allows the swarmd namespace out.Install rolled back with a pre-install hook failure
Install rolled back with a pre-install hook failure
Working as intended: the ECR Job is a
pre-install hook, so a failure aborts
before any service is created. Fix the cause and re-run helm install — there
is nothing to clean up.The platform runs, but billing shows the wrong tier
The platform runs, but billing shows the wrong tier
Image pulls and tier enforcement are separate paths. Pulls only need the key
once; billing-service verifies continuously against
licence.serverUrl. If
that URL is wrong or unreachable, the tier falls back to STARTER.Next
Databases
Where all this state actually lives.
Configuration
Secrets, app settings, and the optional components.
