Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HH-Lion Deployment Setup Guide

Step-by-step instructions to deploy hh-lion to engineering (DigitalOcean DOKS) and production (AWS EKS).


Table of Contents

  1. Architecture Overview
  2. Prerequisites
  3. Kubernetes Cluster Setup
  4. GitHub Environments Setup
  5. GitHub Repository Secrets
  6. GitHub Environment Variables (per env)
  7. GitHub Environment Secrets (per env)
  8. AWS OIDC Setup (Production)
  9. Create Kubernetes Namespace
  10. Trigger a Deployment
  11. Verify Deployment
  12. Troubleshooting

1. Architecture Overview

┌─────────────┐    push develop     ┌──────────────────┐
│  Developer   │ ──────────────────► │  GitHub Actions   │
│  / GitHub    │    or manual        │  deploy.yml       │
└─────────────┘    dispatch          └────────┬─────────┘
                                              │
                    ┌─────────────────────────┼──────────────────────────┐
                    │ Engineering                  │ Production               │
                    ▼                          ▼                          │
           ┌──────────────┐          ┌──────────────┐                    │
           │ DOCR Registry│          │ GHCR Registry│                    │
           └──────┬───────┘          └──────┬───────┘                    │
                  │                          │                            │
           ┌──────▼───────┐          ┌──────▼───────┐                    │
           │ DigitalOcean │          │   AWS EKS    │                    │
           │   DOKS       │          │   (OIDC)     │                    │
           └──────────────┘          └──────────────┘                    │
                                                                         │
  • Engineering: Auto-deploys on push to develop branch. Images pushed to DigitalOcean Container Registry (DOCR).
  • Production: Manual workflow_dispatch only. Images pushed to GitHub Container Registry (GHCR). Auth via OIDC.

2. Prerequisites

Before starting, ensure you have:

  • A DigitalOcean account with a managed Kubernetes cluster (DOKS) for engineering
  • An AWS account with an EKS cluster for production
  • GitHub repository hungryhub-team/hh-lion with admin access
  • DigitalOcean CLI (doctl) installed locally (optional, for debugging)
  • AWS CLI (aws) installed locally (optional, for debugging)
  • kubectl installed locally

3. Kubernetes Cluster Setup

Engineering (DigitalOcean DOKS)

  1. Create a DOKS cluster via the DigitalOcean console.
  2. Note the Cluster ID (visible in the cluster URL or via doctl kubernetes cluster list).
  3. Create a DigitalOcean Container Registry (DOCR) if you don’t have one.
  4. Note the Registry URL (e.g., registry.digitalocean.com/hungryhub).

Production (AWS EKS)

  1. Create an EKS cluster via AWS Console or Terraform.
  2. Note the cluster name and region.
  3. Set up OIDC federation (see Section 8).

3a. Optional: Deploy MLflow in Kubernetes (Engineering + Production)

Serving mode in engineering and production requires a real model reference (FORCE_MOCK_MODEL=false and valid MLFLOW_MODEL_NAME + MLFLOW_MODEL_ALIAS). The app needs a reachable MLflow tracking server. This repo now includes:

For copy-paste engineering bootstrap commands (from empty data to non-mock deploy), see Engineering Bootstrap Command Sheet.

For production training-data bootstrap from booking_production, see Production Training Bootstrap Runbook.

  • k8s/engineering/mlflow.yaml
  • k8s/production/mlflow.yaml

Each env-specific manifest defines the Deployment/Service/Ingress only and expects:

  • mlflow-config ConfigMap
  • mlflow-secrets Secret

There is no shared root k8s/mlflow.yaml template anymore; k8s/engineering/mlflow.yaml and k8s/production/mlflow.yaml are the canonical manifests for cluster deployments.

You can create those manually (see k8s/README.md) or let GitHub Actions create/update them from environment vars and secrets.

Apply per environment after editing placeholders:

# Engineering cluster
kubectl apply -f k8s/engineering/mlflow.yaml

# Production cluster
kubectl apply -f k8s/production/mlflow.yaml

Then set hh-lion environment values:

  • MLFLOW_TRACKING_URI -> your MLflow URL (Ingress host or internal service URL)
  • MLFLOW_MODEL_NAME + MLFLOW_MODEL_ALIAS -> preferred registered-model reference for serving (for example homepage_two_tower + production)

The hh-lion serving app no longer needs direct object-store credentials to download model artifacts when the MLflow server runs in artifact-proxy mode. The MLflow server still needs its own R2/S3 credentials.

