Kubernetes Deployment

This guide covers deploying Momo to a Kubernetes cluster. Example manifests are included in the deploy/examples/ directory of the repository.

Prerequisites


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=20

You should see [migrate] All migrations applied successfully.


Deployment Details

App Deployment

The deployment.yaml includes:

PostgreSQL StatefulSet

Ingress


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:

These integrate transparently with the momo-secrets Secret referenced in deployment.yaml.