Kubernetes

The Stategraph Helm chart deploys Stategraph on Kubernetes, with an ingress, health probes, and a bundled or external PostgreSQL.

Before you begin

  • Kubernetes 1.19 or later, Helm 3, and kubectl configured for your cluster.
  • An ingress controller, for external access.
  • The Helm chart repository URL, available on request with onboarding support: contact Stategraph.

Quick start

Install the chart in its own namespace:

helm repo add stategraph <stategraph-helm-repo>
helm repo update
helm install stategraph stategraph/stategraph \
  --namespace stategraph \
  --create-namespace

For a local trial, forward the service, and create the first admin account on the setup screen at http://localhost:8080.

kubectl port-forward -n stategraph svc/stategraph 8080:80

Cookie security follows the URL

An https:// URL in STATEGRAPH_UI_BASE sets the Secure cookie flag, and an http:// URL does not. So stategraph.ui.base=http://myserver:8080 allows sign-in over plain HTTP, for a port-forward or an internal network.

Production installation

Install with HTTPS, an ingress, and a cert-manager issuer:

helm install stategraph stategraph/stategraph \
  --namespace stategraph \
  --create-namespace \
  --set stategraph.ui.base="https://stategraph.example.com" \
  --set stategraph.ui.oauthRedirectBase="https://stategraph.example.com" \
  --set ingress.enabled=true \
  --set ingress.hosts[0].host="stategraph.example.com" \
  --set ingress.hosts[0].paths[0].path="/" \
  --set ingress.hosts[0].paths[0].pathType="Prefix" \
  --set ingress.tls[0].secretName="stategraph-tls" \
  --set ingress.tls[0].hosts[0]="stategraph.example.com" \
  --set ingress.annotations."cert-manager\.io/cluster-issuer"="letsencrypt-prod"

Configuration

helm show values stategraph/stategraph lists all chart values. The common ones:

Value Description Default
stategraph.image.tag Image version latest
stategraph.replicaCount Number of server pods 1
stategraph.ui.base Public URL (STATEGRAPH_UI_BASE) http://localhost:8080
stategraph.ui.oauthRedirectBase OAuth callback base (STATEGRAPH_OAUTH_REDIRECT_BASE) unset
postgresql.enabled Run the bundled PostgreSQL true
postgresql.auth.existingSecret Secret with the database password ""
postgresql.persistence.size Database volume 10Gi
ingress.enabled Create an ingress false

Server settings are environment variables in the env list of the stategraph container. helm show values shows where your chart version puts that list:

# values.yaml
env:
  - name: STATEGRAPH_OAUTH_EMAIL_DOMAIN
    value: "example.com"

Then roll out the change:

helm upgrade stategraph stategraph/stategraph -n stategraph -f values.yaml

More than one replica

With Google or OIDC sign-in and a stategraph.replicaCount above 1, set STATEGRAPH_OAUTH_COOKIE_SECRET to the same value on each pod, with 16, 24, or 32 characters. Otherwise each process makes a random cookie secret, replicas do not share sessions, and a sign-in fails when its callback goes to another replica. Replicas that start together migrate safely: see Upgrades.

Using external PostgreSQL

To use your own PostgreSQL server:

helm install stategraph stategraph/stategraph \
  --namespace stategraph \
  --create-namespace \
  --set postgresql.enabled=false \
  --set postgresql.host="postgres.internal.example.com" \
  --set postgresql.port=5432 \
  --set postgresql.auth.username="stategraph" \
  --set postgresql.auth.existingSecret="external-db-secret"

Create the secret with the database password:

kubectl create secret generic external-db-secret \
  --from-literal=db-password='your-password' \
  -n stategraph

Create the stategraph database before the first start.

Authentication

Local email and password sign-in is on by default. For Google or OIDC, set the OAuth values at install:

helm install stategraph stategraph/stategraph \
  --namespace stategraph \
  --create-namespace \
  --set stategraph.ui.base="https://stategraph.example.com" \
  --set stategraph.ui.oauthRedirectBase="https://stategraph.example.com" \
  --set stategraph.oauth.enabled=true \
  --set stategraph.oauth.type="google" \
  --set stategraph.oauth.clientId="your-client-id" \
  --set stategraph.oauth.clientSecret="your-client-secret"

In your provider, register https://stategraph.example.com/oauth2/google/callback or /oauth2/oidc/callback as the redirect URI. To limit sign-in to your domain, set STATEGRAPH_OAUTH_EMAIL_DOMAIN. See Access control.

Health checks

The chart sets livenessProbe to /health/live, which answers 200 when the web server is up, and readinessProbe to /health/ready, which answers 200 when the server has migrated and serves requests. To change them:

# values.yaml
livenessProbe:
  httpGet:
    path: /health/live
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 10
  failureThreshold: 3

readinessProbe:
  httpGet:
    path: /health/ready
    port: 8080
  initialDelaySeconds: 10
  periodSeconds: 10
  failureThreshold: 10

Migrations run before the server starts, so for a large database, allow the readiness probe enough failures. See Health checks.

Enable cost estimation

Set STATEGRAPH_COST_ENABLED=true in the container env, so that each new or rescheduled pod starts with cost estimation on:

# values.yaml
env:
  - name: STATEGRAPH_COST_ENABLED
    value: "true"

