Application Onboarding Guide
This guide walks through the full journey from zero to a running application on the App Platform — from AppPlatformRegistration to pods serving traffic.
How the App Platform Works
The App Platform uses intent-based provisioning. You declare what you want via Kubernetes custom resources (CRs), and the operator reconciles your intent into running infrastructure. If you delete a CR, the operator cleans up. If something drifts, it reconciles back.
You declare: Operator provisions:
AppPlatformRegistration "my-app" -> Creates my-app-platform GitHub repo
-> Generates deploy key + registers on GitHub
-> Generates per-platform AGE encryption key
-> Commits Flux config to flux repo
-> Commits .sops.yaml to platform repo
-> Enforces RBAC in environment namespaces
OIDC client registration is handled separately — app teams create labeled Secrets in the <app>-platform namespace and the OidcClientReconciler picks them up. See Enabling SSO below.
Prerequisites
| Requirement | Who provides it |
|---|---|
| AppPlatformRegistration | System team creates it |
| Authelia group membership | System team configures it |
| A GitHub repository with K8s manifests | Your team |
Part 1: System Team Setup
This section is for system team / platform admins who register new applications.
1.1 Configure Authelia Groups
Create OIDC groups in Authelia for the application team. Convention:
| Group | Role | Purpose |
|---|---|---|
myapp-ops | admin | Full CRUD on resources, namespace management |
myapp-developer | developer | Read workloads, exec, port-forward |
myapp-viewer | reader | Read-only access to workloads and logs |
1.2 Create the AppPlatformRegistration
apiVersion: labrats.work/v1alpha1
kind: AppPlatformRegistration
metadata:
name: my-app
spec:
displayName: "My Application"
access:
- group: myapp-ops
role: admin
- group: myapp-developer
role: developer
- group: myapp-viewer
role: reader
What happens when you apply this:
- Creates the
my-app-platformGitHub repo (private) - Generates an Ed25519 deploy key and registers it on GitHub
- Generates a per-platform AGE encryption key for SOPS
- Commits Flux config to the Flux repo under
apps/my-app-platform/ - Commits
.sops.yamlto the platform repo root - Discovers namespaces with label
labrats.work/app: my-appand creates RBAC
1.3 OIDC clients (optional) {#enabling-sso}
OIDC clients are no longer declared on the APR. To register an Authelia OIDC client for an app, create a labeled Secret in the <app>-platform namespace:
apiVersion: v1
kind: Secret
metadata:
name: my-app-oidc
namespace: my-app-platform
labels:
labrats.work/oidc-client: my-app # client_id in Authelia
labrats.work/app: my-app
type: Opaque
stringData:
hostname: app.my-app.hcl.labrats.work
callbackPath: /api/auth/oidc/callback # optional
# clientSecret is operator-generated
The OidcClientReconciler watches these Secrets, generates the clientSecret, registers the client with Authelia, and reports status via the labrats.work/oidc-status annotation. Multiple OIDC clients per app are supported — one Secret per client/hostname.
Part 2: Application Team Setup
2.1 Prepare Your Repository
Your repository needs Kubernetes manifests that Flux can apply:
apps.my-app/
├── src/ # Your application code
├── Dockerfile
└── k8s/
├── base/
│ ├── kustomization.yaml
│ ├── deployment.yaml
│ ├── service.yaml
│ └── ingress.yaml
└── overlays/
├── dev/
│ └── kustomization.yaml # namespace: my-app-dev
└── production/
└── kustomization.yaml # namespace: my-app-prod
2.2 Configure the Platform Repo
The operator creates the my-app-platform repo AND scaffolds the standard files into it on first reconcile. You inherit:
my-app-platform/
├── kustomization.yaml # root — references every sibling
├── my-app-source.yaml # GitRepository → apps.my-app
├── my-app-image-automation.yaml # ImageRepository + Policy + Update
├── my-app-prod.yaml # Flux Kustomization for prod env
├── my-app-platform.yaml # Flux Kustomization → ./platform
├── .sops.yaml # per-app age recipient
└── platform/
├── kustomization.yaml
└── rbac.yaml # secret-reader for cross-ns secrets
See the platform-repo layout spec for what each file does.
Clone and customise only if you need to — most apps run with the scaffold unchanged. Common edits:
- Extra environments. Add
my-app-<env>.yaml(dev, staging, admin). See the multi-env image automation cookbook. - Extra images. Duplicate the ImageRepository + ImagePolicy blocks in
my-app-image-automation.yamlfor additional container images. - Secrets. Add SOPS-encrypted Secrets under
platform/— they decrypt with your per-app age key, not the cluster key.
2.3 Namespaces
- Platform namespaces (
<app>-platform) are created automatically by the operator - App namespaces are created via the dashboard (
POST /api/namespaces) - All namespaces carry
labrats.work/app: my-app - Only
app-role namespaces appear in the dashboard
2.4 Flux Takes Over
Once the Flux CRs exist, Flux controllers handle the rest:
- Source Controller clones your repo via SSH (using the deploy key)
- Kustomize Controller runs
kustomize buildon each overlay path - Applies the resulting manifests to each target namespace
- Prunes removed resources (garbage collection)
- Repeats every 5-10 minutes
From this point, every git push to your repo automatically deploys to the cluster.
2.5 Verify Your Deployment
# AppPlatformRegistration status
kubectl get appplatformregistration my-app
# Platform repo Flux resources
kubectl get gitrepository -n my-app-platform
# Kustomizations
kubectl get kustomization -n my-app-platform
# RBAC in env namespaces
kubectl get rolebindings -n my-app-dev
# Running pods
kubectl get pods -n my-app-dev
kubectl get pods -n my-app-prod
Part 3: Adding Secrets
Secrets are managed through the dashboard — no need to clone the platform repo or run sops manually.
- Navigate to Secrets in the sidebar
- Select your app using the context selector
- Click Create Secret
- Enter a name, select one or more target namespaces, and add key-value pairs
- Click Create
The operator automatically encrypts the data using SOPS + AGE, commits it to the platform Git repo, and Flux deploys the decrypted secret to the target namespace(s).
Dashboard → K8s Secret (intent) → Operator (encrypt + commit) → Flux (decrypt + apply)
- Secrets are write-only — the dashboard shows key names but never values
- Only admins can create, update, or delete secrets
Part 4: Day-2 Operations
Adding a new environment
Preferred (ADR-006): declare the env on the APR and the operator scaffolds the Flux Kustomization, ImageRepository/Policy, and namespace for you.
spec:
imageStreams:
- name: app
image: ghcr.io/labrats-work/apps.my-app
environments:
- name: staging # new
namespace: my-app-staging
hostname: app.my-app-staging.hcl.labrats.work
imagePolicy: { kind: strict, range: "=0.75.0" }
Then create the matching kustomize overlay (k8s/overlays/staging/) in your app repo. Image cadence per env is controlled by imagePolicy.kind: loose (auto-rolls), strict (PR-bumped range), or none (no image automation — overlay tag is the source of truth). See the ADR-006 design doc and the multi-env image automation cookbook.
Legacy spec.namespaces[] is still supported (mutually exclusive with spec.environments[]) for apps that hand-author their platform repo files.
Updating access control
Update the spec.access entries in the AppPlatformRegistration:
- Added groups get new RoleBindings
- Removed groups lose their RoleBindings
- Changes reconcile within 5 minutes
Troubleshooting
AppPlatformRegistration not Ready
kubectl get appplatformregistration my-app -o jsonpath='{.status.conditions}'
Common causes:
- RepoCreationFailed — Check GITHUB_TOKEN permissions
- DeployKeyFailed — Failed to generate or register deploy key
Can't see my app in the dashboard
Your Authelia groups don't match the spec.access entries. Verify group membership.
Pushed code but nothing deployed
- Check GitRepository source:
kubectl get gitrepository -n my-app-platform - Check Kustomization status:
kubectl get kustomization -n my-app-platform - Check Flux logs:
kubectl -n flux-system logs deploy/kustomize-controller
For a wider menu of symptoms, see the disaster-recovery runbook.
Next steps
- Admin Guide — day-to-day dashboard operations
(force reconcile, suspend, rotate secrets).
- Developer Guide — building the app side: CI,
image tagging, env vars.
dev/prod cadence + rollback procedure.
canonical file shape the operator scaffolds.