This folder contains the Kubernetes objects for the whole stack, grouped by kind:
| File | Objects |
|---|---|
namespace.yaml |
exstream Namespace |
secrets.yaml |
exstream-jwt Secret (shared JWT_SECRET) |
configmaps.yaml |
web-nginx ConfigMap (SPA-only nginx config) |
volumes.yaml |
PersistentVolumeClaims: auth-data, playlist-data, file-data |
deployments.yaml |
Deployments: auth, playlist, file, forward-auth, web |
services.yaml |
ClusterIP Services for each Deployment |
hpa.yaml |
HorizontalPodAutoscalers for web and forward-auth |
ingressroute.yaml |
Traefik Middleware (ForwardAuth) + IngressRoute |
- A Kubernetes cluster (k3s, kind, minikube, or managed) and
kubectlpointed at it. - Traefik v3 with the
kubernetesCRDprovider — the routing uses Traefik'sIngressRoute/MiddlewareCRDs. k3s ships Traefik by default; elsewhere install it with Helm:helm install traefik traefik/traefik. - The IngressRoute binds to Traefik's
webentrypoint (port 80/8000 by default). - metrics-server for the HPAs (
kubectl top nodesshould work). - A default StorageClass for the PersistentVolumeClaims.
- A container registry the cluster can pull from.
The manifests reference ghcr.io/code-by-sia/exstream-<component>:latest.
Replace the prefix with your own registry if it differs (one sed across
deployments.yaml), then build from the repository root:
REGISTRY=ghcr.io/code-by-sia
docker build -f auth/Dockerfile -t $REGISTRY/exstream-auth:latest .
docker build -f playlist/Dockerfile -t $REGISTRY/exstream-playlist:latest .
docker build -f file/Dockerfile -t $REGISTRY/exstream-file:latest .
docker build -f api/Dockerfile -t $REGISTRY/exstream-forward-auth:latest api
docker build -t $REGISTRY/exstream-web:latest web
docker push $REGISTRY/exstream-auth:latest
docker push $REGISTRY/exstream-playlist:latest
docker push $REGISTRY/exstream-file:latest
docker push $REGISTRY/exstream-forward-auth:latest
docker push $REGISTRY/exstream-web:latestThe web build uses the Dockerfile's default (production) target: Vite build
served by nginx. The Xi service images install the Xi toolchain via Homebrew —
if the code-by-sia/x tap is private, make sure Docker has GitHub SSH auth
(see the root README).
For local clusters you can skip the registry: kind load docker-image … or
k3s ctr images import … after building.
kubectl apply -f deploy/namespace.yaml
kubectl -n exstream create secret generic exstream-jwt \
--from-literal=JWT_SECRET="$(openssl rand -hex 32)"secrets.yaml carries a change-me placeholder so the manifests are complete;
prefer creating the secret out of band as above (skip applying secrets.yaml),
or edit the value before applying. auth (token issuer) and forward-auth
(token verifier) must see the same value.
kubectl apply -f deploy/configmaps.yaml
kubectl apply -f deploy/volumes.yaml
kubectl apply -f deploy/deployments.yaml
kubectl apply -f deploy/services.yaml
kubectl apply -f deploy/hpa.yaml
kubectl apply -f deploy/ingressroute.yaml(Or simply kubectl apply -f deploy/ once the secret exists — files apply in
alphabetical order, which works fine here.)
Wait for the rollout:
kubectl -n exstream get pods -wAll traffic enters through Traefik's web entrypoint. Depending on how Traefik
is exposed:
# k3s / LoadBalancer: use the external IP of the traefik service
kubectl -n kube-system get svc traefik
# or port-forward Traefik locally
kubectl -n kube-system port-forward svc/traefik 8080:80Then open http://localhost:8080 — the SPA is served at /, the APIs under
/auth, /playlists, /music/search, /file. The auth service seeds the
admin/admin123 and test/test123 development users on first start
(backed by the auth-data volume, so they survive restarts).
Smoke test through the ingress:
curl -X POST http://localhost:8080/auth/login \
-H 'Content-Type: application/json' \
-d '{"username":"test","password":"test123"}'webandforward-authare stateless; their HPAs scale 1–5 replicas at 70% average CPU. TuneminReplicas/maxReplicasinhpa.yaml.auth,playlist, andfilepersist data as flat files onReadWriteOncevolumes and usestrategy: Recreate; keep them at one replica each. To scale them, move their storage to a shared backend (database/object store) first.
kubectl delete namespace exstreamThe PVCs (and their data) are deleted with the namespace; back up the volumes first if you need the libraries to survive.