Zum Inhalt springen

natron-deployment

Type Skill
Plugin awl-cicd · v0.0.22
Invoke /awl-cicd:natron-deployment
Tools Read, Write, Edit, Bash, Glob, Grep
Source plugins/awl-cicd/skills/natron-deployment/SKILL.md

This skill should be used when the user asks to “add a database”, “postgres for the app”, “pgvector”, “S3 storage”, “persistent volume”, “basic auth on staging”, “custom domain”, “expose only via teleport”, “deploy frontend and backend from one repo”, “rotate secret”, “argocd out of sync”, “delete a deployment”, or “deploy to customer cluster”. Provisions and operates cluster-side resources for an app on the AWL Natron Kubernetes cluster: shared PostgreSQL via pgAdmin, S3 on flow.swiss, persistent volumes, ingress basic auth, Teleport-only endpoints, custom domains, monorepo multi-app deploys, ArgoCD operations. For the CI deploy job and DEPLOYMENT_VALUES basics use deploying-to-kubernetes.

Trigger phrases: add a database · postgres for the app · pgvector · S3 storage · persistent volume · basic auth on staging · custom domain · expose only via teleport · deploy frontend and backend from one repo · rotate secret · argocd out of sync · delete a deployment · deploy to customer cluster

Pipeline setup, DEPLOYMENT_VALUES basics, the pre-merge checklist, and branch-to-environment mapping live in the deploying-to-kubernetes skill. Read that first if the app has no deploy job yet.