Aliases used for serving should resolve to model versions logged through the proxied MLflow server. If an alias still points at an older run whose artifact URI is a direct object-store path such as s3://..., the deploy workflow now fails early and you should re-log or re-register that model through the current MLflow deployment before rolling out serving.

To let GitHub Actions deploy MLflow automatically, also set:

  • Env var MLFLOW_DEPLOY_ENABLED=true
  • Env var MLFLOW_ARTIFACT_ROOT (e.g. s3://hh-lion-bucket-engineering/mlflow/artifacts)
  • Optional env var MLFLOW_AWS_REGION (defaults to auto for R2)
  • Optional env var MLFLOW_WORKERS (defaults to 1 engineering / 2 production)
  • Optional env var MLFLOW_HOSTNAME (overrides ingress host in the manifest)
  • Optional env var MLFLOW_SERVER_ALLOWED_HOSTS (for MLflow host header protection; e.g. mlflow-engineering.hh-engineering.my.id)
  • Optional env var MLFLOW_SERVER_CORS_ALLOWED_ORIGINS (for browser access; e.g. https://mlflow-engineering.hh-engineering.my.id)
  • Env secret MLFLOW_BACKEND_STORE_URI (Postgres connection URI)

Serving access is configured per environment in the serving manifest:

  • k8s/engineering/serving.yaml is internal-only behind a ClusterIP service
  • k8s/production/serving.yaml is internal-only behind a ClusterIP service

4. GitHub Environments Setup

Go to GitHub → Repository → Settings → Environments and create two environments:

EnvironmentProtection Rules (Recommended)
engineeringNone (auto-deploy on push to develop)
productionRequired reviewers, deployment branch = main

Important: The environment names must be exactly engineering and production (lowercase).


5. GitHub Repository Settings (repo-level)

These are repo-level settings (not environment-specific).

Repository Variables

GitHub → Settings → Secrets and variables → Actions → Variables (Repository variables)

Variable NameDescriptionExample
REGISTRY_URLDigitalOcean container registry URLregistry.digitalocean.com/hungryhub
REGISTRY_USERNAMEDOCR login usernamehungryhub-bot or your DO email
GHCR_USERNAMEGitHub Container Registry username that owns the production PAThungryhub-bot

Repository Secrets

GitHub → Settings → Secrets and variables → Actions → Repository secrets

Secret NameDescriptionExample
DOCR_TOKENDigitalOcean token used for DOCR docker logindop_v1_abc123...
DO_API_TOKENDigitalOcean API token with access to the engineering DOKS cluster (used by doctl)dop_v1_abc123...
PATGitHub Personal Access Token with write:packages scope (image push)ghp_xxxx...

Note: PAT is used for GHCR login (production image push) and for creating the imagePullSecret in EKS. Set repository variable GHCR_USERNAME to the exact GitHub username that owns this PAT; the workflow now fails production deploys if it is missing.


6. GitHub Environment Variables (per env)

Set these under each GitHub Environment (engineering and production separately):

GitHub → Settings → Environments → [engineering/production] → Environment variables

Variable NameDescriptionEngineering ExampleProduction Example
ENVIRONMENTRuntime environment identifierengineeringproduction
FORCE_MOCK_MODELUse mock model for servingfalsefalse
GROWTHBOOK_DEFAULT_HOMEPAGE_MODELFallback homepage model variant used when GrowthBook is unavailable or returns no flag valuemember_two_towermember_two_tower
CLICKHOUSE_HOSTClickHouse server hostnamech-engineering.internalch-prod.internal
CLICKHOUSE_PORTClickHouse native protocol port90009000
CLICKHOUSE_HTTP_PORTClickHouse HTTP protocol port (used by training/feature jobs)81238123
CLICKHOUSE_SECUREUse TLS for ClickHouse native/HTTP clientstruetrue
CLICKHOUSE_VERIFY_CERTVerify ClickHouse TLS certificatetruetrue
CLICKHOUSE_USERClickHouse usernamedefaultrecsys_app
CLICKHOUSE_DATABASEClickHouse database nameliondb_engineeringliondb_production
REDIS_HOSTRedis server hostnameredis-engineering.internalredis-prod.internal
REDIS_PORTRedis port63796379
REDIS_DBOptional Redis logical database index05
REDIS_TLSEnable TLS for Redis connectionsfalsefalse
OPENSEARCH_NODEOpenSearch node URL including scheme and optional porthttps://search-engineering.internal:443https://search-prod.internal:443
OPENSEARCH_INDEXOpenSearch index or alias name used by retrieval and metadata refreshengineering_restaurantsproduction_restaurants
OPENSEARCH_VERIFY_CERTSVerify OpenSearch TLS certificatestruetrue
OPENSEARCH_CA_CERTSOptional CA bundle path mounted in the pod(empty or mounted path)/etc/ssl/certs/custom-opensearch-ca.pem
NAMESPACEOptional Kafka topic namespace prefix applied to topic vars at runtimeengineeringproduction
KAFKA_BOOTSTRAP_SERVERSKafka bootstrap servers for ingestion/emissionbroker1:9092,broker2:9092broker1:9092,broker2:9092
KAFKA_SECURITY_PROTOCOLKafka broker security protocolsasl_sslssl
KAFKA_SASL_MECHANISMSASL mechanism for secured brokersSCRAM-SHA-512(empty)
KAFKA_SSL_CA_LOCATIONBroker CA file path mounted in pod/etc/hh-lion/kafka/broker-ca.crt/etc/hh-lion/kafka/broker-ca.crt
KAFKA_SSL_CERT_LOCATIONBroker client certificate file path mounted in pod(empty or mounted path)/etc/hh-lion/kafka/broker-client.crt
KAFKA_SSL_KEY_LOCATIONBroker client key file path mounted in pod(empty or mounted path)/etc/hh-lion/kafka/broker-client.key
KAFKA_SCHEMA_REGISTRY_URLSchema Registry URL for consumer/producer contractshttps://schema-registry.engineering.internal:8081https://schema-registry.prod.internal:8081
KAFKA_SCHEMA_REGISTRY_BASIC_AUTH_CREDENTIALS_SOURCESchema Registry auth sourceUSER_INFOUSER_INFO
KAFKA_SCHEMA_REGISTRY_SSL_CA_LOCATIONSchema Registry CA file path mounted in pod/etc/hh-lion/kafka/schema-registry-ca.crt/etc/hh-lion/kafka/schema-registry-ca.crt
KAFKA_SCHEMA_REGISTRY_SSL_CERT_LOCATIONSchema Registry client cert file path mounted in pod(empty or mounted path)/etc/hh-lion/kafka/schema-registry-client.crt
KAFKA_SCHEMA_REGISTRY_SSL_KEY_LOCATIONSchema Registry client key file path mounted in pod(empty or mounted path)/etc/hh-lion/kafka/schema-registry-client.key
KAFKA_EVENTS_TOPICBase interaction events topic consumed into ClickHouse; prefixed by NAMESPACE when sethh.lion.interaction.eventshh.lion.interaction.events
KAFKA_CONSUMER_GROUPConsumer group for ingestion workerfeature-store-consumerfeature-store-consumer
KAFKA_DLQ_TOPICBase dead-letter topic for invalid events; prefixed by NAMESPACE when sethh.lion.interaction.events.dlqhh.lion.interaction.events.dlq
KAFKA_IMPRESSION_TOPICBase topic for serving impression emission; prefixed by NAMESPACE when sethh.lion.recsys.impressionshh.lion.recsys.impressions
KAFKA_IMPRESSION_SCHEMA_PATHOptional path to Avro schema in runtime container; leave empty for JSON fallback(empty)(empty)
MLFLOW_TRACKING_URIMLflow tracking server URLhttp://mlflow.engineering.internalhttp://mlflow.prod.internal
MLFLOW_S3_ENDPOINT_URLS3/R2 endpoint for MLflow artifactshttps://<acct>.r2.cloudflarestorage.comhttps://<acct>.r2.cloudflarestorage.com
MLFLOW_ARTIFACT_ROOTMLflow artifact root for the MLflow services3://hh-lion-bucket-engineering/mlflow/artifactss3://hh-lion-bucket-production/mlflow/artifacts
MLFLOW_MODEL_NAMERegistered model name for servinghomepage_two_towerhomepage_two_tower
MLFLOW_MODEL_ALIASRegistered model alias for servingengineeringproduction
MLFLOW_DEPLOY_ENABLEDWhether the workflow also deploys MLflowtruetrue
MLFLOW_HOSTNAMEOverride MLflow ingress host (optional)mlflow-engineering.hh-engineering.my.idmlflow.hungryhub.com
MLFLOW_AWS_REGIONArtifact client region for MLflow pod (auto for R2)autoauto
MLFLOW_WORKERSMLflow gunicorn workers (optional)12
MLFLOW_SERVER_ALLOWED_HOSTSAllowed Host headers for MLflow UI/API (optional)mlflow-engineering.hh-engineering.my.idmlflow.hungryhub.com
MLFLOW_SERVER_CORS_ALLOWED_ORIGINSAllowed browser origins for MLflow UI/API (optional)https://mlflow-engineering.hh-engineering.my.idhttps://mlflow.hungryhub.com
SOURCE_CLICKHOUSE_HOSTSource ClickHouse host for production bootstrap backfill workflow(not used)10.10.0.3
SOURCE_CLICKHOUSE_HTTP_PORTSource ClickHouse HTTP port for production bootstrap backfill workflow(not used)8123
SOURCE_CLICKHOUSE_DATABASESource ClickHouse database for production bootstrap backfill workflow(not used)booking_production
FEAST_REGISTRY_TYPEFeast registry backend type for shared deploymentssqlsql
FEAST_REGISTRY_CACHE_TTL_SECONDSOptional Feast registry cache TTL6060
FEAST_REGISTRY_SQLALCHEMY_ECHOOptional SQLAlchemy debug logging flag for Feast registryfalsefalse
FEAST_REGISTRY_SQLALCHEMY_POOL_PRE_PINGOptional SQLAlchemy connection health checks for Feast registrytruetrue
FEAST_REGISTRY_THREAD_POOL_WORKERSOptional Feast registry worker count44
LOG_LEVELApplication log levelDEBUGINFO
CORS_ALLOWED_ORIGINSComma-separated allowed CORS origins*https://app.hungryhub.com
ROLLBAR_ENABLEDEnable Rollbar error reporting for serving plus Python script, worker, and job entry points when the token is also configured and ENVIRONMENT is engineering or productiontruetrue

These become a Kubernetes ConfigMap (lion-config) at deploy time.

Keep the storage roles separate when naming databases:

  • CLICKHOUSE_DATABASE is the analytics / feature-source database and can remain liondb_engineering / liondb_production.
  • MLFLOW_BACKEND_STORE_URI should point to dedicated MLflow metadata databases such as liondb_engineering / liondb_production.
  • FEAST_REGISTRY_PATH should point to dedicated Feast registry databases such as liondb_engineering / liondb_production.

For the current Feast architecture, set FEAST_REGISTRY_TYPE=sql and provide a shared registry DSN via the environment secrets below. The deploy workflow now applies the tracked Feast repo explicitly before rolling out serving, and the engineering bootstrap workflow reuses the same registry when refreshing features or training.

The current Feast retrieval contract is:

  • Feast online store (Redis) is the required serving path for member, item, and cohort model features.
  • Feast offline store (ClickHouse) is the required training and export path through v2 FeatureService contracts.
  • Training features must be point-in-time correct. Features that cannot be retrieved through v2 historical tables with feature_timestamp semantics must be excluded from the model contract.
  • Direct ClickHouse reads are limited to diagnostics, validation, candidate-source support, and session features that are not part of the Feast model-feature contract. They are not an acceptable substitute for Feast online serving.

The v2 ClickHouse feature tables retain history by entity and feature_timestamp. Feast source definitions must point only at those v2 historical tables, and online publication must occur only after historical coverage, freshness, quality, and recommendation-quality gates pass.

For the current deployment policy, set GROWTHBOOK_DEFAULT_HOMEPAGE_MODEL=member_two_tower in both engineering and production so the service keeps the same fallback behavior across environments when GrowthBook is unavailable.

Kafka Topic Naming Standard

Use the following rule for all Kafka topics managed by hh-lion:

  • Effective topic format: <NAMESPACE>.<base-topic>
  • Namespace format: <environment>
  • Base topic format: lowercase, dot-separated, domain-first

Naming rules:

  • Prefer domain-first names such as hh.lion.recsys.impressions
  • Use *.events for canonical event streams such as hh.lion.interaction.events
  • Use .dlq for dead-letter topics such as hh.lion.interaction.events.dlq
  • Do not create new Kafka topics using _
  • Do not use camelCase in topic names

Canonical examples for this repo:

  • engineering.hh.lion.interaction.events
  • engineering.hh.lion.interaction.events.dlq
  • engineering.hh.lion.recsys.impressions
  • production.hh.lion.interaction.events
  • production.hh.lion.interaction.events.dlq
  • production.hh.lion.recsys.impressions

7. GitHub Environment Secrets (per env)

Set these under each GitHub Environment (engineering and production separately):

GitHub → Settings → Environments → [engineering/production] → Environment secrets

Secret NameDescriptionNotes
CLICKHOUSE_PASSWORDClickHouse passwordDifferent credentials per environment
REDIS_PASSWORDRedis passwordRequired only if your Redis instance has auth enabled (e.g., engineering)
AWS_ACCESS_KEY_IDR2/S3 access key for MLflow artifact downloadGitHub environment secret used to populate MLflow server secrets and lion-secrets R2_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEYR2/S3 secret key for MLflow artifact downloadGitHub environment secret used to populate MLflow server secrets and lion-secrets R2_SECRET_ACCESS_KEY
MLFLOW_BACKEND_STORE_URIPostgres connection URI for MLflow service metadata DBe.g., postgresql://user:pass@host:5432/liondb_engineering?sslmode=require
KAFKA_SASL_USERNAMEKafka SASL usernameRequired for sasl_ssl / sasl_plaintext brokers
KAFKA_SASL_PASSWORDKafka SASL passwordRequired for sasl_ssl / sasl_plaintext brokers
KAFKA_SCHEMA_REGISTRY_USERNAMESchema Registry basic-auth usernameOptional; set with password when registry requires auth
KAFKA_SCHEMA_REGISTRY_PASSWORDSchema Registry basic-auth passwordOptional; set with username when registry requires auth
KAFKA_SSL_CA_CERTBroker CA certificate PEMOptional GitHub secret used by deploy workflow to create lion-kafka-certs
KAFKA_SSL_CERTBroker client certificate PEMOptional GitHub secret used by deploy workflow to create lion-kafka-certs
KAFKA_SSL_KEYBroker client key PEMOptional GitHub secret used by deploy workflow to create lion-kafka-certs
KAFKA_SCHEMA_REGISTRY_SSL_CA_CERTSchema Registry CA certificate PEMOptional GitHub secret used by deploy workflow to create lion-kafka-certs
KAFKA_SCHEMA_REGISTRY_SSL_CERTSchema Registry client certificate PEMOptional GitHub secret used by deploy workflow to create lion-kafka-certs
KAFKA_SCHEMA_REGISTRY_SSL_KEYSchema Registry client key PEMOptional GitHub secret used by deploy workflow to create lion-kafka-certs
FEAST_REGISTRY_PATHShared Feast registry SQLAlchemy write DSNRequired for shared Feast registry bootstrapping in deploy/bootstrap workflows; e.g. postgresql://user:pass@host:5432/liondb_engineering?sslmode=require
FEAST_REGISTRY_READ_PATHOptional Feast registry read-replica SQLAlchemy DSNOptional; only set together with FEAST_REGISTRY_PATH, for example a read-only replica of liondb_engineering
CACHE_HMAC_SECRETHMAC secret for cache key hashingGenerate with openssl rand -hex 32
OPENSEARCH_USEROpenSearch basic auth username (optional if auth disabled)search_user
OPENSEARCH_PASSWORDOpenSearch basic auth passwordStored as GitHub secret
ROLLBAR_ACCESS_TOKENRollbar post_server_item access tokenUse separate tokens per environment when possible

The deploy workflow mounts optional Kafka certificate material from the lion-kafka-certs secret into /etc/hh-lion/kafka for both serving and consumer pods. Set the *_LOCATION variables above to those in-container paths and provide the corresponding PEM contents through GitHub environment secrets when SSL or mTLS is required.

In addition, set these infrastructure variables per environment:

Engineering environment variables

Variable NameDescriptionExample
DO_CLUSTER_ID_ENGINEERINGDigitalOcean DOKS cluster IDa1b2c3d4-e5f6-...

Production environment variables

Variable NameDescriptionExample
AWS_ROLE_ARNIAM role ARN for OIDCarn:aws:iam::123456789012:role/hh-lion-deploy
AWS_REGIONAWS region of EKS clusterap-southeast-1
EKS_CLUSTER_NAMEEKS cluster namehungryhub-prod

8. AWS OIDC Setup (Production)

Production uses OIDC federation instead of static AWS access keys. This is more secure — no long-lived credentials stored in GitHub.

Step 1: Create OIDC Identity Provider in AWS IAM

  1. Go to AWS IAM → Identity providers → Add provider
  2. Set:
    • Provider type: OpenID Connect
    • Provider URL: https://token.actions.githubusercontent.com
    • Audience: sts.amazonaws.com
  3. Click Add provider

Step 2: Create IAM Role

  1. Go to IAM → Roles → Create role
  2. Choose Web identity
  3. Select the OIDC provider you just created
  4. Set the trust policy (replace YOUR_GITHUB_ORG and YOUR_REPO):
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Federated": "arn:aws:iam::YOUR_ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
      },
      "Action": "sts:AssumeRoleWithWebIdentity",
      "Condition": {
        "StringEquals": {
          "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
        },
        "StringLike": {
          "token.actions.githubusercontent.com:sub": "repo:hungryhub-team/hh-lion:environment:production"
        }
      }
    }
  ]
}

