Kubernetes
Target: ARM64 home lab cluster (Ubuntu snap k8s), pulling the published GHCR images — see Docker for image tags and the config-mounting convention shared with this page.
Plain, heavily-commented manifests live in the public
VectorStep-Dist repo under
k8s/ —
k8s/service/ and k8s/gateway/, each with deployment.yaml,
service.yaml, configmap.example.yaml, and pvc.yaml; k8s/service/ also
has ingress.example.yaml (below). They are copy-and-adapt templates, not a
generic chart:
git clone https://github.com/bantex01/VectorStep-Dist.gitcd VectorStep-Dist/k8sk8s/README.md
has the exact kubectl apply order and the secret setup. The Gateway’s two
tokens — admin (for whoever authors agents — a Gateway MCP client, or
curl against the write endpoints) and invoke (goes in vectorstep-secrets’
VECTORSTEP_GATEWAY_TOKEN, since the service only reads and runs agents,
never writes them) — are an input on Kubernetes, not an output: generate
both with openssl rand -hex 24 before applying anything, and the Gateway
uses them directly instead of minting its own on first boot. This is the
opposite order from a docker compose install, where the Gateway mints its
own tokens and the installer extracts them afterward. See Gateway
authentication for what each token
grants.
A Helm chart is deliberately out of scope for now: the manifests are the ground truth a chart would template, and templating them before there’s a second real user to justify it is premature.
Deploy the Gateway first, then the service — the service’s config points at
the Gateway’s Service (vectorstep-gateway:18780), and the two deploy as a
matched pair with no wire-version negotiation between them, so run matching
image tags. Every tagged image is signed and has an SBOM attached to its
GitHub release — see Verifying a
release to check what
you’re actually pulling before it reaches a manifest.
Two things worth knowing before you apply them:
- VectorStep runs
replicas: 1withstrategy: Recreate, and that’s required regardless of database backend — the scheduler is in-process and the dedup/event state is in-memory, so a second replica would double-fire scheduled pipelines. PostgreSQL doesn’t change this; it only changes whether SQLite’s single-writer limitation is also in play. See Scaling & availability for what this does and doesn’t mean, and what’s on the roadmap. - Migrations run in-process at boot (
create_tables(), see Deployment → Database) whendatabase.auto_migrateistrue(the default). Withreplicas: 1+strategy: Recreatethat’s safe and needs no init container — the old pod is fully gone before the new one starts. For a DBA-controlled cluster, setauto_migrate: falseand runkubectl exec ... alembic upgrade head(or a one-shotJob) before rolling the new image instead.
Secrets (Gateway tokens, webhook tokens, LLM provider keys) are delivered as
a Kubernetes Secret referenced via envFrom, feeding the same ${VAR}
placeholders the config uses everywhere else — never baked into the
ConfigMap or the image.
Security hardening
Section titled “Security hardening”Both deployments pass Pod Security Admission at restricted unmodified —
runAsNonRoot, a dropped capability set, no privilege escalation,
seccompProfile: RuntimeDefault, and readOnlyRootFilesystem: true. Two
things that follow from readOnlyRootFilesystem that are worth knowing
before you apply them, both already handled in the shipped manifests:
- The service’s
deployment.yamlruns a small init container that creates/data’s expected subdirectories before the main container starts. A freshly provisioned PVC is genuinely empty — unlike a Docker named volume, which gets the image’s existing content copied in automatically the first time it’s used — so without this the pod crash-loops on first boot. - The Gateway’s
deployment.yamlredirectsHOMEand thenpm/uvxcache directories onto the existing PVC. Both spawn MCP servers that download packages at runtime and write under$HOME, which is read-only under this setting otherwise; redirecting the caches onto the PVC is also a bonus, since they now persist across pod restarts instead of re-downloading every time.
k8s/networkpolicy.example.yaml — copy, edit, apply; not applied by default
— adds two ingress-only policies: the Gateway accepts its port only from
pods labelled app: vectorstep, and the service accepts its port only from
your ingress controller’s namespace and whatever namespace your webhook
senders live in. Whether either does anything depends on your cluster’s CNI
actually enforcing NetworkPolicy. See Threat
model and Securing a
deployment for the broader network
placement picture this fits into.
TLS and the Ingress
Section titled “TLS and the Ingress”service.yaml’s Service is ClusterIP — reachable in-cluster only.
k8s/service/ingress.example.yaml is the recommended way to reach it from
outside: an Ingress with a tls: block, and a commented cert-manager.io
annotation for clusters that mint certificates automatically instead of a
manually-provisioned Secret. This is where most enterprise deployments
terminate browser-facing TLS — at the ingress controller, not in the pod.
The in-cluster hop (service → Gateway) is a different story. Both
configmap.example.yaml files carry a commented server.tls block (same
shape described in Deployment → TLS),
with the mount path matching a commented tls secret volume in the
matching deployment.yaml — enable it if you want a verified wss:// hop
rather than relying on the namespace’s NetworkPolicy alone. The shipped,
working default is deliberately plain ws://vectorstep-gateway:18780/rpc:
in-cluster traffic inside a NetworkPolicy-protected namespace is a
defensible place to leave it, and the manifests don’t want to force TLS
setup just to get the quick start running. k8s/service/configmap.example.yaml
has the wss:// alternative commented alongside it.
If you need the Gateway reachable from outside the cluster too (a Gateway
MCP client, say) without changing its Service to something other than
ClusterIP, use kubectl port-forward for anything short-lived, or an
Ingress of its own — with authentication in front of it, since the
Gateway’s admin token can rewrite agent definitions. See
k8s/README.md.
Where next
Section titled “Where next”- Docker — image tags and the
/datavolume convention these manifests build on. - Deployment — the full
config.yamlreference and database/migration mechanics.