No local cluster access exists by default. kubectl works only through Teleport Connect (tsh login --proxy=teleport.awl.k8s.natron.cloud). Anything that needs ArgoCD write permission, namespace deletion, or a database extension is a DevOps request (Slack #tools-autodeploy).

Environment pgAdmin Host from inside the cluster
Staging https://pgadmin.staging.appswithlove.net/ postgres17.postgres:5432
Production https://pgadmin.prod.appswithlove.net/ postgres17.postgres:5432

pgAdmin credentials are in 1Password. Steps, done by a human in pgAdmin:

  1. Login/Group Roles → create a role, enable “Can login”, set a password. Use openssl rand -hex 32; characters like /, +, = break connection URIs.
  2. Databases → create a database with that role as owner. Use the same name for role and database.
  3. Store the password as a GitLab CI variable (masked, scope *).
DEPLOYMENT_VALUES: |
configmap:
DB_HOST: postgres17.postgres
DB_PORT: "5432"
DB_USER: "my-db"
DB_DATABASE: "my-db"
gitlabSecrets:
DB_PASS: DB_PASSWORD

Payload projects need only DATABASE_URI.

Constraints of the shared instance:

  • It runs a PostGIS image (postgis/postgis:17-*). pgvector is not installed and CREATE EXTENSION needs superuser. Both are DevOps tickets; never propose swapping the instance image, it would break PostGIS for every other project.
  • wal_level=logical is already set (needed for PowerSync replication). A replication role needs REPLICATION BYPASSRLS LOGIN plus GRANT SELECT per table, granted by a human in pgAdmin.
  • The instance is shared. Restarts by DevOps hit every project (57P01 admin shutdown, then ECONNREFUSED for a few minutes).
  • No SSL enforcement on the internal connection.

Object storage is flow.swiss S3. Create an access key pair in the flow.swiss console (credentials in 1Password), store both as CI variables.

# staging
DEPLOYMENT_VALUES: |
configmap:
S3_HOST: https://os.alp1.flow.swiss
gitlabSecrets:
S3_KEY: S3_KEY
S3_SECRET: S3_SECRET
# production
DEPLOYMENT_VALUES: |
configmap:
S3_HOST: https://os.zrh1.flow.swiss
gitlabSecrets:
S3_KEY: S3_KEY
S3_SECRET: S3_SECRET

Don’t add the gitlabSecrets mapping before the variables exist; a missing variable makes ArgoCD red. Prefer S3 over persistent volumes for uploads and anything larger than config files.

DEPLOYMENT_VALUES: |
volumes:
uploads:
path: /app/uploads
size: 1Gi
type: pvc

Volumes survive stop jobs and redeploys. Copying files in or fixing permissions (the app runs as UID 1000) requires kubectl cp or a helper pod via Teleport; the recipes are in references/TEMPLATES.md.

Generate the htpasswd line and escape $ as $$$$ (GitLab expands $):

Terminal-Fenster
docker run --rm -ti backplane/htpasswd -nb '<user>' '<pass>' | sed 's/\$/$$$$/g'
DEPLOYMENT_VALUES: |
ingress:
password: <htpasswd-output>

This protects the whole ingress. Paths can’t be excluded.

For internal tools that must not be public:

DEPLOYMENT_VALUES: |
teleport:
enabled: true
name: my-admin-tool # optional
description: Admin UI # optional
ingress:
enabled: false # public ingress is on by default

The app appears in the Teleport of the matching cluster after a few minutes.

Set DEPLOYMENT_DOMAIN on the deploy job. Domains outside *.staging|testing|prod.appswithlove.net need a DNS record pointing at the cluster ingress first (Operation Manuals → Natron.io → HTTP Ingress & SSL in ClickUp). Let’s Encrypt issues the certificate automatically once the domain resolves; certificate errors show up in ArgoCD.

Autodeploy can target external clusters that DevOps registered in ArgoCD:

deploy-main-branch:
variables:
DEPLOYMENT_CLUSTER: "customer-cluster-name"
include:
- component: $CI_SERVER_FQDN/devops/ci-cd-templates/deploy-default-branches@main
inputs:
app-dir: frontend # no trailing slash
app-suffix: -frontend
- component: $CI_SERVER_FQDN/devops/ci-cd-templates/deploy-default-branches@main
inputs:
app-dir: backend
app-suffix: -backend
watch:
- packages/shared/**/*
deploy-main-branch-frontend:
variables:
DEPLOYMENT_DOMAIN: app.example.com
deploy-main-branch-backend:
variables:
DEPLOYMENT_DOMAIN: api.example.com

Each include creates its own build-docker-<suffix> and deploy-*-<suffix> jobs, image tags main-frontend etc., and environments staging-frontend etc. Jobs run only when their app-dir or a watch path changed.

Redis or Meilisearch next to the app: deploy-service template with service-name and preset. Details in gitlab-cicd/references/templates/deploy.md.

Environment URL
Staging https://argocd.teleport.awl-staging.k8s.natron.cloud/
Production https://argocd.teleport.awl.k8s.natron.cloud/

Both need a Teleport account and an ArgoCD account. Application name: <DEPLOYMENT_NAME>-<namespace>-<cluster>.

  • Force refresh: click Refresh on the application.
  • Logs / shell: pod → context menu → Logs or Exec.
  • Last deploy time: creation time of the pod.
  • Error details: click Sync failed, not the error counter.
  • Rotate a secret: update the GitLab variable, then delete the ExternalSecret (same name as the deployment). ArgoCD recreates it with the new value. Deleting needs ArgoCD write permission; without it, change the gitlabSecrets mapping to force a new fetch.
  • Pause: replicas: 0 in DEPLOYMENT_VALUES.
  • Stop an environment: manual stop-*-branch job in the pipeline. Removes the app, keeps volumes.
  • Delete for good (wrong name, wrong namespace, renamed app): DevOps only, via #tools-autodeploy. A renamed app leaves the old one running.
DEPLOYMENT_NAME: ${CI_PROJECT_NAME}
DEPLOYMENT_NAMESPACE: ${CI_PROJECT_NAMESPACE}
DEPLOYMENT_CLUSTER: in-cluster
DEPLOYMENT_DOMAIN: ${CI_PROJECT_PATH_SLUG}.<env>.appswithlove.net
DEPLOYMENT_IMAGE: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_REF_SLUG}
DEPLOYMENT_VALUES:
replicas: 1
port: 80
healthz: /
resources:
requests: { memory: 96Mi }
limits: { memory: 256Mi }
ingress:
enabled: true
className: nginx-external
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "0"
nginx.ingress.kubernetes.io/proxy-buffer-size: "128k"
nginx.ingress.kubernetes.io/proxy-buffers-number: "4"
cert-manager.io/cluster-issuer: letsencrypt-http01