Step 3: Attach Policies to the Role

Attach these AWS managed policies (or more restrictive custom ones):

  • AmazonEKSClusterPolicy — or a custom policy scoped to your cluster
  • A custom policy for EKS access:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "eks:DescribeCluster",
        "eks:ListClusters"
      ],
      "Resource": "arn:aws:eks:ap-southeast-1:YOUR_ACCOUNT_ID:cluster/YOUR_CLUSTER_NAME"
    }
  ]
}

Step 4: Configure EKS RBAC

The IAM role also needs Kubernetes RBAC permissions. Edit the aws-auth ConfigMap or use EKS access entries:

# Option A: aws-auth ConfigMap
kubectl edit configmap aws-auth -n kube-system

Add under mapRoles:

- rolearn: arn:aws:iam::YOUR_ACCOUNT_ID:role/hh-lion-deploy
  username: hh-lion-deploy
  groups:
    - hh-lion-deployers  # avoid system:masters; bind this group to least-privilege RBAC

Step 5: Save the Role ARN

Copy the role ARN and set it as AWS_ROLE_ARN in the production environment variables (see Section 7).


9. Create Kubernetes Namespace

On both clusters, create the namespace before the first deploy:

# Engineering (connect to DOKS first)
doctl kubernetes cluster kubeconfig save YOUR_CLUSTER_ID
kubectl create namespace hh-lion

