Helm Deployment Architecture
Overview
The platform uses a single Helm chart (infrastructure/helm/luckyplans/) to deploy all services across two k3d environments.
| Environment | Cluster | Image Registry | Image Tag |
|---|---|---|---|
| local | k3d on laptop | none (k3d import) | latest |
| prod | k3d on VPS / on-premises | ghcr.io | sha-<commit> (CI) / semver |
Services
| Service | Type | Port | Transport |
|---|---|---|---|
web | Next.js frontend | 3000 | HTTP |
api-gateway | NestJS GraphQL | 3001 | HTTP |
service-core | NestJS microservice | — | Redis RPC |
redis | Message broker / cache | 6379 | Redis protocol |
service-core communicates exclusively via Redis message patterns and has no Kubernetes Service resource.
Chart Structure
infrastructure/helm/luckyplans/
Chart.yaml
values.yaml # defaults + local k3d config
values.prod.yaml # prod overrides (merged on top of values.yaml)
templates/
_helpers.tpl
namespace.yaml
configmap.yaml # luckyplans-config
secret.yaml # luckyplans-secrets (auto-generates JWT_SECRET)
ingress.yaml # Traefik ingress
cluster-issuer.yaml # cert-manager ClusterIssuer (prod only)
middleware-redirect.yaml # Traefik HTTPS redirect + HSTS (prod only)
pdb.yaml # PodDisruptionBudgets
smoke-test-job.yaml # ArgoCD post-sync smoke tests (prod only)
redis/
api-gateway/
service-core/
web/
Key Design Decisions
1. Redis: plain template, not Bitnami subchart
Redis runs as a simple redis:7-alpine Deployment + ClusterIP Service. If you migrate to managed Redis later, remove the redis/ templates and point config.redisHost at the managed host.
2. NEXT_PUBLIC_GRAPHQL_URL is a build-time constraint
Next.js bakes NEXT_PUBLIC_* variables into the JavaScript bundle at docker build time — Helm cannot inject this at helm upgrade time.
Changing web.buildArgs.graphqlUrl in a values file only takes effect if you rebuild and redeploy the web image.
3. Namespace is chart-managed with keep annotation
The chart creates the luckyplans namespace annotated with helm.sh/resource-policy: keep, preventing helm uninstall from deleting the namespace and any PVCs or secrets inside it.
4. Image registry prefix is conditional
| env | image.registry | rendered |
|---|---|---|
| local | "" | luckyplans/api-gateway:latest |
| prod | ghcr.io | ghcr.io/takeshi-su57/api-gateway:sha-abc1234 |
Environment Differences
| Key | local | prod |
|---|---|---|
config.nodeEnv | development | production |
config.corsOrigin | http://localhost | https://luckyplans.xyz |
ingress.host | "" (any) | luckyplans.xyz |
ingress.tls.enabled | false | true |
image.registry | "" | ghcr.io |
| image tags | latest | sha-<commit> |
| replicas | 1 | 2 |
Deployment Commands
Local (laptop)
pnpm deploy:local
# Builds images, imports into k3d, helm upgrade --install with values.yaml
Prod (ArgoCD GitOps)
Prod is managed by ArgoCD — do not run helm upgrade directly.
See ArgoCD and Deployment Guide.
Teardown (local only)
pnpm deploy:teardown
Useful Commands
# Lint the chart
helm lint infrastructure/helm/luckyplans/
# Render templates locally (dry run)
helm template luckyplans infrastructure/helm/luckyplans/
# Check release status
pnpm deploy:status
# View what Helm has deployed
helm -n luckyplans get values luckyplans
helm -n luckyplans history luckyplans
# Verify health
curl -X POST http://localhost/graphql \
-H "Content-Type: application/json" \
-d '{"query":"{ health }"}' | jq .