At the first start, Stategraph loads the price book into the cloud_pricing database in the background. Give that database durable storage: the bundled PostgreSQL volume, or your managed server.

Cost estimation connects with PRICING_DB_*, not DB_*. Its default host is db, the Docker Compose service name, so add PRICING_DB_HOST and PRICING_DB_PASSWORD for your database to the same env list. See Enable cost estimation.

Enable Orchestration

Stategraph Orchestration is off by default. To turn it on, add STATEGRAPH_ORCHESTRATION_ENABLED=true to the same env list. Also add the Orchestration public URLs, the FDW role password, and the GitHub App or GitLab credentials from Kubernetes secrets. For a GitHub App:

# values.yaml
env:
  - name: STATEGRAPH_ORCHESTRATION_ENABLED
    value: "true"
  - name: TERRAT_API_BASE
    value: "https://stategraph.example.com/api"
  - name: TERRAT_UI_BASE
    value: "https://stategraph.example.com"
  - name: TERRAT_WEB_BASE_URL
    value: "https://stategraph.example.com"
  - name: GITHUB_APP_ID
    valueFrom:
      secretKeyRef:
        name: stategraph-github-app
        key: app-id
  - name: GITHUB_APP_CLIENT_ID
    valueFrom:
      secretKeyRef:
        name: stategraph-github-app
        key: client-id
  - name: GITHUB_APP_CLIENT_SECRET
    valueFrom:
      secretKeyRef:
        name: stategraph-github-app
        key: client-secret
  - name: GITHUB_APP_PEM
    valueFrom:
      secretKeyRef:
        name: stategraph-github-app
        key: pem
  - name: GITHUB_WEBHOOK_SECRET
    valueFrom:
      secretKeyRef:
        name: stategraph-github-app
        key: webhook-secret
  - name: STATEGRAPH_FDW_PASSWORD
    valueFrom:
      secretKeyRef:
        name: stategraph-fdw
        key: password

Create the secrets from the values that the setup wizard wrote:

kubectl create secret generic stategraph-github-app -n stategraph \
  --from-literal=app-id="$GITHUB_APP_ID" \
  --from-literal=client-id="$GITHUB_APP_CLIENT_ID" \
  --from-literal=client-secret="$GITHUB_APP_CLIENT_SECRET" \
  --from-literal=pem="$GITHUB_APP_PEM" \
  --from-literal=webhook-secret="$GITHUB_WEBHOOK_SECRET"

kubectl create secret generic stategraph-fdw -n stategraph \
  --from-literal=password='<fdw-password>'

For GitLab, replace the five GITHUB_* entries with the entries below. STATEGRAPH_FDW_PROVISIONER_PASSWORD lets the console connect a GitLab group. For self-managed GitLab, also add GITLAB_API_BASE_URL and GITLAB_WEB_BASE_URL with the root URL of the instance.

# values.yaml, in the same env list
  - name: GITLAB_APP_ID
    valueFrom:
      secretKeyRef:
        name: stategraph-gitlab
        key: app-id
  - name: GITLAB_APP_SECRET
    valueFrom:
      secretKeyRef:
        name: stategraph-gitlab
        key: app-secret
  - name: GITLAB_ACCESS_TOKEN
    valueFrom:
      secretKeyRef:
        name: stategraph-gitlab
        key: access-token
  - name: STATEGRAPH_FDW_PROVISIONER_PASSWORD
    valueFrom:
      secretKeyRef:
        name: stategraph-fdw-provisioner
        key: password
kubectl create secret generic stategraph-gitlab -n stategraph \
  --from-literal=app-id="$GITLAB_APP_ID" \
  --from-literal=app-secret="$GITLAB_APP_SECRET" \
  --from-literal=access-token="$GITLAB_ACCESS_TOKEN"

kubectl create secret generic stategraph-fdw-provisioner -n stategraph \
  --from-literal=password='<provisioner-password>'

Orchestration also needs these on the same PostgreSQL server:

  • The terrateam database.
  • The stategraph_mql role, with the FDW password.
  • For GitLab, the stategraph_provisioner role.
  • On managed PostgreSQL, superuser rights for postgres_fdw, through which the stategraph database reads terrateam.

See Enable Orchestration.

After the rollout, set the webhook URL of your GitHub App to https://stategraph.example.com/api/github/v1/events, with the same secret. For GitLab, connect each group from the Get Started page of the console. The connect wizard shows the project webhook URL, https://stategraph.example.com/api/v1/gitlab/events, and the secret.

Enable security scanning

Add STATEGRAPH_SECURITY with the value 1 to the container env. See Enable security scanning.

Upgrading

helm repo update
helm upgrade stategraph stategraph/stategraph -n stategraph

For production, pin stategraph.image.tag to a version. See Upgrades.

Uninstalling

helm uninstall stategraph -n stategraph
kubectl delete namespace stategraph

Troubleshooting

kubectl get pods -n stategraph
kubectl logs -n stategraph -l app.kubernetes.io/name=stategraph
kubectl get events -n stategraph --sort-by='.lastTimestamp'

A pod that never becomes ready is usually still migrating, or it failed at start and its log shows why:

  • A sign-in provider with a bad configuration.
  • No STATEGRAPH_FDW_PASSWORD with Orchestration on.
  • No GITHUB_WEBHOOK_SECRET.
  • A CONFIG : ERROR line that names a missing GitLab variable.

Next steps