# Production (connect to EKS first)
aws eks update-kubeconfig --name YOUR_CLUSTER --region ap-southeast-1
kubectl create namespace hh-lion

The deploy workflow creates the lion-config ConfigMap and lion-secrets Secret automatically on each deploy. You only need to create the namespace manually once.


10. Trigger a Deployment

Engineering (automatic)

Push or merge to the develop branch:

git checkout develop
git push origin develop

The workflow triggers automatically and deploys to engineering.

Production (manual)

  1. Go to GitHub → Actions → “Build & Deploy to Kubernetes”
  2. Click “Run workflow”
  3. Select environment: production
  4. Click “Run workflow”

The workflow will:

  1. Build the Docker image from recsys/Dockerfile
  2. Push to the appropriate registry (DOCR or GHCR)
  3. Patch the K8s manifest with the new image tag
  4. Create/update the ConfigMap (lion-config) and Secret (lion-secrets)
  5. Apply the K8s manifests
  6. Wait for rollout to complete

11. Verify Deployment

After the workflow completes:

# Check pods are running
kubectl get pods -n hh-lion

# Check the deployed image
kubectl get deployment hh-lion-api -n hh-lion -o jsonpath='{.spec.template.spec.containers[0].image}'

# Check health endpoint (port-forward for testing)
kubectl port-forward svc/hh-lion 8000:80 -n hh-lion
curl http://localhost:8000/health

