Overview
CPLN Advisor watches the workloads in the org it runs in, tracks CPU, memory, replica counts, error rates and billed cost, and turns what it finds into concrete tuning suggestions — memory limits, autoscaling thresholds, replica counts — generated by an LLM grounded in Control Plane runbooks. Suggestions appear in a dashboard, and Autopilot can apply the qualifying ones for you, each with a one-click revert. It reports on the org it runs in: Control Plane injectsCPLN_ORG into every container, so the advisor knows where it lives without being told.
No credential passes through this chart. It creates no credential secret and takes none as a value — it reads two dictionary secrets you create first, and the advisor’s own Control Plane token is entered in the dashboard after install.
What Gets Created
- Dashboard Workload (
RELEASE_NAME-web) — The Next.js UI on port 3000, aserverlessworkload scaling 1–3. The only public workload, and the only one holding a session key. - API Workload (
RELEASE_NAME-api) — FastAPI backend on port 8000,standard, pinned to one replica. Verifies the login, runs the database migration at startup, and is reachable only by the dashboard. - Worker Workload (
RELEASE_NAME-worker) — Runs the scans, applies Autopilot changes, sends the Slack digest. One replica, no inbound traffic. - Scheduler Workload (
RELEASE_NAME-scheduler) — Fires the crons that create the scans. One replica, no inbound traffic. - Redis Workload (
RELEASE_NAME-redis) — Task broker and cache on port 6379. Not persistent and not authenticated; its firewall admits exactly the API, worker and scheduler. - Bundled Postgres (
RELEASE_NAME-postgresplus-pg-vs,-pg-identity,-pg-policy) — The database, from the catalog’s Postgres template pinned at3.4.1: astatefulworkload on its own volume set, with apg_isreadyprobe and scheduled backups behind a switch. It creates no secret — it reads your database credentials secret. - Startup Script Secret (
RELEASE_NAME-startup) — The API’s boot script. Holds no credential. - Identity & Policies (
RELEASE_NAME-identity,-policy,-gvc-policy) —revealon exactly two secrets (the advisor credentials you created and the boot script), plusviewon exactly one GVC, the one you installed into.
cpln workload exec, cpln logs and uninstalling all work against that GVC, and uninstalling can never delete it. The location you configure must already be a location of that GVC, and a GVC location this release did not ask for starts nothing.The Single Location Requirement
Install the advisor into a GVC with exactly one location. One location is not a simplification: scale bounds are per-location, so a second location would mean a second scheduler firing every cron twice, a second Redis splitting the task queue, and a second API running the same startup migration. Every workload this chart owns is pinned to the configuredlocation by defaultOptions.minScale/maxScale: 0 plus a localOptions entry carrying the real replica counts. Measured against a GVC that had a second location added underneath a running release, all five tiers started nothing there and their deployments read:
1 and logs a FATAL line naming both locations, while the correctly placed replica keeps serving untouched.
Workloads the Advisor Currently Skips
The advisor readsdefaultOptions.autoscaling.maxScale: 0 as “suspended” and skips the workload, ignoring the localOptions entry that actually supplies the replica count — even though the workload is running and serving traffic. Scans log:
maxScale: 0 to 1 made the very next scan score it. A fix belongs in the advisor backend, not in the chart — there is no chart-side workaround that does not reintroduce the placement defect this version closed.
Prerequisites
A GVC with exactly one location, and twodictionary secrets that exist before you install. The advisor’s own Control Plane token is set in the dashboard after install, not here.
The Advisor Credentials Secret
The keys are the application’s own environment variable names, so a secret built for a hand-applied deployment works here unchanged.auth.secretName to the name you used.
The Database Credentials Secret
From version3.4.0 the bundled Postgres template reads its credentials from a secret instead of taking them inline, which is what lets this chart bundle a database and still keep every credential out of values. Exactly three keys:
postgres.config.credentialsSecretName to the name you used.
Secret names are org-wide, so give each release its own pair of secret names.
A Service Account for the Advisor
The advisor’s Control Plane token is deliberately not a value and not in either secret. You set it once in the dashboard after signing in, under Configuration → Control Plane, where it is stored encrypted in the database. Create a service account whose token can, at minimum:targetLinks, and trying produces a silently empty fleet. An org-scoped policy carrying targetLinks is rejected with org/task policies only support target=all, and a workload: view binding scoped to //gvc/GVC_NAME/workload/* is stored happily but returns zero workloads. Grant target: all for these two kinds.Without org: readUsage the scan still succeeds — it logs billing: charges query failed … falling back to the allocation estimate and reports estimated rather than billed cost.Installation
Create both prerequisite secrets first, then install by whichever method you prefer:UI
CLI
Terraform
Pulumi
- The API can crash-restart once or twice on a first install. It has no wait-for-database, so it races Postgres (ready at 42 seconds in a measured install) and
alembic upgrade headexits1withConnectionError: unexpected connection_lost(). The third boot ran the migration and served.status.versions[].messagekeeps showingError: exitCode: 1afterwards on an otherwise healthy deployment. - The internal mesh took about four minutes to converge, during which the worker and scheduler could not reach Redis (
Cannot send task to the queue) and four scheduled scans were dropped with every workload still readingready: true. It recovered with no intervention.
If you installed the earlier 1.0.0
An earlier build of 1.0.0 rendered a GVC resource named after the GVC you installed into, so Helm adopted your GVC and has owned it since the first install. The current chart does not declare a GVC at all, and Helm deletes what a chart stops declaring. If you have not installed this template before, none of this section applies — install normally. Migrate to a new release instead:Back up the database
postgres.backup is off, turn it on and let one run complete, or take a manual pg_dumpall. See Backups and Restore.Install as a NEW release into an existing single-location GVC
DATABASE_URL at the new release’s Postgres host.Reuse the same ADVISOR_SECRET_KEY the old release used. A different one means the AI and Slack credentials in the restored rows cannot be decrypted, and they read as “not set”.Load the dump into the new database
Uninstall the old release
cpln helm uninstall, against the GVC you originally installed 1.0.0 into. That is where Helm tracks the release — and be aware it takes the adopted GVC with it.Configuration
The defaultvalues.yaml for this template:
Location
global.cpln.gvc is injected by the platform at install time and is never declared in values. There is no gvc key — the chart refuses to render if it finds one.
Nothing validates the other direction: a location the GVC does not have is accepted, stored, and simply runs nothing anywhere. That is why the API reads the GVC at boot and reports what it found. The read is bounded at roughly 22 seconds and warns rather than fails — a failure logs a warning and the dashboard keeps serving, verified by deleting the GVC-read policy and watching three HTTP 403 retries end in a warning while the API stayed ready with no restarts.
Images
The API, worker and scheduler run the same backend image with different commands — a scan is executed by the worker and served by the API, so they must never drift apart.Login and Sessions
The login name and password are keys in your credentials secret, not values. The login is verified by the API, never by the dashboard, so one workload holds the credentials. There is one account for everyone: change history and activity attribute everything to a single user, and there is no per-person audit trail.session.hours is an idle window that slides forward while you work, under a hard ceiling the app enforces. It is in force, not merely rendered: a session cookie issued at the default setting carried an Expires exactly 12 hours after login. session.rememberDays is the equivalent window with “Keep me signed in” selected.
Access
The dashboard is public and there is no values knob to close it. That is not a toggle because it is not really a choice — the dashboard is the only way into this app, and nothing inside the GVC calls it. What stands between the internet and your fleet is the login:- the session cookie is HMAC-signed with
ADVISOR_SESSION_SECRET, so it cannot be forged - the API fails closed — verified against the running release, every route except
/healthreturned401without a credential, a wrongAuthorization: Bearertoken returned401, and a wrong password returned401 Incorrect username or password. - both credentials come from a secret you create, so there is no shipped default password to look up
/health (200) and a worker-to-API call timed out; the API got +PONG from Redis while the dashboard’s Redis connection hung and returned nothing.
To narrow the dashboard to an office or VPN range, edit inboundAllowCIDR on the RELEASE_NAME-web workload after installing. Access changes take roughly 30 seconds to a few minutes to propagate.
Public URL
appUrl is empty by default and should stay that way unless you use a custom domain. The app derives its own public URL from Control Plane’s built-in environment variables as https://{web workload}-{gvc alias}.cpln.app, and the chart passes it this release’s actual dashboard workload name so the derivation resolves to the right workload. Both the Slack “View in Advisor” links and the CORS allowlist come from it. Never set it to *: combined with credentials, a wildcard makes the server echo back whichever origin asked.
Resources
A block that exposes both a floor and a limit names themminCpu/maxCpu and minMemory/maxMemory; a block that exposes only the limit uses the API’s own field names, bare cpu/memory. minCpu and minMemory are the floor Capacity AI scales up from, and only the three tiers that enable it set one. The worker and Redis run with Capacity AI off — a long scan should not be resized underneath itself, and a broker should not be resized under load — so a floor there would be inert.
The chart checks two limits at render time, because Control Plane enforces both but publishes neither in a schema, so each would otherwise surface as a 400 partway through an install:
maxCpu/minCpumust be strictly under 4:1, and memory is bounded the same way at 4:1 inclusive.- Unit typos.
cpuandmemoryare bare strings with no numeric bound, so512Giwritten for512Miis accepted and the workload then never schedules.
maxCpu or maxMemory on web, api, scheduler or postgres means raising its matching minimum too.
Database
Values underpostgres are passed straight through to the bundled Postgres chart, pinned at 3.4.1. Memory is deliberately above that chart’s defaults, because a fleet scan writes in bursts. Three workloads write this database concurrently — the API serves the dashboard, the worker records scan results and Autopilot changes, and the scheduler writes cron state.
Backups and Restore
Backups are off by default and you should turn them on. They are off only because they need a bucket and a cloud account you create first — and a volume is not a backup: losing it loses every scan, score and Autopilot record.provider: minio needs its own prerequisite dictionary secret holding accessKey and secretKey.
Each run writes one gzipped pg_dumpall plain-SQL file, postgres-<UTC-timestamp>.sql.gz, under <bucket>/<prefix>/. pg_dumpall produces a whole-cluster script including CREATE ROLE and CREATE DATABASE, so it restores into an empty server rather than merging into a running one.
The restore was executed end to end against this template with the aws provider: a 90 KB dump containing all 8 advisor tables and their rows, the database dropped outright, then restored with the application working on the restored data.
Stop the writers
Download the dump
aws nor gsutil.Open a tunnel to the database
Load it
Connecting
After the First Deploy
Sign in
ADVISOR_USERNAME and ADVISOR_PASSWORD from your credentials secret.Connect Control Plane
Add an AI provider, and Slack if you want digests
ADVISOR_SECRET_KEY — which is why losing that key loses them.Enroll workloads and scan
maxScale: 0 are currently skipped — see Workloads the Advisor Currently Skips.Confirm the scheduler is firing
GVC location check OK, and the scheduler’s for it sending run_scan:Diagnosing a Stuck Install
A missing prerequisite secret produces no log output at all. The container never starts, socpln logs returns zero lines and the deployment simply looks slow.
status.versions[].message — it names the missing secret.
Creating the missing secret repairs the deployment on its own, but slowly — roughly 5.5 to 10.5 minutes across the catalog, so poll rather than time-boxing it. A forced redeployment shortcuts it:
Important Notes
- Never
helm upgradea release of the earlier GVC-creating 1.0.0 onto this chart. It deletes the GVC that build adopted and everything in it. See If you installed the earlier 1.0.0. - Install into a single-location GVC. Every advisor workload is pinned to
location, but the bundled Postgres cannot be, and a second GVC location gives it a second independent database on the same service DNS name — billed, empty, and the one a laterlocationchange would repoint you at. - Create both prerequisite secrets before installing. A missing one wedges the deployment silently; Diagnosing a Stuck Install gives the one command that names it.
DATABASE_URLand the database credentials secret must agree, and nothing cross-checks them. A mismatch installs cleanly and then fails to authenticate at runtime.- Losing
ADVISOR_SECRET_KEYloses every credential entered in the UI. They are unrecoverable and must be re-entered. Keep it somewhere durable before you deploy. - The dashboard is public and there is no values knob to close it. The login is the boundary. Narrow
inboundAllowCIDRon theRELEASE_NAME-webworkload after installing if you want it tighter. - The bundled database admits any workload in your GVC. If the GVC is shared, narrow
postgres.internalAccess— see Database. - Backups are off by default and you should turn them on, and restore with a
psqlof version 18 or newer — see Backups and Restore. - A Postgres password is first-boot only. It is read when the data directory initializes; rotating it in the secret afterwards does not change the running server. You must also change it in Postgres itself and update
DATABASE_URLto match. - Rotating any secret requires a forced redeployment.
cpln://secret/…references resolve when a replica starts and are never re-resolved while it lives, so a rotated secret keeps working with the old value indefinitely while everything reports healthy. - Redis is not persistent and not authenticated. Everything in it is derived or transient, so a restart at worst skips one scan and repeats one digest. Its firewall admits exactly the three workloads that use it — do not widen it.
- Both images track
:latest, so an install is not reproducible. Pin:sha-<commit>on both before this matters to you. - Autopilot redeploys your workloads. Each applied suggestion patches a live workload and restarts it. It is per-workload and off until you enable it.
- The advisor’s token is as powerful as you make it. Grant
workload: editonly if you want Autopilot and one-click apply; without it the advisor runs read-only. - The first Helm upgrade after an install can re-apply resources even with identical values — a measured no-op upgrade reported four resources
Updatedwith no chart-authored field changed, and the second upgrade was completely clean. uninstalldeletes the database volume set and the scan history with it. It does not delete the GVC — this chart never owns one — and it does not delete your two prerequisite secrets.