Fleet Observability quickstart
This walks through the default, out-of-the-box path. It deploys the bundled Prometheus
backend and edge collectors with Argo CD to one tenant cluster, using YAML and kubectl
throughout. Each step links to a reference page with the full parameter list and the
Platform UI walkthrough. See those pages for alternatives, such as bringing your own
backend or targeting a control plane cluster.
Prerequisites​
- The Argo CD integration is enabled on both the cluster where Platform is installed and the tenant cluster you collect from, each referencing an Argo CD connector. See Connect to Argo CD.
- A vCluster Platform license that includes Fleet Observability.
kubectlaccess to a project namespace. Platform creates a namespace namedp-<project-name>for each project, so the default project's namespace isp-default. Installations with a customized project namespace prefix, including installations upgraded from older versions, can use a different namespace.- A tenant cluster to collect metrics from.
1. Deploy the metrics backend​
Deploy the bundled Prometheus backend to the local cluster, the cluster where Platform itself is installed:
apiVersion: management.loft.sh/v1
kind: ArgoCDApplication
metadata:
name: fleet-metrics-backend
namespace: p-my-project # replace with your project namespace
spec:
displayName: "Fleet metrics backend"
destination:
cluster:
name: loft-cluster # the reserved name for Platform's local cluster; don't change this
templateRef:
name: fleet-observability-prometheus
parameters:
destinationNamespace: vcluster-platform
prometheusStorageSize: 20Gi
project: default
kubectl apply -f fleet-metrics-backend.yaml
Confirm the application landed in your project's namespace:
kubectl get argocdapplications -n p-default
Substitute your project's namespace if you're not using the default project.
For other backends, the Platform UI, and the full parameter list, see Deploy the metrics backend with Argo CD.
2. Create the connector and deploy the gateway​
Create the observability connector Secret. These endpoints match the backend's default namespace from step 1. The Secret below also configures the connector for the bundled Grafana deployed in step 6, using that template's default certificate Secret name:
apiVersion: v1
kind: Secret
metadata:
name: fleet-observability
namespace: vcluster-platform
labels:
loft.sh/connector-type: observability
platform.vcluster.com/fleet-observability-connector: "true"
type: Opaque
stringData:
metricsBackendOtlpEndpoint: https://prom-prometheus-server.vcluster-platform.svc.cluster.local/api/v1/otlp
metricsBackendPromQLEndpoint: https://prom-prometheus-server.vcluster-platform.svc.cluster.local
metricsBackendCertSecretName: metrics-backend-mtls
grafanaUrl: https://grafana.vcluster-platform.svc.cluster.local
grafanaCertSecretName: grafana-server-tls
fleetObservabilityDashboardUid: vcluster-fleet-observability
kubectl apply -f fleet-observability-connector.yaml
Platform deploys the gateway from this Secret. Check that the gateway pod comes up:
kubectl get pods -n vcluster-platform -l app.kubernetes.io/name=gateway,app.kubernetes.io/instance=fleet-observability
For connector keys, TLS options, and the Platform UI, see Install the observability gateway.
3. Create a metrics-writer key​
Create a metrics-writer access key scoped to your tenant cluster. Set PROJECT and
TENANT_CLUSTER to your tenant cluster's project and instance name. The command prints
the generated token as token: <value> at the end.
PROJECT=default
TENANT_CLUSTER=my-vcluster
KEY=$(head -c 20 /dev/urandom | od -An -tx1 | tr -d ' \n')
kubectl apply -f - <<EOF
apiVersion: storage.loft.sh/v1
kind: AccessKey
metadata:
name: "loft-metrics-writer-p-${PROJECT}-${TENANT_CLUSTER}"
spec:
type: Other
key: "${KEY}"
subject: "loft:metrics-writer:p-${PROJECT}:${TENANT_CLUSTER}"
groups:
- loft:system:metrics-writers
scope:
roles:
- role: metrics-writer
virtualClusters:
- project: ${PROJECT}
virtualCluster: "${TENANT_CLUSTER}"
EOF
echo "token: ${KEY}"
Keep the printed token for the next step.
For control plane cluster keys, reader keys, and rotation, see Configure metrics access.
4. Deploy edge collectors​
The token from step 3 goes into a Secret inside the tenant cluster, in the namespace the
collectors run in. Switch your kubectl context to the tenant cluster, then create the
namespace and the Secret:
kubectl create namespace observability
apiVersion: v1
kind: Secret
metadata:
name: otel-otlp-auth
namespace: observability
type: Opaque
stringData:
authorization: "Bearer REPLACE_WITH_METRICS_WRITER_KEY"
kubectl apply -f otel-otlp-auth.yaml
Switch your kubectl context back to the cluster where Platform runs before continuing.
The ArgoCDApplication you apply next, and its project namespace, are Platform custom
resources that only exist on that cluster, not on the tenant cluster.
Then deploy the collector to your tenant cluster. Edge collectors run inside the tenant
cluster, which resolves DNS in its own zone, so otlpEndpoint needs to point somewhere
the tenant cluster can actually reach. Which value to use depends on where the tenant
cluster runs:
- Same cluster as Platform
- A different cluster
If the tenant cluster runs on the same control plane cluster as Platform, map the
gateway Service into the tenant cluster's DNS instead of exposing it externally. This
requires shared nodes tenancy. Add this to the tenant cluster's vcluster.yaml:
controlPlane:
coredns:
enabled: true
embedded: true
networking:
resolveDNS:
- service: vcluster-platform/gateway-fleet-observability
target:
hostService: vcluster-platform/gateway-fleet-observability
See Resolve DNS for how
to apply vcluster.yaml changes and the full option reference. otlpEndpoint keeps using
the gateway's in-cluster Service name:
apiVersion: management.loft.sh/v1
kind: ArgoCDApplication
metadata:
name: fleet-edge-collectors-my-vcluster # include the tenant cluster name for uniqueness
namespace: p-my-project # replace with your project namespace
spec:
displayName: "Fleet edge collectors"
destination:
virtualCluster:
name: my-vcluster # replace with your tenant cluster name
target: vCluster # deploy into the tenant cluster
templateRef:
name: cluster-collector
parameters:
destinationNamespace: observability
otlpEndpoint: https://gateway-fleet-observability.vcluster-platform.svc.cluster.local:4318
otlpInsecureSkipVerify: "true"
project: default
If the tenant cluster runs on a different cluster than Platform, it can't resolve or
route to the gateway's in-cluster Service name. Expose the gateway with a LoadBalancer
Service instead. Add serviceType: LoadBalancer to the connector Secret from step 2 and
reapply it, then retrieve the assigned address:
kubectl get svc -n vcluster-platform gateway-fleet-observability \
-o jsonpath='{range .status.loadBalancer.ingress[*]}{.hostname}{.ip}{"\n"}{end}'
The cloud provider assigns the address asynchronously, so wait until it's populated, then
use it as otlpEndpoint:
apiVersion: management.loft.sh/v1
kind: ArgoCDApplication
metadata:
name: fleet-edge-collectors-my-vcluster # include the tenant cluster name for uniqueness
namespace: p-my-project # replace with your project namespace
spec:
displayName: "Fleet edge collectors"
destination:
virtualCluster:
name: my-vcluster # replace with your tenant cluster name
target: vCluster # deploy into the tenant cluster
templateRef:
name: cluster-collector
parameters:
destinationNamespace: observability
otlpEndpoint: https://REPLACE_WITH_LOADBALANCER_ADDRESS:4318
otlpInsecureSkipVerify: "true"
project: default
See Expose the gateway with a LoadBalancer for certificate and Service annotation details.
Both branches keep otlpInsecureSkipVerify: "true" to accept the gateway's self-signed
certificate for this quickstart. For a production setup, distribute the gateway's
ca.crt instead, as described in
Customize the gateway serving certificate.
kubectl apply -f fleet-edge-collectors.yaml
For control plane cluster targets, manual collector configuration, and the full parameter list, see Configure edge collectors.
5. Verify​
Check that metrics are reaching the gateway. Run the port-forward in the background and
capture its PID with $!. --retry-connrefused makes curl wait for the tunnel instead
of racing it:
GATEWAY_POD=$(kubectl get pod -n vcluster-platform -l app.kubernetes.io/name=gateway,app.kubernetes.io/instance=fleet-observability -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward -n vcluster-platform pod/$GATEWAY_POD 8889:8889 &
PF_PID=$!
curl --retry 5 --retry-connrefused --retry-delay 1 http://127.0.0.1:8889/metrics
kill $PF_PID
Gateway metrics only confirm a write request arrived, not that it reached the backend.
Create a metrics-reader key scoped to the same tenant cluster and query the Query Proxy
for your first metric. Match PROJECT and TENANT_CLUSTER to the values you used in step
3. See Create a reader key for the
full parameter list:
PROJECT=default
TENANT_CLUSTER=my-vcluster
METRICS_READER_TOKEN=$(head -c 20 /dev/urandom | od -An -tx1 | tr -d ' \n')
kubectl apply -f - <<EOF
apiVersion: storage.loft.sh/v1
kind: AccessKey
metadata:
name: "loft-metrics-reader-p-${PROJECT}-${TENANT_CLUSTER}"
spec:
type: Other
key: "${METRICS_READER_TOKEN}"
subject: "loft:metrics-reader:p-${PROJECT}:${TENANT_CLUSTER}"
groups:
- loft:system:metrics-readers
scope:
roles:
- role: metrics-reader
virtualClusters:
- project: ${PROJECT}
virtualCluster: "${TENANT_CLUSTER}"
EOF
GATEWAY_POD=$(kubectl get pod -n vcluster-platform -l app.kubernetes.io/name=gateway,app.kubernetes.io/instance=fleet-observability -o jsonpath='{.items[0].metadata.name}')
kubectl port-forward -n vcluster-platform pod/$GATEWAY_POD 8081:8081 &
PF_PID=$!
curl -k -G --retry 5 --retry-connrefused --retry-delay 1 \
-H "Authorization: Bearer $METRICS_READER_TOKEN" \
-H "X-Vcluster-Platform-Project: $PROJECT" \
-H "X-Vcluster-Platform-Instance: $TENANT_CLUSTER" \
--data-urlencode 'query=up' \
https://127.0.0.1:8081/api/v1/query
kill $PF_PID
A non-empty result array confirms the write reached the backend and is queryable.
If writes aren't showing up, see Troubleshooting.
6. Deploy the bundled Grafana (optional)​
Deploy Grafana for pre-provisioned dashboards:
apiVersion: management.loft.sh/v1
kind: ArgoCDApplication
metadata:
name: fleet-grafana
namespace: p-my-project # replace with your project namespace
spec:
displayName: "Fleet Grafana"
destination:
cluster:
name: loft-cluster # the reserved name for Platform's local cluster; don't change this
templateRef:
name: fleet-observability-grafana
parameters:
destinationNamespace: vcluster-platform
platformHost: platform.example.com
project: default
kubectl apply -f fleet-grafana.yaml
Once the Grafana pod is running, sign in at https://<platform-host>/grafana/ with
Platform SSO to see the pre-provisioned Fleet Observability, Cluster Metrics, and GPU
Overview dashboards. For the connector settings Grafana needs and the full dashboard
list, see
Deploy the bundled Grafana with Argo CD.
Next steps​
- Query fleet metrics for dashboards and PromQL access.
- GPU observability templates to add the GPU stack and NVSentinel.
- Troubleshooting if something isn't working.