How to Deploy
All environments use the Helm chart at infrastructure/helm/luckyplans/ with per-environment values files. Continuous delivery is handled by ArgoCD in production.
Architecture Overview
Traefik ingress (single public IP, host-based routing)
luckyplans.xyz -> landing:8080
docs.luckyplans.xyz -> docs:8080
beta.luckyplans.xyz -> web:3000
api.luckyplans.xyz -> api-gateway:3001
admin.luckyplans.xyz -> keycloak/argocd
api-gateway -> Redis
api-gateway -> Keycloak
api-gateway -> PostgreSQL
api-gateway -> MinIO
App services run in the luckyplans namespace.
Prerequisites
| Tool | Version | Install |
|---|---|---|
| Docker | Latest | docker.com |
| k3d | Latest | k3d.io |
| kubectl | Latest | kubernetes.io |
| Helm | >= 3.0 | helm.sh |
| cert-manager | v1.17.1 | Required for prod TLS |
| kubeseal | Latest | Required for prod sealed secrets |
Environments
| local | prod | |
|---|---|---|
| Cluster | k3d on laptop | k3d on VPS / on-premises |
| CD method | Direct Helm | ArgoCD auto-sync |
| Values file | values.yaml | values.yaml + values.prod.yaml |
| Image registry | none (k3d import) | ghcr.io |
| Image tags | latest | sha-<commit> or semver |
| Replicas | 1 | 2 |
| TLS | off | on |
Production domains
luckyplans.xyz-> landing SPAdocs.luckyplans.xyz-> docs SPAbeta.luckyplans.xyz-> web frontendapi.luckyplans.xyz-> api-gatewayadmin.luckyplans.xyz-> Keycloak and ArgoCDv0.api.luckyplans.xyz-> legacy API proxy
Local Deployment
Full deploy
pnpm deploy:local
This creates the cluster, builds images, imports them into k3d, and upgrades the Helm release.
After completion:
- Landing: http://localhost
- Product app: http://localhost/login
- Docs: http://localhost/docs
- GraphQL Playground: http://localhost/graphql
Targeted deploy
./infrastructure/scripts/deploy-local.sh landing
./infrastructure/scripts/deploy-local.sh web
./infrastructure/scripts/deploy-local.sh api-gateway web
./infrastructure/scripts/deploy-local.sh prisma-migrate
./infrastructure/scripts/deploy-local.sh --helm-only
Teardown
pnpm deploy:teardown
Status
pnpm deploy:status
Manual step-by-step
k3d cluster create luckyplans-local \
--port "80:80@loadbalancer" \
--port "443:443@loadbalancer" \
--agents 1
kubectl config use-context k3d-luckyplans-local
docker build -t luckyplans/landing:latest -f apps/landing/Dockerfile .
docker build --build-arg DOCS_APP_URL="/login" -t luckyplans/docs:latest -f apps/docs/Dockerfile .
docker build --build-arg NEXT_PUBLIC_GRAPHQL_URL="/graphql" --build-arg NEXT_PUBLIC_DOCS_URL="/docs" -t luckyplans/web:latest -f apps/web/Dockerfile .
docker build -t luckyplans/api-gateway:latest -f apps/api-gateway/Dockerfile .
docker build -t luckyplans/prisma-migrate:latest -f packages/prisma/Dockerfile .
docker pull redis:7-alpine
docker pull postgres:17-alpine
k3d image import redis:7-alpine -c luckyplans-local
k3d image import postgres:17-alpine -c luckyplans-local
k3d image import luckyplans/landing:latest -c luckyplans-local
k3d image import luckyplans/docs:latest -c luckyplans-local
k3d image import luckyplans/web:latest -c luckyplans-local
k3d image import luckyplans/api-gateway:latest -c luckyplans-local
k3d image import luckyplans/prisma-migrate:latest -c luckyplans-local
helm upgrade --install luckyplans infrastructure/helm/luckyplans \
--namespace luckyplans \
--create-namespace \
--rollback-on-failure --timeout 3m
CI/CD with ArgoCD
Production deployments follow:
Push to main -> CI -> Docker Build & Push -> Update Image Tags -> ArgoCD auto-sync -> smoke tests
Production First-Time Setup
Prerequisites
- DNS A records for
luckyplans.xyz,docs.luckyplans.xyz,beta.luckyplans.xyz,api.luckyplans.xyz,admin.luckyplans.xyz, andv0.api.luckyplans.xyz - Firewall access for ports 22, 80, and 443
- GitHub token with package read access
CD_PUSH_TOKENfor updating tagskubesealinstalled locally
Setup steps
k3d cluster create luckyplans-prod \
--port "80:80@loadbalancer" \
--port "443:443@loadbalancer" \
--agents 1
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.1/cert-manager.yaml
kubectl -n cert-manager rollout status deploy/cert-manager
./infrastructure/scripts/install-sealed-secrets.sh
git clone https://github.com/takeshi-su57/luckyplans.git
cd luckyplans
./infrastructure/scripts/install-argocd.sh --github-token <your-github-pat>
./infrastructure/scripts/seal-secrets.sh
Paste the generated encrypted values into infrastructure/helm/luckyplans/values.prod.yaml, commit, and let ArgoCD sync.
Secrets Management
Production secrets are managed with Bitnami Sealed Secrets.
Required secrets:
JWT_SECRETSESSION_SECRETWORKER_CREDENTIAL_PEPPERKEYCLOAK_CLIENT_SECRETPOSTGRES_PASSWORDKEYCLOAK_ADMIN_PASSWORDMINIO_ACCESS_KEYMINIO_SECRET_KEY
Rotate secrets by regenerating and resealing them:
./infrastructure/scripts/seal-secrets.sh
Kubernetes Security Baseline
- Keep
sealedSecrets.enabled: truein production. - Rotate session, worker, JWT, Keycloak, and MinIO secrets on a fixed schedule and after incidents.
- Restrict access to
values.prod.yaml, Sealed Secrets backups, and CI secrets. - Enforce HTTPS-only ingress and keep
certManager.enabled: true. - Keep image tags immutable.
- Use non-root containers and do not relax capability drops.
- Keep ArgoCD auto-sync as the source of truth.
Missing WORKER_CREDENTIAL_PEPPER
If the gateway fails with Missing required environment variable: WORKER_CREDENTIAL_PEPPER:
./infrastructure/scripts/seal-secrets.sh --seal-only WORKER_CREDENTIAL_PEPPER
Add the output under sealedSecrets.encryptedData in values.prod.yaml and let ArgoCD sync.
Scaling
apiGateway:
replicas: 3
landing:
replicas: 2
docs:
replicas: 2
web:
replicas: 2
Commit and push the values change. Do not use kubectl scale on ArgoCD-managed workloads.
Edge Agent Release and Rollout
CI needs these signing secrets:
EDGE_RELEASE_SIGNING_PRIVATE_KEYEDGE_RELEASE_SIGNING_KEY_ID
The gateway verifies releases with EDGE_RELEASE_SIGNING_PUBLIC_KEY.
Artifact publishing flow
The release workflow produces:
luckyplans-edge-agent-v<version>-linux-x64-service.tar.gzluckyplans-edge-agent-v<version>-win32-x64-service.zip- detached
.sigfiles SHA256SUMSmanifest.json
Release registration flow
- Build and publish edge artifacts.
- Open
manifest.json. - Register the release using
createEdgeRelease. - Confirm
edgeReleasesreturns platform-specific artifacts. - Set
targetVersionfor selected workers or start a campaign.
Registration token requirement
Edge registration requires an active enrollment token created in the Edges UI.
Recommended flow:
- Open
/edges. - Create an enrollment token.
- Copy it immediately.
- Use it during edge onboarding.
- Revoke it after onboarding completes.
Viewing Logs
kubectl -n luckyplans logs -f deployment/api-gateway
kubectl -n luckyplans logs -f deployment/web
kubectl -n luckyplans logs <pod-name> --previous
Rollback
ArgoCD-managed
git log --oneline -5
git revert <commit-sha>
git push origin main
Local
helm -n luckyplans rollback luckyplans