12. Troubleshooting

Pod in CrashLoopBackOff

kubectl logs deployment/hh-lion-api -n hh-lion --previous

Common causes:

  • Missing or incorrect env vars in ConfigMap/Secret
  • ClickHouse or Redis not reachable from the cluster
  • Invalid MLFLOW_MODEL_NAME or MLFLOW_MODEL_ALIAS — model version or artifacts not found

ImagePullBackOff

kubectl describe pod -l app=hh-lion -n hh-lion | grep -A5 Events

Common causes:

  • Engineering: DOCR token expired or registry URL wrong
  • Production: hh-lion-ghcr-secret not created or PAT expired

Workflow Fails at “Configure AWS credentials”

  • Verify the OIDC provider is set up in AWS IAM
  • Check the trust policy matches repo:hungryhub-team/hh-lion:environment:production
  • Ensure AWS_ROLE_ARN environment variable is correct

ConfigMap/Secret Not Updating

The deploy workflow uses --dry-run=client -o yaml | kubectl apply -f - for idempotent updates. If values seem stale:

# Check current configmap values
kubectl get configmap lion-config -n hh-lion -o yaml

# Check secrets exist (values are base64-encoded)
kubectl get secret lion-secrets -n hh-lion -o yaml

Quick Reference: All GitHub Settings

