Kubernetes Deployment
This guide covers deploying Momo to a Kubernetes cluster. Example manifests are included in the deploy/examples/ directory of the repository.
Prerequisites
- A working Kubernetes cluster (1.25+)
kubectlconfigured and pointing at your cluster- ingress-nginx installed in your cluster
- cert-manager installed with a
ClusterIssuernamedletsencrypt-prod - A domain name pointing at your clusterβs ingress IP
Manifest Overview
| File | Purpose |
|---|---|
namespace.yaml |
Creates the momo namespace |
postgres-statefulset.yaml |
PostgreSQL 18 StatefulSet with a persistent volume |
secret.example.yaml |
Template for all required secrets β fill in and apply, never commit |
deployment.yaml |
App deployment with 2 replicas, liveness/readiness probes, non-root security context |
service.yaml |
ClusterIP service exposing the app on port 3000 |
ingress.yaml |
Ingress with TLS via cert-manager |
Step-by-Step Deployment
Step 1 β Create the namespace
kubectl apply -f deploy/examples/namespace.yaml
Step 2 β Deploy PostgreSQL
kubectl apply -f deploy/examples/postgres-statefulset.yaml
Wait for the database to be ready:
kubectl rollout status statefulset/momo-postgres -n momo
Step 3 β Create secrets
Copy the example secret template and fill in real values:
cp deploy/examples/secret.example.yaml secret.yaml
# Edit secret.yaml β fill in every CHANGE_ME value
The required fields in secret.yaml:
stringData:
DATABASE_URL: "postgresql://momo:yourpassword@momo-postgres:5432/momo"
AUTH_SECRET: "generate with: openssl rand -base64 32"
AUTH_TRUST_HOST: "true"
NEXT_PUBLIC_APP_URL: "https://momo.example.com"
NEXTAUTH_URL: "https://momo.example.com"
# At least one OAuth provider
GITHUB_CLIENT_ID: ""
GITHUB_CLIENT_SECRET: ""
# VAPID keys for push notifications
NEXT_PUBLIC_VAPID_PUBLIC_KEY: ""
VAPID_PRIVATE_KEY: ""
VAPID_CONTACT: "mailto:admin@example.com"
# Cron protection
CRON_SECRET: "generate with: openssl rand -hex 32"
Important:
AUTH_TRUST_HOST: "true"is required for Kubernetes β Auth.js v5 rejects requests from unrecognised hosts unless this is set.
Apply the secret:
kubectl apply -f secret.yaml -n momo
Delete the file immediately after applying β never commit it:
rm secret.yaml
Step 4 β Update the ingress domain
Edit deploy/examples/ingress.yaml and replace momo.example.com with your actual domain:
spec:
tls:
- hosts:
- momo.example.com # change this
secretName: momo-tls
rules:
- host: momo.example.com # change this
Step 5 β Deploy the application
kubectl apply -f deploy/examples/deployment.yaml
kubectl apply -f deploy/examples/service.yaml
kubectl apply -f deploy/examples/ingress.yaml
Step 6 β Verify
kubectl get pods -n momo
kubectl get ingress -n momo
The app should be accessible at https://momo.example.com once cert-manager provisions the TLS certificate (usually within 1-2 minutes).
Migrations run automatically. The container runs all pending database migrations before the Next.js server starts on every pod start. Check the logs to confirm:
kubectl logs -n momo deployment/momo-app --tail=20You should see
[migrate] All migrations applied successfully.
Deployment Details
App Deployment
The deployment.yaml includes:
- 2 replicas with pod anti-affinity to spread across nodes
- Resource limits: 512Mi memory, 500m CPU per pod
- Liveness probe: GET
/api/healthevery 30s β restarts the container if it fails 3 times - Readiness probe: GET
/api/healthevery 10s β removes the pod from the service if it fails - Non-root security context: runs as user
1001(nextjs) - All secrets injected via
envFrom: secretRef: name: momo-secrets
PostgreSQL StatefulSet
- PostgreSQL 18 with a persistent volume claim
- Data is stored in a
PersistentVolumeClaimβ survives pod restarts - Accessible within the cluster at
momo-postgres:5432
Ingress
- Requires ingress-nginx and cert-manager
- cert-manager automatically provisions a Letβs Encrypt certificate
- All HTTP traffic is redirected to HTTPS
Updating Momo
To deploy a new version:
# Pull the latest image
kubectl set image deployment/momo-app momo=ghcr.io/jp1337/momo:latest -n momo
# Or trigger a rollout if using imagePullPolicy: Always
kubectl rollout restart deployment/momo-app -n momo
Migrations for any new schema changes run automatically when the pods restart.
Rotating the AUTH_SECRET
kubectl patch secret momo-secrets -n momo \
--type=merge \
-p '{"stringData":{"AUTH_SECRET":"your-new-secret"}}'
kubectl rollout restart deployment/momo-app -n momo
All existing user sessions will be invalidated and users will need to sign in again.
Scaling
To increase the number of replicas:
kubectl scale deployment/momo-app --replicas=3 -n momo
Momo is stateless β all state is in PostgreSQL. You can scale horizontally without any additional configuration.
Viewing Logs
# All app pods
kubectl logs -n momo -l app=momo --tail=100 -f
# Specific pod
kubectl logs -n momo <pod-name> --tail=100 -f
Secrets Management (Advanced)
For production clusters, consider using a dedicated secrets solution instead of plain Kubernetes Secrets:
- Sealed Secrets β encrypt secrets before committing to git
- External Secrets Operator β sync from AWS Secrets Manager, Vault, etc.
- HashiCorp Vault β full secrets management solution
These integrate transparently with the momo-secrets Secret referenced in deployment.yaml.