Repository Variables (repo-level)

VariableValue
REGISTRY_URLregistry.digitalocean.com/hungryhub
REGISTRY_USERNAMEDOCR username

Repository Secrets (repo-level)

SecretValue
DOCR_TOKENDigitalOcean token for registry login
DO_API_TOKENDigitalOcean API token for doctl / DOKS access
PATGitHub PAT (write:packages)

Engineering Environment — Variables

VariableValue
ENVIRONMENTengineering
FORCE_MOCK_MODELfalse
CLICKHOUSE_HOSTYour engineering ClickHouse host
CLICKHOUSE_PORT9000
CLICKHOUSE_HTTP_PORT8123
CLICKHOUSE_SECUREtrue
CLICKHOUSE_VERIFY_CERTtrue
CLICKHOUSE_USEREngineering ClickHouse username
CLICKHOUSE_DATABASEliondb_engineering
REDIS_HOSTYour engineering Redis host
REDIS_PORT6379
REDIS_DB0
REDIS_TLSfalse
NAMESPACEengineering
KAFKA_BOOTSTRAP_SERVERSEngineering Kafka bootstrap servers
KAFKA_SECURITY_PROTOCOLsasl_ssl
KAFKA_SASL_MECHANISMe.g. SCRAM-SHA-512
KAFKA_SSL_CA_LOCATION/etc/hh-lion/kafka/broker-ca.crt
KAFKA_SCHEMA_REGISTRY_URLEngineering Schema Registry URL
KAFKA_SCHEMA_REGISTRY_BASIC_AUTH_CREDENTIALS_SOURCEUSER_INFO
KAFKA_SCHEMA_REGISTRY_SSL_CA_LOCATION/etc/hh-lion/kafka/schema-registry-ca.crt
KAFKA_EVENTS_TOPIChh.lion.interaction.events
KAFKA_CONSUMER_GROUPfeature-store-consumer
KAFKA_DLQ_TOPIChh.lion.interaction.events.dlq
KAFKA_IMPRESSION_TOPIChh.lion.recsys.impressions
KAFKA_IMPRESSION_SCHEMA_PATHOptional; only set when schema is mounted in runtime container
MLFLOW_TRACKING_URIYour engineering MLflow URL
MLFLOW_S3_ENDPOINT_URLYour engineering R2/S3 endpoint
MLFLOW_MODEL_NAMERegistered model name for engineering serving
MLFLOW_MODEL_ALIASRegistered model alias for engineering serving
FEAST_REGISTRY_TYPEsql
LOG_LEVELDEBUG
CORS_ALLOWED_ORIGINS*
ROLLBAR_ENABLEDtrue
DO_CLUSTER_ID_ENGINEERINGDigitalOcean cluster ID

Engineering Environment — Secrets

SecretValue
CLICKHOUSE_PASSWORDEngineering ClickHouse password
REDIS_PASSWORDEngineering Redis password (if enabled)
AWS_ACCESS_KEY_IDEngineering R2/S3 artifact access key (copied by deploy workflow into lion-secrets as R2_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEYEngineering R2/S3 artifact secret key (copied by deploy workflow into lion-secrets as R2_SECRET_ACCESS_KEY)
MLFLOW_BACKEND_STORE_URIe.g. postgresql://user:pass@host:5432/liondb_engineering?sslmode=require
FEAST_REGISTRY_PATHe.g. postgresql://user:pass@host:5432/liondb_engineering?sslmode=require
KAFKA_SASL_USERNAMEEngineering Kafka SASL username
KAFKA_SASL_PASSWORDEngineering Kafka SASL password
KAFKA_SCHEMA_REGISTRY_USERNAMEEngineering Schema Registry username (if required)
KAFKA_SCHEMA_REGISTRY_PASSWORDEngineering Schema Registry password (if required)
KAFKA_SSL_CA_CERTBroker CA certificate PEM for mounted secret
KAFKA_SCHEMA_REGISTRY_SSL_CA_CERTSchema Registry CA certificate PEM for mounted secret
CACHE_HMAC_SECRETopenssl rand -hex 32
ROLLBAR_ACCESS_TOKENEngineering Rollbar post_server_item access token

Production Environment — Variables

VariableValue
ENVIRONMENTproduction
FORCE_MOCK_MODELfalse
CLICKHOUSE_HOSTYour production ClickHouse host
CLICKHOUSE_PORT9000
CLICKHOUSE_HTTP_PORT8123
CLICKHOUSE_SECUREtrue
CLICKHOUSE_VERIFY_CERTtrue
CLICKHOUSE_USERProduction ClickHouse username
CLICKHOUSE_DATABASEliondb_production
REDIS_HOSTYour production Redis host
REDIS_PORT6379
REDIS_DB5
REDIS_TLSfalse
NAMESPACEproduction
KAFKA_BOOTSTRAP_SERVERSProduction Kafka bootstrap servers
KAFKA_SECURITY_PROTOCOLssl
KAFKA_SSL_CA_LOCATION/etc/hh-lion/kafka/broker-ca.crt
KAFKA_SSL_CERT_LOCATION/etc/hh-lion/kafka/broker-client.crt
KAFKA_SSL_KEY_LOCATION/etc/hh-lion/kafka/broker-client.key
KAFKA_SCHEMA_REGISTRY_URLProduction Schema Registry URL
KAFKA_SCHEMA_REGISTRY_BASIC_AUTH_CREDENTIALS_SOURCEUSER_INFO
KAFKA_SCHEMA_REGISTRY_SSL_CA_LOCATION/etc/hh-lion/kafka/schema-registry-ca.crt
KAFKA_SCHEMA_REGISTRY_SSL_CERT_LOCATION/etc/hh-lion/kafka/schema-registry-client.crt
KAFKA_SCHEMA_REGISTRY_SSL_KEY_LOCATION/etc/hh-lion/kafka/schema-registry-client.key
KAFKA_EVENTS_TOPIChh.lion.interaction.events
KAFKA_CONSUMER_GROUPfeature-store-consumer
KAFKA_DLQ_TOPIChh.lion.interaction.events.dlq
KAFKA_IMPRESSION_TOPIChh.lion.recsys.impressions
KAFKA_IMPRESSION_SCHEMA_PATHOptional; only set when schema is mounted in runtime container
MLFLOW_TRACKING_URIYour production MLflow URL
MLFLOW_S3_ENDPOINT_URLYour production R2/S3 endpoint
MLFLOW_MODEL_NAMERegistered model name for production serving
MLFLOW_MODEL_ALIASRegistered model alias for production serving
FEAST_REGISTRY_TYPEsql
LOG_LEVELINFO
CORS_ALLOWED_ORIGINShttps://app.hungryhub.com
ROLLBAR_ENABLEDtrue
AWS_ROLE_ARNIAM role ARN for OIDC
AWS_REGIONe.g., ap-southeast-1
EKS_CLUSTER_NAMEEKS cluster name

Production Environment — Secrets

SecretValue
CLICKHOUSE_PASSWORDProduction ClickHouse password
REDIS_PASSWORDProduction Redis password (optional; leave unset/empty if no auth)
AWS_ACCESS_KEY_IDProduction R2/S3 artifact access key (copied by deploy workflow into lion-secrets as R2_ACCESS_KEY_ID)
AWS_SECRET_ACCESS_KEYProduction R2/S3 artifact secret key (copied by deploy workflow into lion-secrets as R2_SECRET_ACCESS_KEY)
MLFLOW_BACKEND_STORE_URIe.g. postgresql://user:pass@host:5432/liondb_production?sslmode=require
FEAST_REGISTRY_PATHe.g. postgresql://user:pass@host:5432/liondb_production?sslmode=require
KAFKA_SCHEMA_REGISTRY_USERNAMEProduction Schema Registry username (if required)
KAFKA_SCHEMA_REGISTRY_PASSWORDProduction Schema Registry password (if required)
KAFKA_SSL_CA_CERTBroker CA certificate PEM for mounted secret
KAFKA_SSL_CERTBroker client certificate PEM for mounted secret
KAFKA_SSL_KEYBroker client key PEM for mounted secret
KAFKA_SCHEMA_REGISTRY_SSL_CA_CERTSchema Registry CA certificate PEM for mounted secret
KAFKA_SCHEMA_REGISTRY_SSL_CERTSchema Registry client certificate PEM for mounted secret
KAFKA_SCHEMA_REGISTRY_SSL_KEYSchema Registry client key PEM for mounted secret
CACHE_HMAC_SECRETopenssl rand -hex 32
ROLLBAR_ACCESS_TOKENProduction Rollbar post_server_item access token